Skip to content

Commit be89e3b

Browse files
Remove references to now deprecated DataTimeSchema, replaces usages with more functional TimePeriod class
1 parent c269550 commit be89e3b

File tree

5 files changed

+65
-32
lines changed

5 files changed

+65
-32
lines changed

src/oshconnect/eventbus.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,25 @@
55
# Contact Email: ian@botts-inc.com
66
# =============================================================================
77
import collections
8+
from typing import Any
9+
from uuid import UUID
810
from abc import ABC
911

1012

13+
class Event(ABC):
14+
"""
15+
A base class for events in the event bus system.
16+
"""
17+
id: UUID
18+
topic: str
19+
payload: Any
20+
21+
def __init__(self, id: UUID, topic: str, payload: Any):
22+
self.id = id
23+
self.topic = topic
24+
self.payload = payload
25+
26+
1127
class EventBus(ABC):
1228
"""
1329
A base class for an event bus system.

src/oshconnect/resource_datamodels.py

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,14 @@
88

99
from typing import List
1010

11+
from timemanagement import TimeInstant
1112
from .geometry import Geometry
1213
from .api_utils import Link
1314
from pydantic import BaseModel, ConfigDict, Field, SerializeAsAny, model_validator
1415
from shapely import Point
1516

1617
from .schema_datamodels import DatastreamRecordSchema, CommandSchema
17-
from .timemanagement import DateTimeSchema, TimePeriod
18+
from .timemanagement import TimePeriod
1819

1920

2021
class BoundingBox(BaseModel):
@@ -114,7 +115,7 @@ class SystemResource(BaseModel):
114115
keywords: List[str] = Field(None)
115116
identifiers: List[str] = Field(None)
116117
classifiers: List[str] = Field(None)
117-
valid_time: DateTimeSchema = Field(None, alias="validTime")
118+
valid_time: TimePeriod = Field(None, alias="validTime")
118119
security_constraints: List[SecurityConstraints] = Field(None, alias="securityConstraints")
119120
legal_constraints: List[LegalConstraints] = Field(None, alias="legalConstraints")
120121
characteristics: List[Characteristics] = Field(None)
@@ -177,31 +178,31 @@ def handle_aliases(cls, values):
177178

178179

179180
class ObservationResource(BaseModel):
180-
model_config = ConfigDict(populate_by_name=True)
181+
model_config = ConfigDict(populate_by_name=True, arbitrary_types_allowed=True)
181182

182183
sampling_feature_id: str = Field(None, alias="samplingFeature@Id")
183184
procedure_link: Link = Field(None, alias="procedure@link")
184-
phenomenon_time: DateTimeSchema = Field(None, alias="phenomenonTime")
185-
result_time: DateTimeSchema = Field(..., alias="resultTime")
185+
phenomenon_time: TimeInstant = Field(None, alias="phenomenonTime")
186+
result_time: TimeInstant = Field(..., alias="resultTime")
186187
parameters: dict = Field(None)
187188
result: dict = Field(...)
188189
result_link: Link = Field(None, alias="result@link")
189190

190191

191192
class ControlStreamResource(BaseModel):
192-
model_config = ConfigDict(populate_by_name=True)
193+
model_config = ConfigDict(populate_by_name=True, arbitrary_types_allowed=True)
193194

194195
cs_id: str = Field(None, alias="id")
195196
name: str = Field(...)
196197
description: str = Field(None)
197-
valid_time: TimePeriod = Field(..., alias="validTime")
198+
valid_time: TimePeriod = Field(None, alias="validTime")
198199
input_name: str = Field(None, alias="inputName")
199200
procedure_link: Link = Field(None, alias="procedureLink@link")
200201
deployment_link: Link = Field(None, alias="deploymentLink@link")
201202
feature_of_interest_link: Link = Field(None, alias="featureOfInterest@link")
202203
sampling_feature_link: Link = Field(None, alias="samplingFeature@link")
203-
issue_time: DateTimeSchema = Field(None, alias="issueTime")
204-
execution_time: DateTimeSchema = Field(None, alias="executionTime")
204+
issue_time: TimePeriod = Field(None, alias="issueTime")
205+
execution_time: TimePeriod = Field(None, alias="executionTime")
205206
live: bool = Field(None)
206207
asynchronous: bool = Field(True, alias="async")
207208
command_schema: SerializeAsAny[CommandSchema] = Field(None, alias="schema")

src/oshconnect/streamableresource.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -802,7 +802,6 @@ class ControlStream(StreamableResource[ControlStreamResource]):
802802
_inbound_status_deque: deque
803803
_outbound_status_deque: deque
804804

805-
806805
def __init__(self, node: Node = None, controlstream_resource: ControlStreamResource = None):
807806
super().__init__(node=node)
808807
self._underlying_resource = controlstream_resource

src/oshconnect/timemanagement.py

Lines changed: 24 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@
1111
import time
1212
from datetime import datetime, timezone
1313
from enum import Enum
14-
from typing import Any, Self
14+
from typing import Any
1515

16-
from pydantic import BaseModel, ConfigDict, Field, field_validator, model_serializer, model_validator
16+
from pydantic import BaseModel, ConfigDict, Field, model_serializer, model_validator
1717

1818

1919
class TemporalModes(Enum):
@@ -200,24 +200,25 @@ def __repr__(self):
200200
return f'{self.get_iso_time()}'
201201

202202

203-
class DateTimeSchema(BaseModel):
204-
is_instant: bool = Field(True, description="Whether the date time is an instant or a period.")
205-
iso_date: str = Field(None, description="The ISO formatted date time.")
206-
time_period: tuple = Field(None, description="The time period of the date time.")
207-
208-
@model_validator(mode='before')
209-
def valid_datetime_type(self) -> Self:
210-
if self.is_instant:
211-
if self.iso_date is None:
212-
raise ValueError("Instant date time must have a valid ISO8601 date.")
213-
return self
214-
215-
@field_validator('iso_date')
216-
@classmethod
217-
def check_iso_date(cls, v) -> str:
218-
if not v:
219-
raise ValueError("Instant date time must have a valid ISO8601 date.")
220-
return v
203+
# class DateTimeSchema(BaseModel):
204+
# is_instant: bool = Field(True, description="Whether the date time is an instant or a period.")
205+
# iso_date: str = Field(None, description="The ISO formatted date time.")
206+
# time_period: tuple = Field(None, description="The time period of the date time.")
207+
#
208+
# @model_validator(mode='before')
209+
# def valid_datetime_type(self) -> Self:
210+
# print("DEBUGGING DateTimeSchema valid_datetime_type")
211+
# if self.is_instant:
212+
# if self.iso_date is None:
213+
# raise ValueError("Instant date time must have a valid ISO8601 date.")
214+
# return self
215+
#
216+
# @field_validator('iso_date')
217+
# @classmethod
218+
# def check_iso_date(cls, v) -> str:
219+
# if not v:
220+
# raise ValueError("Instant date time must have a valid ISO8601 date.")
221+
# return v
221222

222223

223224
class IndeterminateTime(Enum):
@@ -245,7 +246,7 @@ def valid_time_period(cls, data) -> Any:
245246
data_dict['end'] = cls.check_mbr_type(data['end'])
246247

247248
if not cls.compare_start_lt_end(data_dict['start'], data_dict['end']):
248-
raise ValueError("Start time must be less than end time")
249+
raise ValueError("Start time must be less than or equal to end time")
249250

250251
return data_dict
251252

@@ -263,11 +264,12 @@ def check_mbr_type(value):
263264
return tp
264265
elif isinstance(value, TimeInstant):
265266
return value
267+
return None
266268

267269
@classmethod
268270
def compare_start_lt_end(cls, start: TimeInstant | str, end: TimeInstant | str) -> bool:
269271
if isinstance(start, TimeInstant) and isinstance(end, TimeInstant):
270-
return start < end
272+
return start <= end
271273
elif isinstance(start, str) and isinstance(end, str):
272274
if start == "now" and end == "now":
273275
raise ValueError("Start and end cannot both be 'now'")

tests/test_resource_datamodels.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# =============================================================================
2+
# Copyright (c) 2025 Botts Innovative Research Inc.
3+
# Date: 2025/10/22
4+
# Author: Ian Patterson
5+
# Contact Email: ian@botts-inc.com
6+
# =============================================================================
7+
from src.oshconnect.resource_datamodels import ControlStreamResource
8+
9+
10+
def test_control_stream_resource():
11+
res_str = {'id': '0228vl6tn15g', 'name': 'Puppy Pi Control', 'description': 'Puppy pi control', 'system@id': '029tjlvogsng', 'system@link': {'href': 'http://192.168.8.136:8080/sensorhub/api/systems/029tjlvogsng?f=json', 'uid': 'urn:puppypi:001', 'type': 'application/geo+json'}, 'inputName': 'puppypicontrol', 'validTime': ['2025-10-21T19:04:56.505817Z', 'now'], 'issueTime': ['2025-10-22T17:12:58.51182Z', '2025-10-22T17:12:58.51182Z'], 'controlledProperties': [{'definition': 'http://sensorml.com/ont/swe/property/triggercontrol', 'label': 'Forward', 'description': 'Moves the puppy pi forward when true'}], 'formats': ['application/json', 'application/swe+json', 'application/swe+csv', 'application/swe+xml', 'application/swe+binary']}
12+
# res_dict = json.loads(res_str)
13+
csr = ControlStreamResource.model_validate(res_str)
14+
15+
assert isinstance(csr, ControlStreamResource)

0 commit comments

Comments
 (0)