11import datetime
22import typing
33import numpy
4+ import warnings
45import pydantic
56
67
1819]
1920
2021
22+ def validate_timestamp (timestamp : str , raise_except : bool = True ) -> bool :
23+ """
24+ Validate a user-provided timestamp
25+ """
26+ try :
27+ _ = datetime .datetime .strptime (timestamp , DATETIME_FORMAT )
28+ except ValueError as e :
29+ if raise_except :
30+ raise e
31+ return False
32+
33+ return True
34+
35+
36+ @pydantic .validate_call (config = {"validate_default" : True })
37+ def simvue_timestamp (
38+ date_time : datetime .datetime
39+ | typing .Annotated [str | None , pydantic .BeforeValidator (validate_timestamp )]
40+ | None = None ,
41+ ) -> str :
42+ """Return the Simvue valid timestamp
43+
44+ Parameters
45+ ----------
46+ date_time: datetime.datetime, optional
47+ if provided, the datetime object to convert, else use current date and time
48+
49+ Returns
50+ -------
51+ str
52+ Datetime string valid for the Simvue server
53+ """
54+ if isinstance (date_time , str ):
55+ warnings .warn (
56+ "Timestamps as strings for object recording will be deprecated in Python API >= 2.3"
57+ )
58+ if not date_time :
59+ date_time = datetime .datetime .now (datetime .timezone .utc )
60+ elif isinstance (date_time , str ):
61+ _local_time = datetime .datetime .now ().tzinfo
62+ date_time = (
63+ datetime .datetime .strptime (date_time , DATETIME_FORMAT )
64+ .replace (tzinfo = _local_time )
65+ .astimezone (datetime .timezone .utc )
66+ )
67+ return date_time .strftime (DATETIME_FORMAT )
68+
69+
2170# Pydantic class to validate run.init()
2271class RunInput (pydantic .BaseModel ):
2372 model_config = pydantic .ConfigDict (extra = "forbid" )
@@ -33,44 +82,24 @@ class RunInput(pydantic.BaseModel):
3382class MetricSet (pydantic .BaseModel ):
3483 model_config = pydantic .ConfigDict (extra = "forbid" )
3584 time : pydantic .NonNegativeFloat | pydantic .NonNegativeInt
36- timestamp : str
85+ timestamp : typing . Annotated [ str | None , pydantic . BeforeValidator ( simvue_timestamp )]
3786 step : pydantic .NonNegativeInt
3887 values : dict [str , int | float | bool ]
3988
40- @pydantic .field_validator ("timestamp" , mode = "after" )
41- @classmethod
42- def timestamp_str (cls , value : str ) -> str :
43- try :
44- _ = datetime .datetime .strptime (value , DATETIME_FORMAT )
45- except ValueError as e :
46- raise AssertionError (
47- f"Invalid timestamp, expected form '{ DATETIME_FORMAT } '"
48- ) from e
49- return value
50-
5189
5290class GridMetricSet (pydantic .BaseModel ):
53- model_config = pydantic .ConfigDict (arbitrary_types_allowed = True , extra = "forbid" )
91+ model_config = pydantic .ConfigDict (
92+ arbitrary_types_allowed = True , extra = "forbid" , validate_default = True
93+ )
5494 time : pydantic .NonNegativeFloat | pydantic .NonNegativeInt
55- timestamp : str
95+ timestamp : typing . Annotated [ str | None , pydantic . BeforeValidator ( simvue_timestamp )]
5696 step : pydantic .NonNegativeInt
57- array : list | numpy .ndarray
97+ array : list [ float ] | numpy .ndarray
5898 grid : str
5999 metric : str
60100
61- @pydantic .field_validator ("timestamp" , mode = "after" )
62- @classmethod
63- def timestamp_str (cls , value : str ) -> str :
64- try :
65- datetime .datetime .strptime (value , DATETIME_FORMAT )
66- except ValueError as e :
67- raise AssertionError (
68- f"Invalid timestamp, expected form '{ DATETIME_FORMAT } '"
69- ) from e
70- return value
71-
72101 @pydantic .field_serializer ("array" , when_used = "always" )
73- def serialize_array (self , value : numpy .ndarray | list , * _ ) -> list :
102+ def serialize_array (self , value : numpy .ndarray | list [ float ] , * _ ) -> list [ float ] :
74103 if isinstance (value , list ):
75104 return value
76105 return value .tolist ()
@@ -79,15 +108,4 @@ def serialize_array(self, value: numpy.ndarray | list, *_) -> list:
79108class EventSet (pydantic .BaseModel ):
80109 model_config = pydantic .ConfigDict (extra = "forbid" )
81110 message : str
82- timestamp : str
83-
84- @pydantic .field_validator ("timestamp" , mode = "after" )
85- @classmethod
86- def timestamp_str (cls , value : str ) -> str :
87- try :
88- datetime .datetime .strptime (value , DATETIME_FORMAT )
89- except ValueError as e :
90- raise AssertionError (
91- f"Invalid timestamp, expected form '{ DATETIME_FORMAT } '"
92- ) from e
93- return value
111+ timestamp : typing .Annotated [str | None , pydantic .BeforeValidator (simvue_timestamp )]
0 commit comments