Passed
Push — main ( 29adcf...6c3df6 )
by Douglas
01:40
created

WellRoi.__post_init__()   B

Complexity

Conditions 7

Size

Total Lines 13
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
eloc 13
nop 1
dl 0
loc 13
rs 8
c 0
b 0
f 0
1
import typing
0 ignored issues
show
introduced by
Missing module docstring
Loading history...
2
from dataclasses import dataclass
3
from enum import Enum
4
from typing import Dict
5
6
from pocketutils.core.exceptions import OutOfRangeError, ErrorUtils
7
8
9
class Edge(Enum):
0 ignored issues
show
introduced by
Missing class docstring
Loading history...
10
    left = 1
11
    right = 2
12
    top = 3
13
    bottom = 4
14
15
16
class Axis(Enum):
0 ignored issues
show
introduced by
Missing class docstring
Loading history...
17
    horizontal = 1
18
    vertical = 2
19
20
21
@ErrorUtils.args(edge=Edge, axis=Axis)
0 ignored issues
show
best-practice introduced by
Too many ancestors (8/7)
Loading history...
22
class RoiError(OutOfRangeError):
23
    """The ROI is invalid."""
24
25
26
@dataclass(frozen=True, order=True)
0 ignored issues
show
introduced by
Missing class docstring
Loading history...
27
class WellRoi:
28
    row: int
29
    column: int
30
    x0: int
0 ignored issues
show
Coding Style Naming introduced by
Attribute name "x0" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]2,|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
31
    y0: int
0 ignored issues
show
Coding Style Naming introduced by
Attribute name "y0" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]2,|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
32
    x1: int
0 ignored issues
show
Coding Style Naming introduced by
Attribute name "x1" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]2,|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
33
    y1: int
0 ignored issues
show
Coding Style Naming introduced by
Attribute name "y1" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]2,|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
34
35
    def __post_init__(self):
36
        if self.row < 0:
37
            raise OutOfRangeError(f"Row ({self.row}) < 0", value=self.row)
38
        if self.column < 0:
39
            raise OutOfRangeError(f"Column ({self.column}) < 0", value=self.column)
40
        if self.x0 < 0:
41
            raise RoiError(f"x0 ({self.x0}) < 0", edge=Edge.left)
42
        if self.y0 < 0:
43
            raise RoiError(f"y0 ({self.y0}) < 0", edge=Edge.top)
44
        if self.x0 >= self.x1:
45
            raise RoiError(f"x0 ({self.x0}) >= x1 ({self.x1})", axis=Axis.horizontal)
46
        if self.y0 >= self.y1:
47
            raise RoiError(f"y0 ({self.y0}) >= y1 ({self.y1})", axis=Axis.vertical)
48
49
    def __repr__(self) -> str:
50
        return f"{self.row},{self.column}=({self.x0},{self.y0})→({self.x1},{self.y1})"
51
52
    def __str__(self):
53
        return repr(self)
54
55
56
@dataclass(frozen=True, repr=True, order=True)
0 ignored issues
show
introduced by
Missing class docstring
Loading history...
57
class PlateRois:
58
    n_rows: int
59
    n_columns: int
60
    image_roi: WellRoi
61
    top_left_roi: WellRoi
62
    padx: float
63
    pady: float
64
65
    @property
66
    def well_rois(self):
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
67
        return self._get_roi_coordinates(self.top_left_roi, self.padx, self.pady)
68
69
    def __iter__(self):
70
        return iter(self.well_rois.keys())
71
72
    def __len__(self):
73
        return len(self.well_rois)
74
75
    def __getitem__(self, item):
76
        try:
77
            return self.well_rois[item[0], item[1]]
78
        except (IndexError, AttributeError):
79
            raise TypeError("Must look up well ROIs by (row, column) tuple indices.")
80
81
    def _get_roi_coordinates(
82
        self, top_left_roi: WellRoi, padx: float, pady: float
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
83
    ) -> Dict[typing.Tuple[int, int], WellRoi]:
84
        tl = top_left_roi
0 ignored issues
show
Coding Style Naming introduced by
Variable name "tl" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]2,|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
85
        width = top_left_roi.x1 - top_left_roi.x0
86
        height = top_left_roi.y1 - top_left_roi.y0
87
        wells_x_edge = tl.x0 + self.n_columns * width + (self.n_columns - 1) * padx
88
        wells_y_edge = tl.y0 + self.n_rows * height + (self.n_rows - 1) * pady
89
        # make sure the wells don't extend outside the image bounds
90
        if tl.x0 < self.image_roi.x0:
91
            raise RoiError(f"{tl.x0} < {self.image_roi.x0}", edge=Edge.left)
92
        if wells_x_edge > self.image_roi.x1:
93
            raise RoiError(f"{wells_x_edge} < {self.image_roi.x1}", edge=Edge.right)
94
        if tl.y0 < self.image_roi.y0:
95
            raise RoiError(f"{tl.y0} < {self.image_roi.y0}", edge=Edge.top)
96
        if wells_y_edge > self.image_roi.y1:
97
            raise RoiError(f"{wells_y_edge} > {self.image_roi.y1}", edge=Edge.bottom)
98
        # now build
99
        rois = {}
100
        x = tl.x0
0 ignored issues
show
Coding Style Naming introduced by
Variable name "x" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]2,|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
101
        y = tl.y0
0 ignored issues
show
Coding Style Naming introduced by
Variable name "y" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]2,|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
102
        for row in range(0, self.n_rows):
103
            for column in range(0, self.n_columns):
104
                rois[(row, column)] = WellRoi(row, column, x, y, x + width, y + height)
105
                x += width + padx
0 ignored issues
show
Coding Style Naming introduced by
Variable name "x" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]2,|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
106
            y += height + pady
0 ignored issues
show
Coding Style Naming introduced by
Variable name "y" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]2,|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
107
            x = tl.x0
0 ignored issues
show
Coding Style Naming introduced by
Variable name "x" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]2,|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
108
        return rois
109
110
111
__all__ = ["WellRoi", "PlateRois"]
112