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 | str, optional
47+ if provided, the datetime object to convert,
48+ else use current date and time
49+ if a string assume to be local time.
50+
51+ Returns
52+ -------
53+ str
54+ Datetime string valid for the Simvue server
55+ """
56+ if isinstance (date_time , str ):
57+ warnings .warn (
58+ "Timestamps as strings for object recording will be deprecated in Python API >= 2.3"
59+ )
60+ if not date_time :
61+ date_time = datetime .datetime .now (datetime .timezone .utc )
62+ elif isinstance (date_time , str ):
63+ _local_time = datetime .datetime .now ().tzinfo
64+ date_time = (
65+ datetime .datetime .strptime (date_time , DATETIME_FORMAT )
66+ .replace (tzinfo = _local_time )
67+ .astimezone (datetime .timezone .utc )
68+ )
69+ return date_time .strftime (DATETIME_FORMAT )
70+
71+
2172# Pydantic class to validate run.init()
2273class RunInput (pydantic .BaseModel ):
2374 model_config = pydantic .ConfigDict (extra = "forbid" )
@@ -33,44 +84,24 @@ class RunInput(pydantic.BaseModel):
3384class MetricSet (pydantic .BaseModel ):
3485 model_config = pydantic .ConfigDict (extra = "forbid" )
3586 time : pydantic .NonNegativeFloat | pydantic .NonNegativeInt
36- timestamp : str
87+ timestamp : typing . Annotated [ str | None , pydantic . BeforeValidator ( simvue_timestamp )]
3788 step : pydantic .NonNegativeInt
3889 values : dict [str , int | float | bool ]
3990
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-
5191
5292class GridMetricSet (pydantic .BaseModel ):
53- model_config = pydantic .ConfigDict (arbitrary_types_allowed = True , extra = "forbid" )
93+ model_config = pydantic .ConfigDict (
94+ arbitrary_types_allowed = True , extra = "forbid" , validate_default = True
95+ )
5496 time : pydantic .NonNegativeFloat | pydantic .NonNegativeInt
55- timestamp : str
97+ timestamp : typing . Annotated [ str | None , pydantic . BeforeValidator ( simvue_timestamp )]
5698 step : pydantic .NonNegativeInt
57- array : list | numpy .ndarray
99+ array : list [ float ] | list [ list [ float ]] | numpy .ndarray
58100 grid : str
59101 metric : str
60102
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-
72103 @pydantic .field_serializer ("array" , when_used = "always" )
73- def serialize_array (self , value : numpy .ndarray | list , * _ ) -> list :
104+ def serialize_array (self , value : numpy .ndarray | list [ float ] , * _ ) -> list [ float ] :
74105 if isinstance (value , list ):
75106 return value
76107 return value .tolist ()
@@ -79,15 +110,4 @@ def serialize_array(self, value: numpy.ndarray | list, *_) -> list:
79110class EventSet (pydantic .BaseModel ):
80111 model_config = pydantic .ConfigDict (extra = "forbid" )
81112 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
113+ timestamp : typing .Annotated [str | None , pydantic .BeforeValidator (simvue_timestamp )]
0 commit comments