diff --git a/geomagio/api/ws/DataApiQuery.py b/geomagio/api/ws/DataApiQuery.py index e7c224532ecafcb02f180ce8724be595d2cae830..ac88e6092a94967b8d2a3907936b88fd776c6545 100644 --- a/geomagio/api/ws/DataApiQuery.py +++ b/geomagio/api/ws/DataApiQuery.py @@ -73,6 +73,15 @@ class DataApiQuery(BaseModel): format: Union[OutputFormat, str] = OutputFormat.IAGA2002 data_host: Union[DataHost, str] = DataHost.DEFAULT + @field_validator("starttime", mode="before") + def validate_starttime( + cls, starttime: CustomUTCDateTimeType + ) -> CustomUTCDateTimeType: + if not starttime: + return default_starttime() + else: + return starttime + @field_validator("data_host", mode="before") def validate_data_host(cls, data_host: DataHost) -> DataHost: if data_host not in DataHost.values(): diff --git a/geomagio/pydantic_utcdatetime.py b/geomagio/pydantic_utcdatetime.py index 862cd138446571bf1e8ec9efe2da62b50a5d0b56..7653f6ad6ff610f50f899b526db1eed73ebf4268 100644 --- a/geomagio/pydantic_utcdatetime.py +++ b/geomagio/pydantic_utcdatetime.py @@ -32,7 +32,7 @@ class CustomUTCDateTimeValidator: time = UTCDateTime(value) except: raise ValueError( - f"Invalid time type. Expected format is '%Y-%m-%dT%H:%M:%S.%fZ'" + f"Invalid time type. See obspy UTCDateTime for more information." ) return time diff --git a/test/DataApiQuery_test.py b/test/DataApiQuery_test.py index c03e88f29c231858b4c722b6a69f7cc4cb204bd5..117f1758c1be5f4d9668dc25208ec8fcee5a3596 100644 --- a/test/DataApiQuery_test.py +++ b/test/DataApiQuery_test.py @@ -30,6 +30,24 @@ def test_DataApiQuery_defaults(): assert_equal(query.data_host.value, "edgecwb.usgs.gov") +def test_DataApiQuery_starttime_is_none(): + query = DataApiQuery(id="BOU", starttime=None) + + now = datetime.datetime.now(tz=datetime.timezone.utc) + expected_start_time = UTCDateTime(year=now.year, month=now.month, day=now.day) + expected_endtime = expected_start_time + (86400 - 0.001) + + assert_equal(query.id, "BOU") + assert_equal(query.starttime, expected_start_time) + assert_equal(query.endtime, expected_endtime) + assert_equal(query.elements, ["X", "Y", "Z", "F"]) + assert_equal(query.sampling_period, SamplingPeriod.MINUTE) + assert_equal(query.data_type, DataType.VARIATION) + assert_equal(query.format, OutputFormat.IAGA2002) + # assumes the env var DATA_HOST is not set + assert_equal(query.data_host.value, "edgecwb.usgs.gov") + + def test_DataApiQuery_valid(): query = DataApiQuery( id="DED", @@ -88,6 +106,40 @@ def test_DataApiQuery_default_endtime(): assert_equal(query.data_host, DataHost.DEFAULT) +def test_DataApiQuery_default_only_endtime(): + # test a valid endtime that is after default starttime + hour_later = datetime.datetime.now() + datetime.timedelta(hours=1) + + query = DataApiQuery(id="BOU", endtime=hour_later) + + assert_equal(query.id, "BOU") + + now = datetime.datetime.now(tz=datetime.timezone.utc) + expected_start_time = UTCDateTime(year=now.year, month=now.month, day=now.day) + # starttime is at the beginning of today + assert_equal(query.starttime, expected_start_time) + # endtime is still what the user set + assert_equal(query.endtime, hour_later) + + assert_equal(query.elements, ["X", "Y", "Z", "F"]) + assert_equal(query.sampling_period, SamplingPeriod.MINUTE) + assert_equal(query.data_type, DataType.VARIATION) + assert_equal(query.format, OutputFormat.IAGA2002) + assert_equal(query.data_host, DataHost.DEFAULT) + + +def test_DataApiQuery_default_only_invalid_endtime(): + # test an invalid endtime that is before default starttime + query = None + try: + query = DataApiQuery(id="BOU", endtime="2024-09-01T00:00:01") + except Exception as e: + err = e.errors() + assert "Value error, Starttime must be before endtime." == err[0]["msg"] + + assert_equal(query, None) + + def test_DataApiQuery_starttime_after_endtime(): query = None try: diff --git a/test/api_test/ws_test/data_test.py b/test/api_test/ws_test/data_test.py index 2cf8d5d7699542e95f37ec6842c83993f1c05184..3376e31100056e7186ce47543fad8f81d352e404 100644 --- a/test/api_test/ws_test/data_test.py +++ b/test/api_test/ws_test/data_test.py @@ -1,3 +1,4 @@ +import datetime from fastapi import Depends from fastapi.testclient import TestClient from numpy.testing import assert_equal @@ -39,6 +40,21 @@ def test_get_data_query(test_client): assert_equal(query.data_type, "R1") +def test_get_data_query_no_starttime(test_client): + """test.api_test.ws_test.data_test.test_get_data_query_no_starttime()""" + response = test_client.get("/query/?id=BOU&elements=X,Y,Z,F") + query = DataApiQuery(**response.json()) + assert_equal(query.id, "BOU") + + # expect starttime to default to start of current day + now = datetime.datetime.now(tz=datetime.timezone.utc) + expected = UTCDateTime(year=now.year, month=now.month, day=now.day) + + assert_equal(query.starttime, expected) + # expect endtime to default to 1 day after starttiime + assert_equal(query.endtime, expected + (86400 - 0.001)) + + async def test_get_data_query_extra_params(test_client): with pytest.raises(ValueError) as error: response = await test_client.get( diff --git a/test/api_test/ws_test/filter_test.py b/test/api_test/ws_test/filter_test.py index 20fb73cf92124d9510a665b7e4c574737ddad9a1..dbd2964ea2072d8dd678ae745fe60a5f37c54f64 100644 --- a/test/api_test/ws_test/filter_test.py +++ b/test/api_test/ws_test/filter_test.py @@ -1,3 +1,4 @@ +import datetime from fastapi import Depends from fastapi.testclient import TestClient from numpy.testing import assert_equal @@ -35,3 +36,18 @@ def test_get_filter_data_query(test_client): assert_equal(query.data_type, "variation") assert_equal(query.input_sampling_period, SamplingPeriod.MINUTE) assert_equal(query.output_sampling_period, SamplingPeriod.HOUR) + + +def test_get_filter_data_query_no_starttime(test_client): + """test.api_test.ws_test.data_test.test_get_filter_data_query_no_starttime()""" + response = test_client.get("/filter/?id=BOU&elements=X,Y,Z,F") + query = FilterApiQuery(**response.json()) + assert_equal(query.id, "BOU") + + # expect starttime to default to start of current day + now = datetime.datetime.now(tz=datetime.timezone.utc) + expected = UTCDateTime(year=now.year, month=now.month, day=now.day) + + assert_equal(query.starttime, expected) + # expect endtime to default to 1 day after starttiime + assert_equal(query.endtime, expected + (86400 - 0.001)) diff --git a/test/pydantic_utcdatetime_test.py b/test/pydantic_utcdatetime_test.py index ef81615a4ede899880b017224b0586b8eae257bd..232cd7b1cd6e58ecbe5b91e4eeb53efc87f900b7 100644 --- a/test/pydantic_utcdatetime_test.py +++ b/test/pydantic_utcdatetime_test.py @@ -17,6 +17,13 @@ def test_UTCDateTime_string(): assert_equal(t.starttime, UTCDateTime(2024, 11, 5, 0, 0)) +def test_UTCDateTime_ISO860_string(): + # `ISO8601:2004`_ string from obspy docs + t = TimeClass(starttime="2024-11-05T00:00:00.00") + + assert_equal(t.starttime, UTCDateTime(2024, 11, 5, 0, 0)) + + def test_UTCDateTime_datetime(): t = TimeClass(starttime=datetime.datetime(2024, 11, 5, tzinfo=tz.tzutc())) @@ -42,5 +49,9 @@ def test_invalid(): except Exception as e: err = e.errors() assert "Input should be an instance of UTCDateTime" == err[0]["msg"] + assert ( + "Value error, Invalid time type. See obspy UTCDateTime for more information." + == err[1]["msg"] + ) assert_equal(t, None)