Skip to content

Commit 68855d7

Browse files
Add deserialisation (i.e., field validators) for minute fields (#137)
* Add test Same as in 99955d7 * Add deserialization of minute fields to timedelta objects Fixes #136 * update validator
1 parent 39047a7 commit 68855d7

4 files changed

Lines changed: 36 additions & 0 deletions

File tree

pyproject.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,8 @@ ignore = [
101101
"E501",
102102
# consider `[meta, header, *data]` instead of concatenation
103103
"RUF005",
104+
# Use `X | Y` in `isinstance` call instead of `(X, Y)`
105+
"UP038",
104106
# multi-line-summary-first-line
105107
"D212",
106108
# one-blank-line-before-class

src/virtualship/expedition/ship_config.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
import pydantic
99
import yaml
1010

11+
from virtualship.utils import _validate_numeric_mins_to_timedelta
12+
1113

1214
class ArgoFloatConfig(pydantic.BaseModel):
1315
"""Configuration for argos floats."""
@@ -37,6 +39,10 @@ class ADCPConfig(pydantic.BaseModel):
3739
def _serialize_period(self, value: timedelta, _info):
3840
return value.total_seconds() / 60.0
3941

42+
@pydantic.field_validator("period", mode="before")
43+
def _validate_period(cls, value: int | float | timedelta) -> timedelta:
44+
return _validate_numeric_mins_to_timedelta(value)
45+
4046

4147
class CTDConfig(pydantic.BaseModel):
4248
"""Configuration for CTD instrument."""
@@ -55,6 +61,10 @@ class CTDConfig(pydantic.BaseModel):
5561
def _serialize_stationkeeping_time(self, value: timedelta, _info):
5662
return value.total_seconds() / 60.0
5763

64+
@pydantic.field_validator("stationkeeping_time", mode="before")
65+
def _validate_stationkeeping_time(cls, value: int | float | timedelta) -> timedelta:
66+
return _validate_numeric_mins_to_timedelta(value)
67+
5868

5969
class ShipUnderwaterSTConfig(pydantic.BaseModel):
6070
"""Configuration for underwater ST."""
@@ -71,6 +81,10 @@ class ShipUnderwaterSTConfig(pydantic.BaseModel):
7181
def _serialize_period(self, value: timedelta, _info):
7282
return value.total_seconds() / 60.0
7383

84+
@pydantic.field_validator("period", mode="before")
85+
def _validate_period(cls, value: int | float | timedelta) -> timedelta:
86+
return _validate_numeric_mins_to_timedelta(value)
87+
7488

7589
class DrifterConfig(pydantic.BaseModel):
7690
"""Configuration for drifters."""
@@ -88,6 +102,10 @@ class DrifterConfig(pydantic.BaseModel):
88102
def _serialize_lifetime(self, value: timedelta, _info):
89103
return value.total_seconds() / 60.0
90104

105+
@pydantic.field_validator("lifetime", mode="before")
106+
def _validate_lifetime(cls, value: int | float | timedelta) -> timedelta:
107+
return _validate_numeric_mins_to_timedelta(value)
108+
91109

92110
class XBTConfig(pydantic.BaseModel):
93111
"""Configuration for xbt instrument."""

src/virtualship/utils.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from datetime import timedelta
12
from functools import lru_cache
23
from importlib.resources import files
34
from typing import TextIO
@@ -151,3 +152,10 @@ def mfp_to_yaml(excel_file_path: str, yaml_output_path: str): # noqa: D417
151152

152153
# Save to YAML file
153154
schedule.to_yaml(yaml_output_path)
155+
156+
157+
def _validate_numeric_mins_to_timedelta(value: int | float | timedelta) -> timedelta:
158+
"""Convert minutes to timedelta when reading."""
159+
if isinstance(value, timedelta):
160+
return value
161+
return timedelta(minutes=value)

tests/expedition/test_simulate_schedule.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,3 +46,11 @@ def test_simulate_schedule_too_far() -> None:
4646
result = simulate_schedule(projection, ship_config, schedule)
4747

4848
assert isinstance(result, ScheduleProblem)
49+
50+
51+
def test_time_in_minutes_in_ship_schedule() -> None:
52+
"""Test whether the pydantic serializer picks up the time *in minutes* in the ship schedule."""
53+
ship_config = ShipConfig.from_yaml("expedition_dir/ship_config.yaml")
54+
assert ship_config.adcp_config.period == timedelta(minutes=5)
55+
assert ship_config.ctd_config.stationkeeping_time == timedelta(minutes=20)
56+
assert ship_config.ship_underwater_st_config.period == timedelta(minutes=5)

0 commit comments

Comments
 (0)