From cf35b491a736f9b69ba96ac4e34f60338b713540 Mon Sep 17 00:00:00 2001 From: Robin Wimmers Date: Tue, 18 Jun 2024 09:36:00 +0200 Subject: [PATCH 1/3] refactor!: measured settlement series; allow update start measurement --- .../measured_settlement_series.py | 187 +++++++++--------- tests/measurements/conftest.py | 4 +- .../test_measured_settlement_series.py | 182 ++--------------- 3 files changed, 112 insertions(+), 261 deletions(-) diff --git a/src/baec/measurements/measured_settlement_series.py b/src/baec/measurements/measured_settlement_series.py index cf9702a..34111de 100644 --- a/src/baec/measurements/measured_settlement_series.py +++ b/src/baec/measurements/measured_settlement_series.py @@ -1,7 +1,7 @@ from __future__ import annotations import datetime -from functools import cache, wraps +from functools import wraps from typing import Any, Callable, Dict, List, Literal, Tuple import pandas as pd @@ -10,8 +10,8 @@ from matplotlib.figure import Figure from matplotlib.ticker import ScalarFormatter +from baec.coordinates import CoordinateReferenceSystems from baec.measurements.measured_settlement import MeasuredSettlement -from baec.measurements.settlement_rod_measurement import SettlementRodMeasurementStatus from baec.measurements.settlement_rod_measurement_series import ( SettlementRodMeasurementSeries, ) @@ -93,59 +93,12 @@ class MeasuredSettlementSeries: Represents a series of MeasuredSettlement objects, derived for a single settlement rod. """ - def __init__(self, items: List[MeasuredSettlement]) -> None: - """ - Initializes a MeasuredSettlementSeries object. - - Parameters - ---------- - items : List[MeasuredSettlement] - The list of MeasuredSettlement objects. - - Raises - ------ - TypeError - If the input types are incorrect. - ValueError - If the list of measurements is empty. - If the measurements are not for the same project, object, horizontal or - vertical units. - """ - - # Initialize all attributes using private setters. - self._set_items(items) - - # Set properties common to all measured settlements - self._project = self.items[0].project - self._object_id = self.items[0].object_id - self._start_date_time = self.items[0].start_date_time - self._horizontal_units = self.items[0].horizontal_units - self._vertical_units = self.items[0].vertical_units - - # Set properties that are lists - self._date_times = [] - self._days = [] - self._fill_thicknesses = [] - self._settlements = [] - self._x_displacements = [] - self._y_displacements = [] - self._statuses = [] - for item in self.items: - self._date_times.append(item.date_time) - self._days.append(item.days) - self._fill_thicknesses.append(item.fill_thickness) - self._settlements.append(item.settlement) - self._x_displacements.append(item.x_displacement) - self._y_displacements.append(item.y_displacement) - self._statuses.append(item.status) - - @classmethod - def from_settlement_rod_measurement_series( - cls, + def __init__( + self, series: SettlementRodMeasurementSeries, start_index: int | None = None, start_date_time: datetime.datetime | None = None, - ) -> MeasuredSettlementSeries: + ) -> None: """ Create a MeasuredSettlementSeries object from a SettlementRodMeasurementSeries object. @@ -184,12 +137,47 @@ def from_settlement_rod_measurement_series( IndexError If the `start_index` is out of range for the series. """ + # Check the types of the input parameters. if not isinstance(series, SettlementRodMeasurementSeries): raise TypeError( "Expected 'SettlementRodMeasurementSeries' type for 'series' parameter." ) + # set SettlementRodMeasurementSeries + self._series = series + + # set start of settlement + self._set_start_index_or_start_date_time(start_index, start_date_time) + + def _set_start_index_or_start_date_time( + self, + start_index: int | None = None, + start_date_time: datetime.datetime | None = None, + ) -> None: + """ + Private setter for measurements attribute. + + Parameters + ---------- + start_index: int | None, optional + The index of the item of the series to consider as the start or zero measurement of the series, or None. + Default is None. + start_date_time: int | None, optional + The date at which the start or zero measurement is taken place, or None. + Default is None. + + + Raises + ------ + TypeError + If the input types are incorrect. + ValueError + If both `start_index` and `start_date_time` are provided. + If the `start_date_time` is out of range for the series. + IndexError + If the `start_index` is out of range for the series. + """ if start_index is not None and not isinstance(start_index, int): raise TypeError("Expected 'int' type or None for 'start_index' parameter.") @@ -209,30 +197,30 @@ def from_settlement_rod_measurement_series( # Check that the start_index is within the range of the series. if start_index is not None: try: - series.measurements[start_index] + self.series.measurements[start_index] except IndexError: raise IndexError( f"start_index = {start_index} is out of range for the series. Length of series " - + f"is {len(series.measurements)}." + + f"is {len(self.series.measurements)}." ) # Check that the start_date_time is within the range of the series. if ( start_date_time is not None - and not series.measurements[0].date_time + and not self.series.measurements[0].date_time <= start_date_time - <= series.measurements[-1].date_time + <= self.series.measurements[-1].date_time ): raise ValueError( f"start_date_time = {start_date_time} is out of range for the series. " - + f"Valid range is {series.measurements[0].date_time} to " - + f"{series.measurements[-1].date_time}." + + f"Valid range is {self.series.measurements[0].date_time} to " + + f"{self.series.measurements[-1].date_time}." ) # Get start index from the start_date_time. if start_index is None: if start_date_time is not None: - for start_index, measurement in enumerate(series.measurements): + for start_index, measurement in enumerate(self.series.measurements): if measurement.date_time >= start_date_time: break # Else, both the start_index and start_date_time are None and thus @@ -240,17 +228,21 @@ def from_settlement_rod_measurement_series( else: start_index = 0 + # set start data info + self._start_index = start_index + self._start_date_time = self.series.measurements[start_index].date_time + # Create a list of MeasuredSettlement objects from the series of measurements. measured_settlements = [] - for measurement in series.measurements[start_index:]: + for measurement in self.series.measurements[self.start_index :]: measured_settlements.append( MeasuredSettlement.from_settlement_rod_measurement( measurement=measurement, - zero_measurement=series.measurements[start_index], + zero_measurement=self.series.measurements[self.start_index], ) ) - return cls(measured_settlements) + self._set_items(measured_settlements) def _set_items(self, value: List[MeasuredSettlement]) -> None: """Private setter for items attribute.""" @@ -323,6 +315,13 @@ def _set_items(self, value: List[MeasuredSettlement]) -> None: # Organize the list of MeasureSettlement objects in chronological order. self._items = sorted(value, key=lambda x: x.date_time) + @property + def series(self) -> SettlementRodMeasurementSeries: + """ + Represents a series of measurements for a single settlement rod. + """ + return self._series + @property def items(self) -> List[MeasuredSettlement]: """ @@ -336,14 +335,14 @@ def project(self) -> Project: """ The project the measured settlements belong to. """ - return self._project + return self.series.project @property def object_id(self) -> str: """ The ID of the object the measured settlements belong to. """ - return self._object_id + return self.series.object_id @property def start_date_time(self) -> datetime.datetime: @@ -352,27 +351,40 @@ def start_date_time(self) -> datetime.datetime: """ return self._start_date_time + @start_date_time.setter + def start_date_time(self, value: datetime.datetime) -> None: + """ + The date and time of the start of measurements (zero measurement). + """ + self._set_start_index_or_start_date_time(start_date_time=value) + @property - def horizontal_units(self) -> str: + def start_index(self) -> int: + """ + The date and time of the start of measurements (zero measurement). """ - The units of the horizontal XY displacements of the measured settlements. + return self._start_index + + @start_index.setter + def start_index(self, value: int) -> None: + """ + The date and time of the start of measurements (zero measurement). """ - return self._horizontal_units + self._set_start_index_or_start_date_time(start_index=value) @property - def vertical_units(self) -> str: + def coordinate_reference_systems(self) -> CoordinateReferenceSystems: """ - The units of the measurements and distances in the vertical direction - of the measured settlements. + The horizontal (X, Y) and vertical (Z) coordinate reference systems of the measurements. """ - return self._vertical_units + return self.series.coordinate_reference_systems @property def date_times(self) -> List[datetime.datetime]: """ The list of date and times for each measured settlement. """ - return self._date_times + return [item.date_time for item in self.items] @property def days(self) -> List[float]: @@ -380,7 +392,7 @@ def days(self) -> List[float]: The list of time elapsed in [days] since the start of measurements for each measured settlement. """ - return self._days + return [item.days for item in self.items] @property def fill_thicknesses(self) -> List[float]: @@ -388,7 +400,7 @@ def fill_thicknesses(self) -> List[float]: The list of fill thicknesses for each measured settlement. Units are according to `vertical_units`. """ - return self._fill_thicknesses + return [item.fill_thickness for item in self.items] @property def settlements(self) -> List[float]: @@ -397,7 +409,7 @@ def settlements(self) -> List[float]: A positive (+) settlement value represents a downward movement. Units are according to `vertical_units`. """ - return self._settlements + return [item.settlement for item in self.items] @property def x_displacements(self) -> List[float]: @@ -405,7 +417,7 @@ def x_displacements(self) -> List[float]: The list of horizontal X-displacements at the rod top relative to the zero measurement. Units are according to the `horizontal_units`. """ - return self._x_displacements + return [item.x_displacement for item in self.items] @property def y_displacements(self) -> List[float]: @@ -413,17 +425,8 @@ def y_displacements(self) -> List[float]: The list of horizontal Y-displacements at the rod top relative to the zero measurement. Units are according to the `horizontal_units`. """ - return self._y_displacements - - @property - def statuses(self) -> List[SettlementRodMeasurementStatus]: - """ - The list of status objects of the settlement rod measurement from which the measured settlement - is derived. - """ - return self._statuses + return [item.y_displacement for item in self.items] - @cache def to_dataframe(self) -> pd.DataFrame: """ Convert the MeasuredSettlementSeries to a pandas DataFrame. @@ -725,8 +728,8 @@ def plot_xy_displacements_plan_view(self, axes: Axes | None = None) -> Axes: axes.set_aspect("equal") axes.grid() - axes.set_xlabel(f"X [{self.horizontal_units}]") - axes.set_ylabel(f"Y [{self.horizontal_units}]") + axes.set_xlabel(f"X [{self.coordinate_reference_systems.horizontal_units}]") + axes.set_ylabel(f"Y [{self.coordinate_reference_systems.horizontal_units}]") axes.set_title( f"Plan view of horizonal measurements at rod top for object: {self.object_id}" ) @@ -782,10 +785,10 @@ def _plot_property_time( } units = { - "fill_thicknesses": self.vertical_units, - "settlements": self.vertical_units, - "x_displacements": self.horizontal_units, - "y_displacements": self.horizontal_units, + "fill_thicknesses": self.coordinate_reference_systems.vertical_units, + "settlements": self.coordinate_reference_systems.vertical_units, + "x_displacements": self.coordinate_reference_systems.horizontal_units, + "y_displacements": self.coordinate_reference_systems.horizontal_units, } # If axes is None create new Axes. diff --git a/tests/measurements/conftest.py b/tests/measurements/conftest.py index 2b54e42..ba198c9 100644 --- a/tests/measurements/conftest.py +++ b/tests/measurements/conftest.py @@ -137,6 +137,6 @@ def example_measured_settlements( @pytest.fixture def example_measured_settlement_series( - example_measured_settlements: List[MeasuredSettlement], + example_settlement_rod_measurement_series: SettlementRodMeasurementSeries, ) -> MeasuredSettlementSeries: - return MeasuredSettlementSeries(items=example_measured_settlements) + return MeasuredSettlementSeries(series=example_settlement_rod_measurement_series) diff --git a/tests/measurements/test_measured_settlement_series.py b/tests/measurements/test_measured_settlement_series.py index 267dabf..e1433b1 100644 --- a/tests/measurements/test_measured_settlement_series.py +++ b/tests/measurements/test_measured_settlement_series.py @@ -1,60 +1,12 @@ import datetime -from copy import deepcopy -from typing import List import pytest from matplotlib import pyplot as plt -from baec.measurements.measured_settlement import MeasuredSettlement from baec.measurements.measured_settlement_series import MeasuredSettlementSeries from baec.measurements.settlement_rod_measurement_series import ( SettlementRodMeasurementSeries, ) -from baec.project import Project - - -def test_measured_settlement_series_init_with_valid_input( - example_measured_settlements: List[MeasuredSettlement], -) -> None: - """Test initialization of MeasuredSettlementSeries with valid input.""" - - # Create series from measured_settlements in chronological order. - series = MeasuredSettlementSeries(items=example_measured_settlements) - - assert series.items == example_measured_settlements - - # Check that the measured_settlements are in chronological order. - assert sorted(series.items, key=lambda x: x.date_time) == series.items - - # Create series from measured_settlements in inverse chronological order. - series = MeasuredSettlementSeries(items=example_measured_settlements[::-1]) - - assert series.items == example_measured_settlements - - # Check that the measured_settlements are in chronological order. - assert sorted(series.items, key=lambda x: x.date_time) == series.items - - # Check that all properties are equal to the input - assert series.project == example_measured_settlements[0].project - assert series.object_id == example_measured_settlements[0].object_id - assert series.start_date_time == example_measured_settlements[0].date_time - assert series.horizontal_units == example_measured_settlements[0].horizontal_units - assert series.vertical_units == example_measured_settlements[0].vertical_units - - for i in range(len(example_measured_settlements)): - assert series.date_times[i] == example_measured_settlements[i].date_time - assert series.days[i] == example_measured_settlements[i].days - assert ( - series.fill_thicknesses[i] == example_measured_settlements[i].fill_thickness - ) - assert series.settlements[i] == example_measured_settlements[i].settlement - assert ( - series.x_displacements[i] == example_measured_settlements[i].x_displacement - ) - assert ( - series.y_displacements[i] == example_measured_settlements[i].y_displacement - ) - assert series.statuses[i] == example_measured_settlements[i].status def test_from_settlement_rod_measurement_series_with_valid_input( @@ -64,7 +16,7 @@ def test_from_settlement_rod_measurement_series_with_valid_input( measurement_series = example_settlement_rod_measurement_series # Valid input with default start_index and start_date_time. - series = MeasuredSettlementSeries.from_settlement_rod_measurement_series( + series = MeasuredSettlementSeries( series=measurement_series, ) @@ -72,11 +24,11 @@ def test_from_settlement_rod_measurement_series_with_valid_input( assert series.project == measurement_series.project assert series.object_id == measurement_series.object_id assert ( - series.horizontal_units + series.coordinate_reference_systems.horizontal_units == measurement_series.coordinate_reference_systems.horizontal_units ) assert ( - series.vertical_units + series.coordinate_reference_systems.vertical_units == measurement_series.coordinate_reference_systems.vertical_units ) @@ -107,14 +59,9 @@ def test_from_settlement_rod_measurement_series_with_valid_input( series.y_displacements == (df["rod_top_y"].iloc[idx:] - df["rod_top_y"].iloc[idx]).to_list() ) - assert series.statuses == [ - item.status for item in measurement_series.measurements[idx:] - ] # Valid input using start_index = 5. - series = MeasuredSettlementSeries.from_settlement_rod_measurement_series( - series=measurement_series, start_index=5 - ) + series.start_index = 5 df = measurement_series.to_dataframe() idx = 5 # expected start index @@ -143,14 +90,9 @@ def test_from_settlement_rod_measurement_series_with_valid_input( series.y_displacements == (df["rod_top_y"].iloc[idx:] - df["rod_top_y"].iloc[idx]).to_list() ) - assert series.statuses == [ - item.status for item in measurement_series.measurements[idx:] - ] # Valid input using start_index = -2. - series = MeasuredSettlementSeries.from_settlement_rod_measurement_series( - series=measurement_series, start_index=-2 - ) + series.start_index = -2 df = measurement_series.to_dataframe() idx = -2 # expected start index @@ -179,15 +121,9 @@ def test_from_settlement_rod_measurement_series_with_valid_input( series.y_displacements == (df["rod_top_y"].iloc[idx:] - df["rod_top_y"].iloc[idx]).to_list() ) - assert series.statuses == [ - item.status for item in measurement_series.measurements[idx:] - ] # Valid input using start_date_time = 2024-04-11 00:00:00. - series = MeasuredSettlementSeries.from_settlement_rod_measurement_series( - series=measurement_series, - start_date_time=datetime.datetime(2024, 4, 11, 0, 0, 0), - ) + series.start_date_time = datetime.datetime(2024, 4, 11, 0, 0, 0) df = measurement_series.to_dataframe() idx = 2 # expected start index @@ -216,15 +152,9 @@ def test_from_settlement_rod_measurement_series_with_valid_input( series.y_displacements == (df["rod_top_y"].iloc[idx:] - df["rod_top_y"].iloc[idx]).to_list() ) - assert series.statuses == [ - item.status for item in measurement_series.measurements[idx:] - ] # Valid input using start_date_time = 2024-04-11 04:00:00. - series = MeasuredSettlementSeries.from_settlement_rod_measurement_series( - series=measurement_series, - start_date_time=datetime.datetime(2024, 4, 11, 4, 0, 0), - ) + series.start_date_time = datetime.datetime(2024, 4, 11, 4, 0, 0) df = measurement_series.to_dataframe() idx = 3 # expected start index @@ -253,9 +183,6 @@ def test_from_settlement_rod_measurement_series_with_valid_input( series.y_displacements == (df["rod_top_y"].iloc[idx:] - df["rod_top_y"].iloc[idx]).to_list() ) - assert series.statuses == [ - item.status for item in measurement_series.measurements[idx:] - ] def test_from_settlement_rod_measurement_series_with_invalid_input( @@ -267,55 +194,55 @@ def test_from_settlement_rod_measurement_series_with_invalid_input( # Invalid series: None with pytest.raises(TypeError, match="series"): - MeasuredSettlementSeries.from_settlement_rod_measurement_series( + MeasuredSettlementSeries( series=None, ) # Invalid start_index: float with pytest.raises(TypeError, match="start_index"): - MeasuredSettlementSeries.from_settlement_rod_measurement_series( + MeasuredSettlementSeries( series=measurement_series, start_index=5.0, ) # Invalid start_index: out of range with positive value with pytest.raises(IndexError, match="start_index"): - MeasuredSettlementSeries.from_settlement_rod_measurement_series( + MeasuredSettlementSeries( series=measurement_series, start_index=20, ) # Invalid start_index: out of range with negative value with pytest.raises(IndexError, match="start_index"): - MeasuredSettlementSeries.from_settlement_rod_measurement_series( + MeasuredSettlementSeries( series=measurement_series, start_index=-20, ) # Invalid start_date_time: str with pytest.raises(TypeError, match="start_date_time"): - MeasuredSettlementSeries.from_settlement_rod_measurement_series( + MeasuredSettlementSeries( series=measurement_series, start_date_time="2024-04-11 00:00:00", ) # Invalid start_date_time: out of range with date before series with pytest.raises(ValueError, match="start_date_time"): - MeasuredSettlementSeries.from_settlement_rod_measurement_series( + MeasuredSettlementSeries( series=measurement_series, start_date_time=datetime.datetime(2024, 4, 1, 0, 0, 0), ) # Invalid start_date_time: out of range with date after series with pytest.raises(ValueError, match="start_date_time"): - MeasuredSettlementSeries.from_settlement_rod_measurement_series( + MeasuredSettlementSeries( series=measurement_series, start_date_time=datetime.datetime(2024, 4, 20, 0, 0, 0), ) # Both start_index and start_date_time can be provided. with pytest.raises(ValueError, match="'start_index' or 'start_date_time'"): - MeasuredSettlementSeries.from_settlement_rod_measurement_series( + MeasuredSettlementSeries( series=measurement_series, start_index=5, start_date_time=datetime.datetime(2024, 4, 11, 0, 0, 0), @@ -374,85 +301,6 @@ def test_date_time_to_days( series.date_time_to_days(date_time="2024-04-24 00:00:00") -def test_measured_settlement_series_init_with_invalid_measurements( - example_measured_settlements: List[MeasuredSettlement], -) -> None: - """Test initialization of MeasuredSettlementSeries with invalid measured_settlements.""" - - # Empty list - with pytest.raises(ValueError, match="items"): - MeasuredSettlementSeries(items=[]) - - # Incorrect type: One item is a string. - measured_settlements = deepcopy(example_measured_settlements) - measured_settlements[0] = "invalid" - with pytest.raises(TypeError): - MeasuredSettlementSeries(items=measured_settlements) - - # Different projects - measured_settlements = deepcopy(example_measured_settlements) - measured_settlements[0]._project = Project(id_="P-002", name="Project 2") - - with pytest.raises(ValueError, match="project"): - MeasuredSettlementSeries(items=measured_settlements) - - # Different measured objects - measured_settlements = deepcopy(example_measured_settlements) - measured_settlements[0]._object_id = "ZB-20" - - with pytest.raises(ValueError, match="object"): - MeasuredSettlementSeries(items=measured_settlements) - - # Different start_date_time - measured_settlements = deepcopy(example_measured_settlements) - measured_settlements[0]._start_date_time = datetime.datetime(1986, 10, 27, 2, 30, 0) - - with pytest.raises(ValueError, match="start date time"): - MeasuredSettlementSeries(items=measured_settlements) - - # Different horizontal units - measured_settlements = deepcopy(example_measured_settlements) - measured_settlements[0]._horizontal_units = "kilometre" - - with pytest.raises(ValueError, match="horizontal units"): - MeasuredSettlementSeries(items=measured_settlements) - - # Different vertical units - measured_settlements = deepcopy(example_measured_settlements) - measured_settlements[0]._vertical_units = "miles" - - with pytest.raises(ValueError, match="vertical units"): - MeasuredSettlementSeries(items=measured_settlements) - - -def test_measured_settlement_series_to_dataframe_method( - example_measured_settlements: List[MeasuredSettlement], -) -> None: - """Test the to_dataframe method of MeasuredSettlementSeries.""" - series = MeasuredSettlementSeries(items=example_measured_settlements) - - df = series.to_dataframe() - - # Check that the DataFrame has the correct number of rows. - assert len(df) == len(example_measured_settlements) - - # Check that the DataFrame has the correct data. - for i, item in enumerate(example_measured_settlements): - assert df.iloc[i]["project_id"] == item.project.id - assert df.iloc[i]["project_name"] == item.project.name - assert df.iloc[i]["object_id"] == item.object_id - assert df.iloc[i]["start_date_time"] == item.start_date_time - assert df.iloc[i]["date_time"] == item.date_time - assert df.iloc[i]["days"] == item.days - assert df.iloc[i]["fill_thickness"] == item.fill_thickness - assert df.iloc[i]["settlement"] == item.settlement - assert df.iloc[i]["x_displacement"] == item.x_displacement - assert df.iloc[i]["y_displacement"] == item.y_displacement - assert df.iloc[i]["horizontal_units"] == item.horizontal_units - assert df.iloc[i]["vertical_units"] == item.vertical_units - assert df.iloc[i]["status"] == item.status.value - - def test_plot_x_displacement_time( example_measured_settlement_series: MeasuredSettlementSeries, ) -> None: From b359cdb9e08d83d9f4153ed770c12124a809b01d Mon Sep 17 00:00:00 2001 From: Robin Wimmers Date: Tue, 18 Jun 2024 14:22:48 +0200 Subject: [PATCH 2/3] refactor!: rename property to attribute and spilt plot utils --- .../measured_settlement_series.py | 106 ++++-------------- src/baec/measurements/plot_utils.py | 72 ++++++++++++ 2 files changed, 95 insertions(+), 83 deletions(-) create mode 100644 src/baec/measurements/plot_utils.py diff --git a/src/baec/measurements/measured_settlement_series.py b/src/baec/measurements/measured_settlement_series.py index 34111de..843045c 100644 --- a/src/baec/measurements/measured_settlement_series.py +++ b/src/baec/measurements/measured_settlement_series.py @@ -11,6 +11,7 @@ from matplotlib.ticker import ScalarFormatter from baec.coordinates import CoordinateReferenceSystems +from baec.measurements import plot_utils from baec.measurements.measured_settlement import MeasuredSettlement from baec.measurements.settlement_rod_measurement_series import ( SettlementRodMeasurementSeries, @@ -479,7 +480,7 @@ def date_time_to_days(self, date_time: datetime.datetime) -> float: The days since the start of the measurements. Note that the days can be a decimal. """ # Check that date_time is datetime.datetime - if not isinstance(date_time, (datetime.datetime)): + if not isinstance(date_time, datetime.datetime): raise TypeError( f"Expected 'date_time.date_time' type for 'date_time' parameter, but got {type(date_time)}." ) @@ -499,7 +500,7 @@ def plot_x_displacement_time( zero measurement over time. """ return self._plot_property_time( - property="x_displacements", + attribute="x_displacements", axes=axes, log_time=log_time, min_log_time=min_log_time, @@ -521,7 +522,7 @@ def plot_y_displacement_time( zero measurement over time. """ return self._plot_property_time( - property="y_displacements", + attribute="y_displacements", axes=axes, log_time=log_time, min_log_time=min_log_time, @@ -542,7 +543,7 @@ def plot_settlement_time( Plot the settlement of the initial ground profile rod over time. """ return self._plot_property_time( - property="settlements", + attribute="settlements", axes=axes, log_time=log_time, min_log_time=min_log_time, @@ -563,7 +564,7 @@ def plot_fill_time( Plot the fill thickness over time. """ return self._plot_property_time( - property="fill_thicknesses", + attribute="fill_thicknesses", axes=axes, log_time=log_time, min_log_time=min_log_time, @@ -687,7 +688,7 @@ def plot_xy_displacements_plan_view(self, axes: Axes | None = None) -> Axes: ------- plt.Axes """ - self._validate_plot_parameter_axes(axes) + plot_utils.validate_plot_parameter_axes(axes) # If axes is None create new Axes. if axes is None: @@ -739,7 +740,7 @@ def plot_xy_displacements_plan_view(self, axes: Axes | None = None) -> Axes: @add_docstring_plot_time(return_type="axes") def _plot_property_time( self, - property: Literal[ + attribute: Literal[ "fill_thicknesses", "settlements", "x_displacements", @@ -755,19 +756,19 @@ def _plot_property_time( Private method to plot the requested property over time. """ # Assert the requested property is one accepted one. - assert property in [ + assert attribute in [ "fill_thicknesses", "settlements", "x_displacements", "y_displacements", - ], "Expected 'fill_thicknesses', 'settlements', 'x_displacements' or 'y_displacements' for 'property' parameter." + ], "Expected 'fill_thicknesses', 'settlements', 'x_displacements' or 'y_displacements' for 'attribute' parameter." # Validate input plot parameters - self._validate_plot_parameter_axes(axes) - self._validate_plot_parameter_log_time(log_time) - self._validate_plot_parameter_min_log_time(min_log_time) - self._validate_plot_parameter_add_date_time(add_date_time) - self._validate_plot_parameter_datetime_format(datetime_format) + plot_utils.validate_plot_parameter_axes(axes) + plot_utils.validate_plot_parameter_log_time(log_time) + plot_utils.validate_plot_parameter_min_log_time(min_log_time) + plot_utils.validate_plot_parameter_add_date_time(add_date_time) + plot_utils.validate_plot_parameter_datetime_format(datetime_format) # Map y_label, titles and units per property y_labels = { @@ -797,25 +798,25 @@ def _plot_property_time( axes = plt.gca() # Plot the property data over time - axes.plot(self.days, getattr(self, property)) + axes.plot(self.days, getattr(self, attribute)) if log_time: axes.set_xlim(min_log_time, max(self.days) + 1.0) axes.set_xscale("log") axes.set_ylim( - min(getattr(self, property)) - 0.5, max(getattr(self, property)) + 0.5 + min(getattr(self, attribute)) - 0.5, max(getattr(self, attribute)) + 0.5 ) - if property == "settlements": + if attribute == "settlements": axes.invert_yaxis() axes.xaxis.set_major_formatter(ScalarFormatter()) axes.xaxis.set_minor_formatter(ScalarFormatter()) - axes.grid(which="both") + axes.grid(visible=True, which="both") - axes.set_ylabel(f"{y_labels[property]} [{units[property]}]") + axes.set_ylabel(f"{y_labels[attribute]} [{units[attribute]}]") axes.set_xlabel("Time [days]") - axes.set_title(f"{titles[property]} for object: {self.object_id}") + axes.set_title(f"{titles[attribute]} for object: {self.object_id}") # Add secondary xaxis with the date_time if add_date_time: @@ -856,8 +857,8 @@ def _add_datetime_as_secondary_axis( datetime.datetime class. """ # Validate input plot parameters - self._validate_plot_parameter_axes(axes) - self._validate_plot_parameter_datetime_format(datetime_format) + plot_utils.validate_plot_parameter_axes(axes) + plot_utils.validate_plot_parameter_datetime_format(datetime_format) # Add secondary xaxis with the date_time axes2 = axes.twiny() @@ -881,64 +882,3 @@ def _add_datetime_as_secondary_axis( axes2.set_xlabel("Date and Time") return axes - - @staticmethod - def _validate_plot_parameter_axes(axes: Axes | None) -> None: - """ - Private method to validate the 'axes' parameter of the plot methods. - """ - if axes is not None and not isinstance(axes, Axes): - raise TypeError( - "Expected 'Axes' type or None for 'axes' parameter, but got {type(axes)}." - ) - - @staticmethod - def _validate_plot_parameter_log_time(log_time: bool) -> None: - """ - Private method to validate the 'log_time' parameter of the plot methods. - """ - if not isinstance(log_time, bool): - raise TypeError( - f"Expected 'bool' type for 'log_time' parameter, but got {type(log_time)}." - ) - - @staticmethod - def _validate_plot_parameter_min_log_time(min_log_time: float) -> None: - """ - Private method to validate the 'min_log_time' parameter of the plot methods. - """ - if not isinstance(min_log_time, (int, float)): - raise TypeError( - f"Expected 'float' type for 'min_log_time' parameter, but got {type(min_log_time)}." - ) - - if min_log_time <= 0.0: - raise ValueError("The 'min_log_time' parameter must be greater than 0.0.") - - @staticmethod - def _validate_plot_parameter_add_date_time(add_date_time: bool) -> None: - """ - Private method to validate the 'add_date_time' parameter of the plot methods. - """ - if not isinstance(add_date_time, bool): - raise TypeError( - f"Expected 'bool' type for 'add_date_time' parameter, but got {type(add_date_time)}." - ) - - @staticmethod - def _validate_plot_parameter_datetime_format(datetime_format: str) -> None: - """ - Private method to validate the 'datetime_format' parameter of the plot methods. - """ - if not isinstance(datetime_format, str): - raise TypeError( - f"Expected 'str' type for 'datetime_format' parameter, but got {type(datetime_format)}." - ) - - try: - datetime.datetime.now().strftime(datetime_format) - except ValueError: - raise ValueError( - "The 'datetime_format' parameter is not a valid format for the strftime method " - + "of the datetime.datetime class." - ) diff --git a/src/baec/measurements/plot_utils.py b/src/baec/measurements/plot_utils.py new file mode 100644 index 0000000..7769f5c --- /dev/null +++ b/src/baec/measurements/plot_utils.py @@ -0,0 +1,72 @@ +from __future__ import annotations + +import datetime +from functools import wraps +from typing import Any, Callable, Dict, List, Literal, Tuple + +import pandas as pd +from matplotlib import pyplot as plt +from matplotlib.axes import Axes +from matplotlib.figure import Figure +from matplotlib.ticker import ScalarFormatter + + +def validate_plot_parameter_axes(axes: Axes | None) -> None: + """ + Private method to validate the 'axes' parameter of the plot methods. + """ + if axes is not None and not isinstance(axes, Axes): + raise TypeError( + "Expected 'Axes' type or None for 'axes' parameter, but got {type(axes)}." + ) + + +def validate_plot_parameter_log_time(log_time: bool) -> None: + """ + Private method to validate the 'log_time' parameter of the plot methods. + """ + if not isinstance(log_time, bool): + raise TypeError( + f"Expected 'bool' type for 'log_time' parameter, but got {type(log_time)}." + ) + + +def validate_plot_parameter_min_log_time(min_log_time: float) -> None: + """ + Private method to validate the 'min_log_time' parameter of the plot methods. + """ + if not isinstance(min_log_time, (int, float)): + raise TypeError( + f"Expected 'float' type for 'min_log_time' parameter, but got {type(min_log_time)}." + ) + + if min_log_time <= 0.0: + raise ValueError("The 'min_log_time' parameter must be greater than 0.0.") + + +def validate_plot_parameter_add_date_time(add_date_time: bool) -> None: + """ + Private method to validate the 'add_date_time' parameter of the plot methods. + """ + if not isinstance(add_date_time, bool): + raise TypeError( + f"Expected 'bool' type for 'add_date_time' parameter, but got {type(add_date_time)}." + ) + + +def validate_plot_parameter_datetime_format(datetime_format: str) -> None: + """ + Private method to validate the 'datetime_format' parameter of the plot methods. + """ + if not isinstance(datetime_format, str): + raise TypeError( + f"Expected 'str' type for 'datetime_format' parameter, but got {type(datetime_format)}." + ) + + try: + datetime.datetime.now().strftime(datetime_format) + except ValueError: + raise ValueError( + "The 'datetime_format' parameter is not a valid format for the strftime method " + + "of the datetime.datetime class." + ) From 9c4fca170d0599a7ec1248b705d38b2e7b98d3e1 Mon Sep 17 00:00:00 2001 From: Robin Wimmers Date: Tue, 18 Jun 2024 15:13:02 +0200 Subject: [PATCH 3/3] style: resolve imported but unused --- src/baec/measurements/plot_utils.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/baec/measurements/plot_utils.py b/src/baec/measurements/plot_utils.py index 7769f5c..ca32e58 100644 --- a/src/baec/measurements/plot_utils.py +++ b/src/baec/measurements/plot_utils.py @@ -1,14 +1,8 @@ from __future__ import annotations import datetime -from functools import wraps -from typing import Any, Callable, Dict, List, Literal, Tuple -import pandas as pd -from matplotlib import pyplot as plt from matplotlib.axes import Axes -from matplotlib.figure import Figure -from matplotlib.ticker import ScalarFormatter def validate_plot_parameter_axes(axes: Axes | None) -> None: