From d16cc883d788dc02bf6ba9779dcad4611e29d1c9 Mon Sep 17 00:00:00 2001
From: Pablo Vasconez
Date: Wed, 3 Jul 2024 14:15:24 +0200
Subject: [PATCH] refactor!(settlement_measurement_rod): refator status and add
status messages to SettlementRodMeasurement
---
docs/index.rst | 2 +-
docs/tree/reference.rst | 17 +-
src/baec/measurements/io/zbase.py | 71 ++-
src/baec/measurements/measured_settlement.py | 32 +
.../measured_settlement_series.py | 18 +-
.../settlement_rod_measurement.py | 219 +++++--
.../settlement_rod_measurement_series.py | 4 +-
tests/measurements/conftest.py | 18 +-
.../measurements/test_measured_settlement.py | 21 +-
.../test_measured_settlement_series.py | 324 +++++-----
.../test_settlement_rod_measurement.py | 555 +++++-------------
.../test_settlement_rod_measurement_series.py | 2 +-
12 files changed, 674 insertions(+), 609 deletions(-)
diff --git a/docs/index.rst b/docs/index.rst
index b8bc5a2..f16492c 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -1,5 +1,5 @@
Welcome to BAEC Model Generator SDK documentation!
-=======================================
+==================================================
The `baec` library is created by `CEMS BV `_ .
diff --git a/docs/tree/reference.rst b/docs/tree/reference.rst
index 70f671c..5d74895 100644
--- a/docs/tree/reference.rst
+++ b/docs/tree/reference.rst
@@ -60,7 +60,22 @@ Measured Settlement Series
:member-order: bysource
.. automethod:: __init__
-
+
+.. _statusMessage:
+
+Status Message
+--------------
+
+.. autoclass:: baec.measurements.settlement_rod_measurement.StatusMessage
+ :members:
+ :inherited-members:
+ :member-order: bysource
+
+ .. automethod:: __init__
+
+.. autoenum:: baec.measurements.settlement_rod_measurement.StatusMessageLevel
+ :members:
+
.. _measurementDevice:
Measurement Device
diff --git a/src/baec/measurements/io/zbase.py b/src/baec/measurements/io/zbase.py
index 1ca66c5..9ced17d 100644
--- a/src/baec/measurements/io/zbase.py
+++ b/src/baec/measurements/io/zbase.py
@@ -11,7 +11,8 @@
from baec.measurements.measurement_device import MeasurementDevice
from baec.measurements.settlement_rod_measurement import (
SettlementRodMeasurement,
- SettlementRodMeasurementStatus,
+ StatusMessage,
+ StatusMessageLevel,
)
from baec.measurements.settlement_rod_measurement_series import (
SettlementRodMeasurementSeries,
@@ -19,20 +20,59 @@
from baec.project import Project
_STATUS_MAP = {
- 0: "ok",
- 1: "ok",
- 2: "disturbed",
- 3: "expired",
- 4: "relocated",
- 5: "rod_is_extended",
- 6: "crooked",
- 7: "deselected",
- 8: "deselected",
- 9: "fictional",
- 10: "unknown",
+ 0: StatusMessage(code=0, description="OK", level=StatusMessageLevel.OK),
+ 1: StatusMessage(code=1, description="OK", level=StatusMessageLevel.OK),
+ 2: StatusMessage(code=2, description="Disturbed", level=StatusMessageLevel.WARNING),
+ 3: StatusMessage(code=3, description="Expired", level=StatusMessageLevel.WARNING),
+ 4: StatusMessage(code=4, description="Relocated", level=StatusMessageLevel.WARNING),
+ 5: StatusMessage(
+ code=5, description="Rod is extended", level=StatusMessageLevel.INFO
+ ),
+ 6: StatusMessage(code=6, description="Crooked", level=StatusMessageLevel.WARNING),
+ 7: StatusMessage(
+ code=7, description="Deselected", level=StatusMessageLevel.WARNING
+ ),
+ 8: StatusMessage(
+ code=8, description="Deselected", level=StatusMessageLevel.WARNING
+ ),
+ 9: StatusMessage(code=9, description="Fictional", level=StatusMessageLevel.WARNING),
}
+def _zbase_status_to_message(status: int) -> StatusMessage:
+ """
+ Convert a ZBase status code to a StatusMessage object.
+ If the status code is not recognized, a default StatusMessage object is returned
+ with level set to StatusMessageLevel.ERROR.
+
+ Parameters
+ ----------
+ status : int
+ The ZBase status code.
+
+ Returns
+ -------
+ status_message : StatusMessage
+ The corresponding StatusMessage object.
+
+ Raises
+ ------
+ TypeError
+ If status is not of type int.
+ """
+ if not isinstance(status, int):
+ raise TypeError(f"status must be of type `int`, but got {type(status)}.")
+
+ status_message = _STATUS_MAP.get(status)
+ if status_message is None:
+ status_message = StatusMessage(
+ code=status,
+ description="Unrecognized status code",
+ level=StatusMessageLevel.ERROR,
+ )
+ return status_message
+
+
def measurements_from_zbase(
filepath_or_buffer: str | PathLike[str] | ReadCsvBuffer[bytes] | ReadCsvBuffer[str],
project_name: str,
@@ -98,6 +138,7 @@ def measurements_from_zbase(
# create SettlementRodMeasurement objects
measurements = []
for _, row in df.iterrows():
+ status = row.get("status")
measurements.append(
SettlementRodMeasurement(
project=Project(id_=id_, name=project_name),
@@ -113,9 +154,9 @@ def measurements_from_zbase(
rod_length=row.get("rod_top_z", 0) - row.get("rod_bottom_z", 0),
rod_bottom_z=row.get("rod_bottom_z", 0),
ground_surface_z=row.get("ground_surface_z", 0),
- status=SettlementRodMeasurementStatus(
- _STATUS_MAP.get(row.get("status", 10), "unknown")
- ),
+ status_messages=[]
+ if status is None
+ else [_zbase_status_to_message(status)],
)
)
diff --git a/src/baec/measurements/measured_settlement.py b/src/baec/measurements/measured_settlement.py
index a9733c4..388a284 100644
--- a/src/baec/measurements/measured_settlement.py
+++ b/src/baec/measurements/measured_settlement.py
@@ -2,10 +2,12 @@
import datetime
from functools import cache, cached_property
+from typing import List
from baec.measurements.settlement_rod_measurement import (
SettlementRodMeasurement,
SettlementRodMeasurementStatus,
+ StatusMessage,
)
from baec.project import Project
@@ -29,6 +31,7 @@ def __init__(
horizontal_units: str,
vertical_units: str,
status: SettlementRodMeasurementStatus,
+ status_messages: List[StatusMessage],
) -> None:
"""
Initializes a MeasuredSettlement object.
@@ -63,6 +66,9 @@ def __init__(
status: SettlementRodMeasurementStatus
The status of the settlement rod measurement from which the measured settlement
is derived.
+ status_messages: List[StatusMessage]
+ The list of status messages about the settlement rod measurement from which the
+ measured settlement is derived.
Raises
------
@@ -86,6 +92,7 @@ def __init__(
self._set_horizontal_units(horizontal_units)
self._set_vertical_units(vertical_units)
self._set_status(status)
+ self._set_status_messages(status_messages)
@classmethod
def from_settlement_rod_measurement(
@@ -156,6 +163,7 @@ def from_settlement_rod_measurement(
horizontal_units=measurement.coordinate_reference_systems.horizontal_units,
vertical_units=measurement.coordinate_reference_systems.vertical_units,
status=measurement.status,
+ status_messages=measurement.status_messages,
)
def _set_project(self, value: Project) -> None:
@@ -274,6 +282,21 @@ def _set_status(self, value: SettlementRodMeasurementStatus) -> None:
)
self._status = value
+ def _set_status_messages(self, value: List[StatusMessage]) -> None:
+ """
+ Private setter for status attribute.
+ """
+ if not isinstance(value, list):
+ raise TypeError(
+ "Expected 'List[StatusMessage]' type for 'status_messages' attribute."
+ )
+ # Check if the input is a list of StatusMessage objects.
+ if not all(isinstance(item, StatusMessage) for item in value):
+ raise TypeError(
+ "Expected 'List[StatusMessage]' type for 'status_messages' attribute."
+ )
+ self._status_messages = value
+
@property
def project(self) -> Project:
"""
@@ -364,6 +387,14 @@ def status(self) -> SettlementRodMeasurementStatus:
"""
return self._status
+ @property
+ def status_messages(self) -> List[StatusMessage]:
+ """
+ The list of status messages about the settlement rod measurement from which the
+ measured settlement is derived.
+ """
+ return self._status_messages
+
@cache
def to_dict(self) -> dict:
"""
@@ -383,4 +414,5 @@ def to_dict(self) -> dict:
"horizontal_units": self.horizontal_units,
"vertical_units": self.vertical_units,
"status": self.status.value,
+ "status_messages": "\n".join([m.to_string() for m in self.status_messages]),
}
diff --git a/src/baec/measurements/measured_settlement_series.py b/src/baec/measurements/measured_settlement_series.py
index 843045c..02fb355 100644
--- a/src/baec/measurements/measured_settlement_series.py
+++ b/src/baec/measurements/measured_settlement_series.py
@@ -139,18 +139,20 @@ def __init__(
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
+ self._set_series(series)
# set start of settlement
self._set_start_index_or_start_date_time(start_index, start_date_time)
+ def _set_series(self, value: SettlementRodMeasurementSeries) -> None:
+ """Private setter for series attribute."""
+ if not isinstance(value, SettlementRodMeasurementSeries):
+ raise TypeError(
+ "Expected 'SettlementRodMeasurementSeries' type for 'series' attribute."
+ )
+ self._series = value
+
def _set_start_index_or_start_date_time(
self,
start_index: int | None = None,
@@ -438,7 +440,7 @@ def to_dataframe(self) -> pd.DataFrame:
A pandas DataFrame with the measured settlements. The columns of the DataFrame are:
project_id, project_name, object_id, start_date_time
date_time, days, fill_thickness, settlement, x_displacement, y_displacement
- horizontal_units, vertical_units, status
+ horizontal_units, vertical_units, status, status_messages
"""
return pd.DataFrame.from_records(
[measurement.to_dict() for measurement in self.items]
diff --git a/src/baec/measurements/settlement_rod_measurement.py b/src/baec/measurements/settlement_rod_measurement.py
index 605a427..1dcda56 100644
--- a/src/baec/measurements/settlement_rod_measurement.py
+++ b/src/baec/measurements/settlement_rod_measurement.py
@@ -2,7 +2,8 @@
import datetime
from enum import Enum
-from functools import cache
+from functools import cache, cached_property, total_ordering
+from typing import List
from baec.coordinates import CoordinateReferenceSystems
from baec.measurements.measurement_device import MeasurementDevice
@@ -12,15 +13,142 @@
class SettlementRodMeasurementStatus(Enum):
"""Represents the status of a settlement rod measurement."""
- OK = "ok"
- DISTURBED = "disturbed"
- EXPIRED = "expired"
- RELOCATED = "relocated"
- ROD_IS_EXTENDED = "rod_is_extended"
- CROOKED = "crooked"
- DESELECTED = "deselected"
- FICTIONAL = "fictional"
- UNKNOWN = "unknown"
+ """If the highest severity level of the status messages is "OK".
+ This will indicate that the measurement is correct."""
+ OK = "OK"
+
+ """If the highest severity level of the status messages is "INFO".
+ This will indicate that there is still at least one informative comment about the measurement. The measurement is still correct."""
+ INFO = "INFO"
+
+ """If the highest severity level of the status messages is "WARNING".
+ This will indicate that there is still at least one warning about the measurement. The measurement may not be correct or accurate enough."""
+ WARNING = "WARNING"
+
+ """If the highest severity level of the status messages is "ERROR".
+ This will indicate that there is still at least one error about the measurement. The measurement is most probably incorrect."""
+ ERROR = "ERROR"
+
+
+@total_ordering
+class StatusMessageLevel(Enum):
+ """Represents the severity level of a status message."""
+
+ """Measurement is correct."""
+ OK = "OK"
+
+ """Measurement has an informative comment. The measurement is still correct."""
+ INFO = "INFO"
+
+ """Measurement has a warning. The measurement may not be correct or accurate enough."""
+ WARNING = "WARNING"
+
+ """Measurement has an error. The measurement is most probably incorrect."""
+ ERROR = "ERROR"
+
+ def __lt__(self, other: object) -> bool:
+ """
+ Compares the order of the status message level.
+ """
+ if isinstance(other, StatusMessageLevel):
+ return self.order() < other.order()
+ return False
+
+ def order(self) -> int:
+ """
+ Returns the order of the status message level.
+ """
+ order = {
+ StatusMessageLevel.OK: 0,
+ StatusMessageLevel.INFO: 1,
+ StatusMessageLevel.WARNING: 2,
+ StatusMessageLevel.ERROR: 3,
+ }
+ return order[self]
+
+
+class StatusMessage:
+ """
+ Represents a status message of a single settlement rod measurement.
+ """
+
+ def __init__(
+ self,
+ code: int,
+ description: str,
+ level: StatusMessageLevel,
+ ):
+ """
+ Initializes a StatusMessage object.
+
+ Parameters
+ ----------
+ code : int
+ The code of the status message.
+ description : str
+ The description of the status message.
+ level : StatusMessageLevel
+ The severity level of the status message.
+ """
+
+ # Initialize all attributes using private setters.
+ self._set_code(code)
+ self._set_description(description)
+ self._set_level(level)
+
+ def _set_code(self, value: int) -> None:
+ """
+ Private setter for code attribute.
+ """
+ if not isinstance(value, int):
+ raise TypeError("Expected 'int' type for 'code' attribute.")
+ self._code = value
+
+ def _set_description(self, value: str) -> None:
+ """
+ Private setter for description attribute.
+ """
+ if not isinstance(value, str):
+ raise TypeError("Expected 'str' type for 'description' attribute.")
+ if value == "":
+ raise ValueError("Empty string not allowed for 'description' attribute.")
+ self._description = value
+
+ def _set_level(self, value: StatusMessageLevel) -> None:
+ """
+ Private setter for level attribute.
+ """
+ if not isinstance(value, StatusMessageLevel):
+ raise TypeError("Expected 'StatusMessageLevel' type for 'level' attribute.")
+ self._level = value
+
+ @cache
+ def to_string(self) -> str:
+ """
+ Convert the status message to a string.
+ """
+ return f"(code={self.code}, description={self.description}, level={self.level.value})"
+
+ @property
+ def code(self) -> int:
+ """
+ The code of the status message.
+ """
+ return self._code
+
+ @property
+ def description(self) -> str:
+ """
+ The description of the status message.
+ """
+ return self._description
+
+ @property
+ def level(self) -> StatusMessageLevel:
+ """
+ The severity level of the status message.
+ """
+ return self._level
class SettlementRodMeasurement:
@@ -41,10 +169,9 @@ def __init__(
rod_length: float,
rod_bottom_z: float,
ground_surface_z: float,
- status: SettlementRodMeasurementStatus,
+ status_messages: List[StatusMessage],
temperature: float | None = None,
voltage: float | None = None,
- comment: str = "",
) -> None:
"""
Initializes a SettlementRodMeasurement object.
@@ -85,14 +212,12 @@ def __init__(
The Z-coordinate of the ground surface.
It is in principle the top of the fill, if present.
Units and datum according to the `coordinate_reference_systems`.
- status: SettlementRodMeasurementStatus
- The status of the measurement.
+ status_messages: List[StatusMessage]
+ The list of status messages about the measurement.
temperature : float or None, optional
The temperature at the time of measurement in [°C], or None if unknown (default: None).
voltage : float or None, optional
The voltage measured in [mV], or None if unknown (default: None).
- comment : str, optional
- Additional comment about the measurement (default: "").
Raises
------
@@ -115,10 +240,9 @@ def __init__(
self._set_rod_length(rod_length)
self._set_rod_bottom_z(rod_bottom_z)
self._set_ground_surface_z(ground_surface_z)
- self._set_status(status)
+ self._set_status_messages(status_messages)
self._set_temperature(temperature)
self._set_voltage(voltage)
- self._set_comment(comment)
def _set_project(self, value: Project) -> None:
"""
@@ -230,15 +354,20 @@ def _set_ground_surface_z(self, value: float) -> None:
raise TypeError("Expected 'float' type for 'ground_surface_z' attribute.")
self._ground_surface_z = value
- def _set_status(self, value: SettlementRodMeasurementStatus) -> None:
+ def _set_status_messages(self, value: List[StatusMessage]) -> None:
"""
Private setter for status attribute.
"""
- if not isinstance(value, SettlementRodMeasurementStatus):
+ if not isinstance(value, list):
+ raise TypeError(
+ "Expected 'List[StatusMessage]' type for 'status_messages' attribute."
+ )
+ # Check if the input is a list of StatusMessage objects.
+ if not all(isinstance(item, StatusMessage) for item in value):
raise TypeError(
- "Expected 'SettlementRodMeasurementStatus' type for 'status' attribute."
+ "Expected 'List[StatusMessage]' type for 'status_messages' attribute."
)
- self._status = value
+ self._status_messages = value
def _set_temperature(self, value: float | None) -> None:
"""
@@ -266,14 +395,6 @@ def _set_voltage(self, value: float | None) -> None:
)
self._voltage = value
- def _set_comment(self, value: str) -> None:
- """
- Private setter for comment attribute.
- """
- if not isinstance(value, str):
- raise TypeError("Expected 'str' type for 'comment' attribute.")
- self._comment = value
-
@property
def project(self) -> Project:
"""
@@ -371,11 +492,38 @@ def ground_surface_z(self) -> float:
return self._ground_surface_z
@property
+ def status_messages(self) -> List[StatusMessage]:
+ """
+ The list of status messages about the measurement.
+ """
+ return self._status_messages
+
+ @cached_property
def status(self) -> SettlementRodMeasurementStatus:
"""
The status of the measurement.
"""
- return self._status
+
+ # If no status messages are available, return OK.
+ if len(self.status_messages) == 0:
+ return SettlementRodMeasurementStatus.OK
+
+ # Get the highest severity level of the status messages.
+ highest_level = max([message.level for message in self.status_messages])
+
+ # Return the corresponding status.
+ if highest_level == StatusMessageLevel.OK:
+ return SettlementRodMeasurementStatus.OK
+ elif highest_level == StatusMessageLevel.INFO:
+ return SettlementRodMeasurementStatus.INFO
+ elif highest_level == StatusMessageLevel.WARNING:
+ return SettlementRodMeasurementStatus.WARNING
+ elif highest_level == StatusMessageLevel.ERROR:
+ return SettlementRodMeasurementStatus.ERROR
+ else:
+ raise ValueError(
+ f"No corresponding SettlementRodMeasurementStatus is available for {highest_level}."
+ )
@property
def temperature(self) -> float | None:
@@ -391,13 +539,6 @@ def voltage(self) -> float | None:
"""
return self._voltage
- @property
- def comment(self) -> str:
- """
- Additional comment about the measurement.
- """
- return self._comment
-
@cache
def to_dict(self) -> dict:
"""
@@ -423,7 +564,7 @@ def to_dict(self) -> dict:
"rod_bottom_z_uncorrected": self.rod_bottom_z_uncorrected,
"ground_surface_z": self.ground_surface_z,
"status": self.status.value,
+ "status_messages": "\n".join([m.to_string() for m in self.status_messages]),
"temperature": self.temperature,
"voltage": self.voltage,
- "comment": self.comment,
}
diff --git a/src/baec/measurements/settlement_rod_measurement_series.py b/src/baec/measurements/settlement_rod_measurement_series.py
index 4916256..2cc7275 100644
--- a/src/baec/measurements/settlement_rod_measurement_series.py
+++ b/src/baec/measurements/settlement_rod_measurement_series.py
@@ -149,8 +149,8 @@ def to_dataframe(self) -> pd.DataFrame:
project_id, project_name, device_id, device_qr_code, object_id,
coordinate_horizontal_epsg_code, coordinate_vertical_epsg_code,
date_time, rod_top_x, rod_top_y, rod_top_z, rod_length, rod_bottom_z
- rod_bottom_z_uncorrected, ground_surface_z, status, temperature,
- voltage, comment.
+ rod_bottom_z_uncorrected, ground_surface_z, status, status_messages, temperature,
+ voltage.
"""
return pd.DataFrame.from_records(
[measurement.to_dict() for measurement in self.measurements]
diff --git a/tests/measurements/conftest.py b/tests/measurements/conftest.py
index ba198c9..951e945 100644
--- a/tests/measurements/conftest.py
+++ b/tests/measurements/conftest.py
@@ -10,6 +10,8 @@
from baec.measurements.settlement_rod_measurement import (
SettlementRodMeasurement,
SettlementRodMeasurementStatus,
+ StatusMessage,
+ StatusMessageLevel,
)
from baec.measurements.settlement_rod_measurement_series import (
SettlementRodMeasurementSeries,
@@ -31,10 +33,11 @@ def valid_settlement_rod_measurement_input() -> dict:
rod_length=2.0,
rod_bottom_z=-1.193,
ground_surface_z=0.419,
- status=SettlementRodMeasurementStatus.OK,
+ status_messages=[
+ StatusMessage(code=0, description="OK", level=StatusMessageLevel.OK),
+ ],
temperature=12.0,
voltage=4232,
- comment="No comment",
)
@@ -58,10 +61,11 @@ def example_settlement_rod_measurements() -> List[SettlementRodMeasurement]:
rod_length = 2.0
rod_bottom_z_start = -1.193
ground_surface_z_start = 0.419
- status = SettlementRodMeasurementStatus.OK
+ status_messages = [
+ StatusMessage(code=0, description="OK", level=StatusMessageLevel.OK),
+ ]
temperature = 12.0
voltage = 4232
- comment = "No comment"
measurements = []
for i in range(10):
@@ -84,10 +88,9 @@ def example_settlement_rod_measurements() -> List[SettlementRodMeasurement]:
rod_length=rod_length,
rod_bottom_z=rod_bottom_z,
ground_surface_z=ground_surface_z,
- status=status,
+ status_messages=status_messages,
temperature=temperature,
voltage=voltage,
- comment=comment,
)
)
@@ -117,6 +120,9 @@ def valid_measured_settlement_input() -> dict:
x_displacement=0.25,
y_displacement=0.75,
status=SettlementRodMeasurementStatus.OK,
+ status_messages=[
+ StatusMessage(code=0, description="OK", level=StatusMessageLevel.OK),
+ ],
)
diff --git a/tests/measurements/test_measured_settlement.py b/tests/measurements/test_measured_settlement.py
index c893578..dc08d95 100644
--- a/tests/measurements/test_measured_settlement.py
+++ b/tests/measurements/test_measured_settlement.py
@@ -9,6 +9,8 @@
from baec.measurements.settlement_rod_measurement import (
SettlementRodMeasurement,
SettlementRodMeasurementStatus,
+ StatusMessage,
+ StatusMessageLevel,
)
from baec.project import Project
@@ -26,6 +28,9 @@ def test_measured_settlement_init_with_valid_input() -> None:
x_displacement = 0.25
y_displacement = 0.75
status = SettlementRodMeasurementStatus.OK
+ status_messages = [
+ StatusMessage(code=0, description="OK", level=StatusMessageLevel.OK),
+ ]
measured_settlement = MeasuredSettlement(
project=project,
@@ -39,6 +44,7 @@ def test_measured_settlement_init_with_valid_input() -> None:
x_displacement=x_displacement,
y_displacement=y_displacement,
status=status,
+ status_messages=status_messages,
)
assert measured_settlement.project == project
@@ -52,6 +58,7 @@ def test_measured_settlement_init_with_valid_input() -> None:
assert measured_settlement.x_displacement == x_displacement
assert measured_settlement.y_displacement == y_displacement
assert measured_settlement.status == status
+ assert measured_settlement.status_messages == status_messages
assert measured_settlement.days == pytest.approx(8.51, abs=0.001)
@@ -91,10 +98,10 @@ def test_measured_settlement_with_invalid_input(
MeasuredSettlement(**valid_measured_settlement_input)
-def test_from_settlement_rod_settlement_with_valid_input(
+def test_from_settlement_rod_settlement(
valid_settlement_rod_measurement: SettlementRodMeasurement,
) -> None:
- """Test constructor method from_measured_settlement_rod_measurement with valid input."""
+ """Test constructor method from_measured_settlement_rod_measurement."""
zero_measurement = deepcopy(valid_settlement_rod_measurement)
measurement = deepcopy(zero_measurement)
measurement._date_time = datetime.datetime(2024, 4, 10, 0, 0, 0)
@@ -126,7 +133,8 @@ def test_from_settlement_rod_settlement_with_valid_input(
measured_settlement.vertical_units
== measurement.coordinate_reference_systems.vertical_units
)
- assert measured_settlement.status == measured_settlement.status
+ assert measured_settlement.status == measurement.status
+ assert measured_settlement.status_messages == measurement.status_messages
# Invalid measurement: None
with pytest.raises(TypeError, match="measurement"):
@@ -189,6 +197,9 @@ def test_measured_settlement_to_dict() -> None:
x_displacement = 0.25
y_displacement = 0.75
status = SettlementRodMeasurementStatus.OK
+ status_messages = [
+ StatusMessage(code=0, description="OK", level=StatusMessageLevel.OK),
+ ]
measured_settlement = MeasuredSettlement(
project=project,
@@ -201,7 +212,8 @@ def test_measured_settlement_to_dict() -> None:
settlement=settlement,
x_displacement=x_displacement,
y_displacement=y_displacement,
- status=SettlementRodMeasurementStatus.OK,
+ status=status,
+ status_messages=status_messages,
)
assert measured_settlement.to_dict() == {
@@ -218,4 +230,5 @@ def test_measured_settlement_to_dict() -> None:
"horizontal_units": horizontal_units,
"vertical_units": vertical_units,
"status": status.value,
+ "status_messages": "(code=0, description=OK, level=OK)",
}
diff --git a/tests/measurements/test_measured_settlement_series.py b/tests/measurements/test_measured_settlement_series.py
index e1433b1..c14de55 100644
--- a/tests/measurements/test_measured_settlement_series.py
+++ b/tests/measurements/test_measured_settlement_series.py
@@ -1,4 +1,7 @@
+from __future__ import annotations
+
import datetime
+from typing import Type
import pytest
from matplotlib import pyplot as plt
@@ -9,16 +12,36 @@
)
-def test_from_settlement_rod_measurement_series_with_valid_input(
+@pytest.mark.parametrize(
+ "start_index,start_date_time,expected_start_index",
+ [
+ (None, None, 0), # default start index and start date time
+ (5, None, 5),
+ (-2, None, -2),
+ (None, datetime.datetime(2024, 4, 11, 0, 0, 0), 2),
+ (None, datetime.datetime(2024, 4, 11, 4, 0, 0), 3),
+ ],
+)
+def test_measured_settlement_series_init_with_valid_input(
example_settlement_rod_measurement_series: SettlementRodMeasurementSeries,
+ start_index: int,
+ start_date_time: datetime.datetime,
+ expected_start_index: int,
) -> None:
- """Test constructor method from_settlement_rod_measurement_series with valid input."""
+ """Test initialization method of MeasuredSettlementSeries with valid input."""
measurement_series = example_settlement_rod_measurement_series
- # Valid input with default start_index and start_date_time.
- series = MeasuredSettlementSeries(
- series=measurement_series,
- )
+ # Create series
+ if start_index is None and start_date_time is None:
+ series = MeasuredSettlementSeries(
+ series=measurement_series,
+ )
+ else:
+ series = MeasuredSettlementSeries(
+ series=measurement_series,
+ start_index=start_index,
+ start_date_time=start_date_time,
+ )
assert isinstance(series, MeasuredSettlementSeries)
assert series.project == measurement_series.project
@@ -33,38 +56,8 @@ def test_from_settlement_rod_measurement_series_with_valid_input(
)
df = measurement_series.to_dataframe()
- idx = 0 # expected start index
- assert len(series.items) == len(df.iloc[idx:])
- assert series.start_date_time == measurement_series.measurements[idx].date_time
- assert series.date_times == df["date_time"].to_list()[idx:]
- assert series.days == list(
- [
- (d - series.start_date_time).total_seconds() / 86400.0
- for d in df["date_time"].to_list()[idx:]
- ]
- )
- assert (
- series.fill_thicknesses
- == (df["ground_surface_z"] - df["rod_bottom_z"]).to_list()[idx:]
- )
- assert (
- series.settlements
- == (df["rod_bottom_z"].iloc[idx] - df["rod_bottom_z"].iloc[idx:]).to_list()
- )
- assert (
- series.x_displacements
- == (df["rod_top_x"].iloc[idx:] - df["rod_top_x"].iloc[idx]).to_list()
- )
- assert (
- series.y_displacements
- == (df["rod_top_y"].iloc[idx:] - df["rod_top_y"].iloc[idx]).to_list()
- )
- # Valid input using start_index = 5.
- series.start_index = 5
-
- df = measurement_series.to_dataframe()
- idx = 5 # expected start index
+ idx = expected_start_index # expected start index
assert len(series.items) == len(df.iloc[idx:])
assert series.start_date_time == measurement_series.measurements[idx].date_time
assert series.date_times == df["date_time"].to_list()[idx:]
@@ -91,104 +84,11 @@ def test_from_settlement_rod_measurement_series_with_valid_input(
== (df["rod_top_y"].iloc[idx:] - df["rod_top_y"].iloc[idx]).to_list()
)
- # Valid input using start_index = -2.
- series.start_index = -2
- df = measurement_series.to_dataframe()
- idx = -2 # expected start index
- assert len(series.items) == len(df.iloc[idx:])
- assert series.start_date_time == measurement_series.measurements[idx].date_time
- assert series.date_times == df["date_time"].to_list()[idx:]
- assert series.days == list(
- [
- (d - series.start_date_time).total_seconds() / 86400.0
- for d in df["date_time"].to_list()[idx:]
- ]
- )
- assert (
- series.fill_thicknesses
- == (df["ground_surface_z"] - df["rod_bottom_z"]).to_list()[idx:]
- )
- assert (
- series.settlements
- == (df["rod_bottom_z"].iloc[idx] - df["rod_bottom_z"].iloc[idx:]).to_list()
- )
- assert (
- series.x_displacements
- == (df["rod_top_x"].iloc[idx:] - df["rod_top_x"].iloc[idx]).to_list()
- )
- assert (
- series.y_displacements
- == (df["rod_top_y"].iloc[idx:] - df["rod_top_y"].iloc[idx]).to_list()
- )
-
- # Valid input using start_date_time = 2024-04-11 00:00:00.
- series.start_date_time = datetime.datetime(2024, 4, 11, 0, 0, 0)
-
- df = measurement_series.to_dataframe()
- idx = 2 # expected start index
- assert len(series.items) == len(df.iloc[idx:])
- assert series.start_date_time == datetime.datetime(2024, 4, 11, 0, 0, 0)
- assert series.date_times == df["date_time"].to_list()[idx:]
- assert series.days == list(
- [
- (d - series.start_date_time).total_seconds() / 86400.0
- for d in df["date_time"].to_list()[idx:]
- ]
- )
- assert (
- series.fill_thicknesses
- == (df["ground_surface_z"] - df["rod_bottom_z"]).to_list()[idx:]
- )
- assert (
- series.settlements
- == (df["rod_bottom_z"].iloc[idx] - df["rod_bottom_z"].iloc[idx:]).to_list()
- )
- assert (
- series.x_displacements
- == (df["rod_top_x"].iloc[idx:] - df["rod_top_x"].iloc[idx]).to_list()
- )
- assert (
- series.y_displacements
- == (df["rod_top_y"].iloc[idx:] - df["rod_top_y"].iloc[idx]).to_list()
- )
-
- # Valid input using start_date_time = 2024-04-11 04:00:00.
- series.start_date_time = datetime.datetime(2024, 4, 11, 4, 0, 0)
-
- df = measurement_series.to_dataframe()
- idx = 3 # expected start index
- assert len(series.items) == len(df.iloc[idx:])
- assert series.start_date_time == measurement_series.measurements[idx].date_time
- assert series.date_times == df["date_time"].to_list()[idx:]
- assert series.days == list(
- [
- (d - series.start_date_time).total_seconds() / 86400.0
- for d in df["date_time"].to_list()[idx:]
- ]
- )
- assert (
- series.fill_thicknesses
- == (df["ground_surface_z"] - df["rod_bottom_z"]).to_list()[idx:]
- )
- assert (
- series.settlements
- == (df["rod_bottom_z"].iloc[idx] - df["rod_bottom_z"].iloc[idx:]).to_list()
- )
- assert (
- series.x_displacements
- == (df["rod_top_x"].iloc[idx:] - df["rod_top_x"].iloc[idx]).to_list()
- )
- assert (
- series.y_displacements
- == (df["rod_top_y"].iloc[idx:] - df["rod_top_y"].iloc[idx]).to_list()
- )
-
-
-def test_from_settlement_rod_measurement_series_with_invalid_input(
+def test_measured_settlement_series_with_invalid_input(
example_settlement_rod_measurement_series: SettlementRodMeasurementSeries,
) -> None:
- """Test constructor method from_settlement_rod_measurement_series with invalid input."""
+ """Test initialization method of MeasuredSettlementSeries with invalid input."""
measurement_series = example_settlement_rod_measurement_series
@@ -249,6 +149,132 @@ def test_from_settlement_rod_measurement_series_with_invalid_input(
)
+@pytest.mark.parametrize(
+ "start_index,expected_start_index_or_error",
+ [
+ (0, 0),
+ (5, 5),
+ (-2, -2),
+ (5.0, TypeError),
+ (20, IndexError), # out of range with positive value
+ (-20, IndexError), # out of range with negative value
+ ],
+)
+def test_measured_settlement_series_start_index_setter(
+ example_settlement_rod_measurement_series: SettlementRodMeasurementSeries,
+ start_index: int,
+ expected_start_index_or_error: int | Type[Exception],
+) -> None:
+ """Test the start_index setter method of MeasuredSettlementSeries."""
+
+ # Create the series
+ measurement_series = example_settlement_rod_measurement_series
+
+ series = MeasuredSettlementSeries(
+ series=measurement_series,
+ )
+
+ # Set the start_index and check whether the expected error is
+ # raised or the expected output is obtained.
+ if isinstance(expected_start_index_or_error, Exception):
+ with pytest.raises(expected_start_index_or_error):
+ series.start_index = start_index
+
+ elif isinstance(expected_start_index_or_error, int):
+ series.start_index = start_index
+
+ # Check the output
+ df = measurement_series.to_dataframe()
+ idx = expected_start_index_or_error
+ assert len(series.items) == len(df.iloc[idx:])
+ assert series.start_date_time == measurement_series.measurements[idx].date_time
+ assert series.date_times == df["date_time"].to_list()[idx:]
+ assert series.days == list(
+ [
+ (d - series.start_date_time).total_seconds() / 86400.0
+ for d in df["date_time"].to_list()[idx:]
+ ]
+ )
+ assert (
+ series.fill_thicknesses
+ == (df["ground_surface_z"] - df["rod_bottom_z"]).to_list()[idx:]
+ )
+ assert (
+ series.settlements
+ == (df["rod_bottom_z"].iloc[idx] - df["rod_bottom_z"].iloc[idx:]).to_list()
+ )
+ assert (
+ series.x_displacements
+ == (df["rod_top_x"].iloc[idx:] - df["rod_top_x"].iloc[idx]).to_list()
+ )
+ assert (
+ series.y_displacements
+ == (df["rod_top_y"].iloc[idx:] - df["rod_top_y"].iloc[idx]).to_list()
+ )
+
+
+@pytest.mark.parametrize(
+ "start_date_time,expected_start_index_or_error",
+ [
+ (datetime.datetime(2024, 4, 11, 0, 0, 0), 2),
+ (datetime.datetime(2024, 4, 11, 4, 0, 0), 3),
+ ("2024-04-11 00:00:00", TypeError),
+ (datetime.datetime(2024, 4, 1, 0, 0, 0), ValueError), # date before series
+ ],
+)
+def test_measured_settlement_series_start_datetime_setter(
+ example_settlement_rod_measurement_series: SettlementRodMeasurementSeries,
+ start_date_time: datetime.datetime,
+ expected_start_index_or_error: int | Type[Exception],
+) -> None:
+ """Test the start_datetime setter method of MeasuredSettlementSeries."""
+
+ # Create the series
+ measurement_series = example_settlement_rod_measurement_series
+
+ series = MeasuredSettlementSeries(
+ series=measurement_series,
+ )
+
+ # Set the start_datetime and check whether the expected error is
+ # raised or the expected output is obtained.
+ if isinstance(expected_start_index_or_error, Exception):
+ with pytest.raises(expected_start_index_or_error):
+ series.start_date_time = start_date_time
+
+ elif isinstance(expected_start_index_or_error, int):
+ series.start_date_time = start_date_time
+
+ # Check the output
+ df = measurement_series.to_dataframe()
+ idx = expected_start_index_or_error
+ assert len(series.items) == len(df.iloc[idx:])
+ assert series.start_date_time == measurement_series.measurements[idx].date_time
+ assert series.date_times == df["date_time"].to_list()[idx:]
+ assert series.days == list(
+ [
+ (d - series.start_date_time).total_seconds() / 86400.0
+ for d in df["date_time"].to_list()[idx:]
+ ]
+ )
+ assert (
+ series.fill_thicknesses
+ == (df["ground_surface_z"] - df["rod_bottom_z"]).to_list()[idx:]
+ )
+ assert (
+ series.settlements
+ == (df["rod_bottom_z"].iloc[idx] - df["rod_bottom_z"].iloc[idx:]).to_list()
+ )
+ assert (
+ series.x_displacements
+ == (df["rod_top_x"].iloc[idx:] - df["rod_top_x"].iloc[idx]).to_list()
+ )
+ assert (
+ series.y_displacements
+ == (df["rod_top_y"].iloc[idx:] - df["rod_top_y"].iloc[idx]).to_list()
+ )
+
+
def test_days_to_date_time(
example_measured_settlement_series: MeasuredSettlementSeries,
) -> None:
@@ -301,6 +327,40 @@ def test_date_time_to_days(
series.date_time_to_days(date_time="2024-04-24 00:00:00")
+def test_measured_settlement_series_to_dataframe_method(
+ example_settlement_rod_measurement_series: SettlementRodMeasurementSeries,
+) -> None:
+ """Test the to_dataframe method of MeasuredSettlementSeries."""
+ measurement_series = example_settlement_rod_measurement_series
+
+ # Valid input with default start_index and start_date_time.
+ series = MeasuredSettlementSeries(
+ series=measurement_series,
+ )
+
+ df = series.to_dataframe()
+
+ # Check that the DataFrame has the correct number of rows.
+ assert len(df) == len(measurement_series.measurements)
+
+ # Check that the DataFrame has the correct data.
+ for i, item in enumerate(series.items):
+ 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
+ assert df.iloc[i]["status_messages"] == "(code=0, description=OK, level=OK)"
+
+
def test_plot_x_displacement_time(
example_measured_settlement_series: MeasuredSettlementSeries,
) -> None:
diff --git a/tests/measurements/test_settlement_rod_measurement.py b/tests/measurements/test_settlement_rod_measurement.py
index 9ce951c..3aae673 100644
--- a/tests/measurements/test_settlement_rod_measurement.py
+++ b/tests/measurements/test_settlement_rod_measurement.py
@@ -1,4 +1,5 @@
import datetime
+from typing import Any, Type
import pyproj
import pytest
@@ -8,10 +9,81 @@
from baec.measurements.settlement_rod_measurement import (
SettlementRodMeasurement,
SettlementRodMeasurementStatus,
+ StatusMessage,
+ StatusMessageLevel,
)
from baec.project import Project
+def test_status_message_level_comparison() -> None:
+ """Test comparison of status message levels."""
+ # OK
+ assert StatusMessageLevel.OK == StatusMessageLevel.OK
+ assert StatusMessageLevel.OK < StatusMessageLevel.INFO
+ assert StatusMessageLevel.OK < StatusMessageLevel.WARNING
+ assert StatusMessageLevel.OK < StatusMessageLevel.ERROR
+
+ # INFO
+ assert StatusMessageLevel.INFO > StatusMessageLevel.OK
+ assert StatusMessageLevel.INFO == StatusMessageLevel.INFO
+ assert StatusMessageLevel.INFO < StatusMessageLevel.WARNING
+ assert StatusMessageLevel.INFO < StatusMessageLevel.ERROR
+
+ # WARNING
+ assert StatusMessageLevel.WARNING > StatusMessageLevel.OK
+ assert StatusMessageLevel.WARNING > StatusMessageLevel.INFO
+ assert StatusMessageLevel.WARNING == StatusMessageLevel.WARNING
+ assert StatusMessageLevel.WARNING < StatusMessageLevel.ERROR
+
+ # ERROR
+ assert StatusMessageLevel.ERROR > StatusMessageLevel.OK
+ assert StatusMessageLevel.ERROR > StatusMessageLevel.INFO
+ assert StatusMessageLevel.ERROR > StatusMessageLevel.WARNING
+ assert StatusMessageLevel.ERROR == StatusMessageLevel.ERROR
+
+ # Different types
+ assert StatusMessageLevel.OK != 0
+
+
+def test_status_message_init_with_valid_input() -> None:
+ """Test initialization of status message with valid input."""
+ code = 0
+ description = "OK"
+ level = StatusMessageLevel.OK
+
+ status_message = StatusMessage(code=code, description=description, level=level)
+
+ assert status_message.code == code
+ assert status_message.description == description
+ assert status_message.level == level
+
+
+def test_status_message_init_with_invalid_input() -> None:
+ """Test initialization of status message with invalid input."""
+ with pytest.raises(TypeError):
+ StatusMessage(code="0", description="OK", level=StatusMessageLevel.OK)
+
+ with pytest.raises(TypeError):
+ StatusMessage(code=0, description=0, level=StatusMessageLevel.OK)
+
+ with pytest.raises(ValueError):
+ StatusMessage(code=0, description="", level=StatusMessageLevel.OK)
+
+ with pytest.raises(TypeError):
+ StatusMessage(code=0, description="OK", level=0)
+
+
+def test_status_message_to_string() -> None:
+ """Test string representation of status message."""
+ code = 0
+ description = "No comment"
+ level = StatusMessageLevel.OK
+
+ status_message = StatusMessage(code=code, description=description, level=level)
+
+ assert status_message.to_string() == "(code=0, description=No comment, level=OK)"
+
+
def test_settlement_rod_measurement_init_with_valid_input() -> None:
"""Test initialization of settlement rod measurement with valid input."""
project = Project(id_="P-001", name="Project 1")
@@ -25,10 +97,11 @@ def test_settlement_rod_measurement_init_with_valid_input() -> None:
rod_length = 2.0
rod_bottom_z = -1.193
ground_surface_z = 0.419
- status = SettlementRodMeasurementStatus.OK
+ status_messages = [
+ StatusMessage(code=0, description="OK", level=StatusMessageLevel.OK),
+ ]
temperature = 12.0
voltage = 4232
- comment = "No comment"
plate_bottom_z_uncorrected = -1.193
measurement = SettlementRodMeasurement(
@@ -43,10 +116,9 @@ def test_settlement_rod_measurement_init_with_valid_input() -> None:
rod_length=rod_length,
rod_bottom_z=rod_bottom_z,
ground_surface_z=ground_surface_z,
- status=status,
+ status_messages=status_messages,
temperature=temperature,
voltage=voltage,
- comment=comment,
)
assert measurement.project == project
@@ -60,402 +132,85 @@ def test_settlement_rod_measurement_init_with_valid_input() -> None:
assert measurement.rod_length == rod_length
assert measurement.ground_surface_z == ground_surface_z
assert measurement.rod_bottom_z == rod_bottom_z
- assert measurement.status == status
+ assert measurement.status_messages == status_messages
+ assert measurement.status == SettlementRodMeasurementStatus.OK
assert measurement.rod_bottom_z_uncorrected == plate_bottom_z_uncorrected
assert measurement.temperature == temperature
assert measurement.voltage == voltage
- assert measurement.comment == comment
-
-
-def test_settlement_rod_measurement_init_with_invalid_project() -> None:
- """Test initialization of settlement rod measurement with invalid project."""
- # Invalid project: None
- with pytest.raises(TypeError, match="project"):
- SettlementRodMeasurement(
- project=None,
- device=MeasurementDevice(id_="BR_003", qr_code="QR-003"),
- object_id="ZB-02",
- date_time=datetime.datetime(2024, 4, 9, 4, 0, 0),
- coordinate_reference_systems=CoordinateReferenceSystems.from_epsg(
- 28992, 5709
- ),
- rod_top_x=123340.266,
- rod_top_y=487597.154,
- rod_top_z=0.807,
- rod_length=2.0,
- rod_bottom_z=-1.193,
- ground_surface_z=0.419,
- status=SettlementRodMeasurementStatus.OK,
- temperature=12.0,
- voltage=4232,
- comment="No comment",
- )
-
-
-def test_settlement_rod_measurement_init_with_invalid_device() -> None:
- """Test initialization of settlement rod measurement with invalid device."""
- # Invalid device: None
- with pytest.raises(TypeError, match="device"):
- SettlementRodMeasurement(
- project=Project(id_="P-001", name="Project 1"),
- device=None,
- object_id="ZB-02",
- date_time=datetime.datetime(2024, 4, 9, 4, 0, 0),
- coordinate_reference_systems=CoordinateReferenceSystems.from_epsg(
- 28992, 5709
- ),
- rod_top_x=123340.266,
- rod_top_y=487597.154,
- rod_top_z=0.807,
- rod_length=2.0,
- rod_bottom_z=-1.193,
- ground_surface_z=0.419,
- status=SettlementRodMeasurementStatus.OK,
- temperature=12.0,
- voltage=4232,
- comment="No comment",
- )
-
-
-def test_settlement_rod_measurement_init_with_invalid_object_id() -> None:
- """Test initialization of settlement rod measurement with invalid object_id."""
- # Invalid point_id: None
- with pytest.raises(TypeError, match="object_id"):
- SettlementRodMeasurement(
- project=Project(id_="P-001", name="Project 1"),
- device=MeasurementDevice(id_="BR_003", qr_code="QR-003"),
- object_id=None,
- date_time=datetime.datetime(2024, 4, 9, 4, 0, 0),
- coordinate_reference_systems=CoordinateReferenceSystems.from_epsg(
- 28992, 5709
- ),
- rod_top_x=123340.266,
- rod_top_y=487597.154,
- rod_top_z=0.807,
- rod_length=2.0,
- rod_bottom_z=-1.193,
- ground_surface_z=0.419,
- status=SettlementRodMeasurementStatus.OK,
- temperature=12.0,
- voltage=4232,
- comment="No comment",
- )
-
- # Invalid rod_id: Empty string
- with pytest.raises(ValueError, match="object_id"):
- SettlementRodMeasurement(
- project=Project(id_="P-001", name="Project 1"),
- device=MeasurementDevice(id_="BR_003", qr_code="QR-003"),
- object_id="",
- date_time=datetime.datetime(2024, 4, 9, 4, 0, 0),
- coordinate_reference_systems=CoordinateReferenceSystems.from_epsg(
- 28992, 5709
- ),
- rod_top_x=123340.266,
- rod_top_y=487597.154,
- rod_top_z=0.807,
- rod_length=2.0,
- rod_bottom_z=-1.193,
- ground_surface_z=0.419,
- status=SettlementRodMeasurementStatus.OK,
- temperature=12.0,
- voltage=4232,
- comment="No comment",
- )
-
-
-def test_settlement_rod_measurement_init_with_invalid_date_time() -> None:
- """Test initialization of settlement rod measurement with invalid date_time."""
- # Invalid date_time: None
- with pytest.raises(TypeError, match="date_time"):
- SettlementRodMeasurement(
- project=Project(id_="P-001", name="Project 1"),
- device=MeasurementDevice(id_="BR_003", qr_code="QR-003"),
- object_id="ZB-02",
- date_time=None,
- coordinate_reference_systems=CoordinateReferenceSystems.from_epsg(
- 28992, 5709
- ),
- rod_top_x=123340.266,
- rod_top_y=487597.154,
- rod_top_z=0.807,
- rod_length=2.0,
- rod_bottom_z=-1.193,
- ground_surface_z=0.419,
- status=SettlementRodMeasurementStatus.OK,
- temperature=12.0,
- voltage=4232,
- comment="No comment",
- )
-
-
-def test_settlement_rod_measurement_init_with_invalid_coordinate_reference_systems() -> (
- None
-):
- """Test initialization of settlement rod measurement with invalid coordinate reference systems."""
- # Invalid coordinate_reference_system: None
- with pytest.raises(TypeError, match="coordinate_reference_systems"):
- SettlementRodMeasurement(
- project=Project(id_="P-001", name="Project 1"),
- device=MeasurementDevice(id_="BR_003", qr_code="QR-003"),
- object_id="ZB-02",
- date_time=datetime.datetime(2024, 4, 9, 4, 0, 0),
- coordinate_reference_systems=None,
- rod_top_x=123340.266,
- rod_top_y=487597.154,
- rod_top_z=0.807,
- rod_length=2.0,
- rod_bottom_z=-1.193,
- ground_surface_z=0.419,
- status=SettlementRodMeasurementStatus.OK,
- temperature=12.0,
- voltage=4232,
- comment="No comment",
- )
-
-
-def test_settlement_rod_measurement_init_with_invalid_rod_top_x() -> None:
- """Test initialization of settlement rod measurement with invalid rod_top_x."""
- # Invalid rod_top_x: String value
- with pytest.raises(TypeError, match="rod_top_x"):
- SettlementRodMeasurement(
- project=Project(id_="P-001", name="Project 1"),
- device=MeasurementDevice(id_="BR_003", qr_code="QR-003"),
- object_id="ZB-02",
- date_time=datetime.datetime(2024, 4, 9, 4, 0, 0),
- coordinate_reference_systems=CoordinateReferenceSystems.from_epsg(
- 28992, 5709
- ),
- rod_top_x="123340.266",
- rod_top_y=487597.154,
- rod_top_z=0.807,
- rod_length=2.0,
- rod_bottom_z=-1.193,
- ground_surface_z=0.419,
- status=SettlementRodMeasurementStatus.OK,
- temperature=12.0,
- voltage=4232,
- comment="No comment",
- )
-
-
-def test_settlement_rod_measurement_init_with_invalid_rod_top_y() -> None:
- """Test initialization of settlement rod measurement with invalid rod_top_y."""
- # Invalid rod_top_y: None
- with pytest.raises(TypeError, match="rod_top_y"):
- SettlementRodMeasurement(
- project=Project(id_="P-001", name="Project 1"),
- device=MeasurementDevice(id_="BR_003", qr_code="QR-003"),
- object_id="ZB-02",
- date_time=datetime.datetime(2024, 4, 9, 4, 0, 0),
- coordinate_reference_systems=CoordinateReferenceSystems.from_epsg(
- 28992, 5709
- ),
- rod_top_x=123340.266,
- rod_top_y=None,
- rod_top_z=0.807,
- rod_length=2.0,
- rod_bottom_z=-1.193,
- ground_surface_z=0.419,
- status=SettlementRodMeasurementStatus.OK,
- temperature=12.0,
- voltage=4232,
- comment="No comment",
- )
-
-
-def test_settlement_rod_measurement_init_with_rod_top_z() -> None:
- """Test initialization of settlement rod measurement with invalid rod_top_z."""
- # Invalid rod_top_z: String value
- with pytest.raises(TypeError, match="rod_top_z"):
- SettlementRodMeasurement(
- project=Project(id_="P-001", name="Project 1"),
- device=MeasurementDevice(id_="BR_003", qr_code="QR-003"),
- object_id="ZB-02",
- date_time=datetime.datetime(2024, 4, 9, 4, 0, 0),
- coordinate_reference_systems=CoordinateReferenceSystems.from_epsg(
- 28992, 5709
- ),
- rod_top_x=123340.266,
- rod_top_y=487597.154,
- rod_top_z="0.807",
- rod_length=2.0,
- rod_bottom_z=-1.193,
- ground_surface_z=0.419,
- status=SettlementRodMeasurementStatus.OK,
- temperature=12.0,
- voltage=4232,
- comment="No comment",
- )
-
-
-def test_settlement_rod_measurement_init_with_invalid_rod_length() -> None:
- """Test initialization of settlement rod measurement with invalid rod_length."""
- # Invalid point_z: String value
- with pytest.raises(TypeError, match="rod_length"):
- SettlementRodMeasurement(
- project=Project(id_="P-001", name="Project 1"),
- device=MeasurementDevice(id_="BR_003", qr_code="QR-003"),
- object_id="ZB-02",
- date_time=datetime.datetime(2024, 4, 9, 4, 0, 0),
- coordinate_reference_systems=CoordinateReferenceSystems.from_epsg(
- 28992, 5709
- ),
- rod_top_x=123340.266,
- rod_top_y=487597.154,
- rod_top_z=0.807,
- rod_length="2.0",
- rod_bottom_z=-1.193,
- ground_surface_z=0.419,
- status=SettlementRodMeasurementStatus.OK,
- temperature=12.0,
- voltage=4232,
- comment="No comment",
- )
-
- # Invalid rod_length: Negative value
- with pytest.raises(ValueError, match="rod_length"):
- SettlementRodMeasurement(
- project=Project(id_="P-001", name="Project 1"),
- device=MeasurementDevice(id_="BR_003", qr_code="QR-003"),
- object_id="ZB-02",
- date_time=datetime.datetime(2024, 4, 9, 4, 0, 0),
- coordinate_reference_systems=CoordinateReferenceSystems.from_epsg(
- 28992, 5709
- ),
- rod_top_x=123340.266,
- rod_top_y=487597.154,
- rod_top_z=0.807,
- rod_length=-2.0,
- rod_bottom_z=-1.193,
- ground_surface_z=0.419,
- status=SettlementRodMeasurementStatus.OK,
- temperature=12.0,
- voltage=4232,
- comment="No comment",
- )
-
-
-def test_settlement_rod_measurement_init_with_invalid_ground_surface_z() -> None:
- """Test initialization of settlement rod measurement with invalid ground_surface_z."""
- # Invalid ground_surface_z: String value
- with pytest.raises(TypeError, match="ground_surface_z"):
- SettlementRodMeasurement(
- project=Project(id_="P-001", name="Project 1"),
- device=MeasurementDevice(id_="BR_003", qr_code="QR-003"),
- object_id="ZB-02",
- date_time=datetime.datetime(2024, 4, 9, 4, 0, 0),
- coordinate_reference_systems=CoordinateReferenceSystems.from_epsg(
- 28992, 5709
- ),
- rod_top_x=123340.266,
- rod_top_y=487597.154,
- rod_top_z=0.807,
- rod_length=2.0,
- rod_bottom_z=-1.193,
- ground_surface_z="0.419",
- status=SettlementRodMeasurementStatus.OK,
- temperature=12.0,
- voltage=4232,
- comment="No comment",
- )
-
-
-def test_settlement_rod_measurement_init_with_invalid_rod_bottom_z() -> None:
- """Test initialization of settlement rod measurement with invalid rod_bottom_z."""
- # Invalid rod_bottom_z: String value
- with pytest.raises(TypeError, match="rod_bottom_z"):
- SettlementRodMeasurement(
- project=Project(id_="P-001", name="Project 1"),
- device=MeasurementDevice(id_="BR_003", qr_code="QR-003"),
- object_id="ZB-02",
- date_time=datetime.datetime(2024, 4, 9, 4, 0, 0),
- coordinate_reference_systems=CoordinateReferenceSystems.from_epsg(
- 28992, 5709
- ),
- rod_top_x=123340.266,
- rod_top_y=487597.154,
- rod_top_z=0.807,
- rod_length=2.0,
- rod_bottom_z="-1.193",
- ground_surface_z=0.419,
- status=SettlementRodMeasurementStatus.OK,
- temperature=12.0,
- voltage=4232,
- comment="No comment",
- )
-
-
-def test_settlement_rod_measurement_init_with_invalid_temperature() -> None:
- """Test initialization of settlement rod measurement with invalid temperature."""
- # Invalid temperature: String value
- with pytest.raises(TypeError, match="temperature"):
- SettlementRodMeasurement(
- project=Project(id_="P-001", name="Project 1"),
- device=MeasurementDevice(id_="BR_003", qr_code="QR-003"),
- object_id="ZB-02",
- date_time=datetime.datetime(2024, 4, 9, 4, 0, 0),
- coordinate_reference_systems=CoordinateReferenceSystems.from_epsg(
- 28992, 5709
- ),
- rod_top_x=123340.266,
- rod_top_y=487597.154,
- rod_top_z=0.807,
- rod_length=2.0,
- rod_bottom_z=-1.193,
- ground_surface_z=0.419,
- status=SettlementRodMeasurementStatus.OK,
- temperature="12.0",
- voltage=4232,
- comment="No comment",
- )
-
-
-def test_settlement_rod_measurement_init_with_invalid_voltage() -> None:
- """Test initialization of settlement rod measurement with invalid voltage."""
- # Invalid voltage: String value
- with pytest.raises(TypeError, match="voltage"):
- SettlementRodMeasurement(
- project=Project(id_="P-001", name="Project 1"),
- device=MeasurementDevice(id_="BR_003", qr_code="QR-003"),
- object_id="ZB-02",
- date_time=datetime.datetime(2024, 4, 9, 4, 0, 0),
- coordinate_reference_systems=CoordinateReferenceSystems.from_epsg(
- 28992, 5709
- ),
- rod_top_x=123340.266,
- rod_top_y=487597.154,
- rod_top_z=0.807,
- rod_length=2.0,
- rod_bottom_z=-1.193,
- ground_surface_z=0.419,
- status=SettlementRodMeasurementStatus.OK,
- temperature=12.0,
- voltage="4232",
- comment="No comment",
- )
-
-
-def test_settlement_rod_measurement_init_with_invalid_comment() -> None:
- """Test initialization of settlement rod measurement with invalid comment."""
- # Invalid comment: Integer value
- with pytest.raises(TypeError):
- SettlementRodMeasurement(
- project=Project(id_="P-001", name="Project 1"),
- device=MeasurementDevice(id_="BR_003", qr_code="QR-003"),
- object_id="ZB-02",
- date_time=datetime.datetime(2024, 4, 9, 4, 0, 0),
- coordinate_reference_systems=CoordinateReferenceSystems.from_epsg(
- 28992, 5709
- ),
- rod_top_x=123340.266,
- rod_top_y=487597.154,
- rod_top_z=0.807,
- rod_length=2.0,
- rod_bottom_z=-1.193,
- ground_surface_z=0.419,
- status=SettlementRodMeasurementStatus.OK,
- temperature=12.0,
- voltage=4232,
- comment=123,
- )
+
+
+@pytest.mark.parametrize(
+ "parameter,invalid_input,expected_error",
+ [
+ ("project", None, TypeError),
+ ("device", None, TypeError),
+ ("object_id", None, TypeError),
+ ("object_id", "", ValueError),
+ ("date_time", None, TypeError),
+ ("coordinate_reference_systems", None, TypeError),
+ ("rod_top_x", "123340.266", TypeError),
+ ("rod_top_y", None, TypeError),
+ ("rod_top_z", "0.807", TypeError),
+ ("rod_length", "2.0", TypeError),
+ ("rod_length", -2.0, ValueError),
+ ("ground_surface_z", "0.419", TypeError),
+ ("rod_bottom_z", "-1.193", TypeError),
+ ("temperature", "12.0", TypeError),
+ ("voltage", "4232", TypeError),
+ ("status_messages", None, TypeError),
+ ("status_messages", ["OK"], TypeError),
+ ],
+)
+def test_settlement_rod_measurement_init_with_invalid_input(
+ valid_settlement_rod_measurement_input: dict,
+ parameter: str,
+ invalid_input: Any,
+ expected_error: Type[Exception],
+) -> None:
+ """Test initialization of SettlementRodMeasurement with invalid input."""
+ valid_settlement_rod_measurement_input[parameter] = invalid_input
+ with pytest.raises(expected_error, match=parameter):
+ SettlementRodMeasurement(**valid_settlement_rod_measurement_input)
+
+
+def test_settlement_rod_measurement_status(
+ valid_settlement_rod_measurement_input: dict,
+) -> None:
+ """Test status property of SettlementRodMeasurement."""
+
+ # No status messages
+ valid_settlement_rod_measurement_input["status_messages"] = []
+ measurement = SettlementRodMeasurement(**valid_settlement_rod_measurement_input)
+ assert measurement.status == SettlementRodMeasurementStatus.OK
+
+ # Different status messages with OK as highest level.
+ valid_settlement_rod_measurement_input["status_messages"] = [
+ StatusMessage(code=5, description="OK", level=StatusMessageLevel.OK),
+ StatusMessage(code=1, description="No comments", level=StatusMessageLevel.OK),
+ ]
+ measurement = SettlementRodMeasurement(**valid_settlement_rod_measurement_input)
+ assert measurement.status == SettlementRodMeasurementStatus.OK
+
+ # Different status messages with INFO as highest level.
+ valid_settlement_rod_measurement_input["status_messages"] = [
+ StatusMessage(code=5, description="OK", level=StatusMessageLevel.OK),
+ StatusMessage(code=1, description="INFO", level=StatusMessageLevel.INFO),
+ ]
+ measurement = SettlementRodMeasurement(**valid_settlement_rod_measurement_input)
+ assert measurement.status == SettlementRodMeasurementStatus.INFO
+
+ # Different status messages with WARNING as highest level.
+ valid_settlement_rod_measurement_input["status_messages"] = [
+ StatusMessage(code=5, description="WARNING", level=StatusMessageLevel.WARNING),
+ StatusMessage(code=2, description="INFO", level=StatusMessageLevel.INFO),
+ ]
+ measurement = SettlementRodMeasurement(**valid_settlement_rod_measurement_input)
+ assert measurement.status == SettlementRodMeasurementStatus.WARNING
+
+ # Different status messages with ERROR as highest level.
+ valid_settlement_rod_measurement_input["status_messages"] = [
+ StatusMessage(code=5, description="WARNING", level=StatusMessageLevel.WARNING),
+ StatusMessage(code=10, description="ERROR", level=StatusMessageLevel.ERROR),
+ ]
+ measurement = SettlementRodMeasurement(**valid_settlement_rod_measurement_input)
+ assert measurement.status == SettlementRodMeasurementStatus.ERROR
diff --git a/tests/measurements/test_settlement_rod_measurement_series.py b/tests/measurements/test_settlement_rod_measurement_series.py
index 18c6ab9..9d4f986 100644
--- a/tests/measurements/test_settlement_rod_measurement_series.py
+++ b/tests/measurements/test_settlement_rod_measurement_series.py
@@ -142,9 +142,9 @@ def test_settlement_rod_measurement_series_to_dataframe_method(
)
assert df.iloc[i]["ground_surface_z"] == measurement.ground_surface_z
assert df.iloc[i]["status"] == measurement.status.value
+ assert df.iloc[i]["status_messages"] == "(code=0, description=OK, level=OK)"
assert df.iloc[i]["temperature"] == measurement.temperature
assert df.iloc[i]["voltage"] == measurement.voltage
- assert df.iloc[i]["comment"] == measurement.comment
def test_plot_x_time(