From 2942b06f6a174c32d3e84b4289dbfb7902b5f0d1 Mon Sep 17 00:00:00 2001 From: zack-rma Date: Mon, 23 Sep 2024 11:29:36 -0700 Subject: [PATCH 1/6] Added timeseries profile support with associated tests --- cwms/__init__.py | 3 + cwms/timeseries/timeseries_profile.py | 151 ++++++++++++ .../timeseries/timeseries_profile_instance.py | 229 ++++++++++++++++++ cwms/timeseries/timeseries_profile_parser.py | 192 +++++++++++++++ tests/resources/timeseries_profile.json | 16 ++ tests/resources/timeseries_profile_data.txt | 28 +++ .../resources/timeseries_profile_indexed.json | 28 +++ .../timeseries_profile_instance.json | 56 +++++ .../timeseries_profile_instances.json | 38 +++ tests/resources/timeseries_profiles.json | 34 +++ .../timeseries_profiles_indexed.json | 58 +++++ .../timeseries_profile_instance_test.py | 120 +++++++++ .../timeseries_profile_parser_test.py | 76 ++++++ tests/timeseries/timeseries_profile_test.py | 73 ++++++ 14 files changed, 1102 insertions(+) create mode 100644 cwms/timeseries/timeseries_profile.py create mode 100644 cwms/timeseries/timeseries_profile_instance.py create mode 100644 cwms/timeseries/timeseries_profile_parser.py create mode 100644 tests/resources/timeseries_profile.json create mode 100644 tests/resources/timeseries_profile_data.txt create mode 100644 tests/resources/timeseries_profile_indexed.json create mode 100644 tests/resources/timeseries_profile_instance.json create mode 100644 tests/resources/timeseries_profile_instances.json create mode 100644 tests/resources/timeseries_profiles.json create mode 100644 tests/resources/timeseries_profiles_indexed.json create mode 100644 tests/timeseries/timeseries_profile_instance_test.py create mode 100644 tests/timeseries/timeseries_profile_parser_test.py create mode 100644 tests/timeseries/timeseries_profile_test.py diff --git a/cwms/__init__.py b/cwms/__init__.py index f1e9a48e..f8db7d6f 100644 --- a/cwms/__init__.py +++ b/cwms/__init__.py @@ -14,6 +14,9 @@ from cwms.timeseries.timerseries_identifier import * from cwms.timeseries.timeseries import * from cwms.timeseries.timeseries_bin import * +from cwms.timeseries.timeseries_profile import * +from cwms.timeseries.timeseries_profile_instance import * +from cwms.timeseries.timeseries_profile_parser import * from cwms.timeseries.timeseries_txt import * try: diff --git a/cwms/timeseries/timeseries_profile.py b/cwms/timeseries/timeseries_profile.py new file mode 100644 index 00000000..7461f698 --- /dev/null +++ b/cwms/timeseries/timeseries_profile.py @@ -0,0 +1,151 @@ +# Copyright (c) 2024 +# United States Army Corps of Engineers - Hydrologic Engineering Center (USACE/HEC) +# All Rights Reserved. USACE PROPRIETARY/CONFIDENTIAL. +# Source may not be released without written approval from HEC + +from datetime import datetime +from typing import Optional + +import pandas as pd + +import cwms.api as api +from cwms.cwms_types import Data + + +def get_timeseries_profile(office_id: str, location_id: str, parameter_id: str) -> Data: + """ + Retrieves a timeseries profile. + + Parameters + ---------- + office_id: string + The owning office of the timeseries profile + location_id: string + The location associated with the timeseries profile parser + parameter_id: string + Name of the key parameter associated with the timeseries profile + + Returns + ------- + cwms data type + """ + + endpoint = f"timeseries/profile/{parameter_id}" + params = { + "office": office_id, + "location-id": location_id, + } + + response = api.get(endpoint, params) + return Data(response) + + +def get_timeseries_profiles( + office_mask: Optional[str], + location_mask: Optional[str], + parameter_id_mask: Optional[str], + page: Optional[str] = None, + page_size: Optional[int] = 1000, +) -> Data: + """ + Retrieves all timeseries profiles that fit the provided masks. Does not include time series values. + + Parameters + ---------- + office_mask: string + A mask to limit the results based on office. Uses regex to compare with office IDs in the database. + Default value is '*' + location_mask: string + A mask to limit the results based on location. Uses regex to compare with location IDs in the database. + Default value is '*' + parameter_id_mask: string + A mask to limit the results based on the parameter associated with the timeseries profile. Uses regex to + compare the parameter IDs in the database. Default value is '*' + + Returns + ------- + cwms data type + """ + + endpoint = "timeseries/profile" + params = { + "office-mask": office_mask, + "location-mask": location_mask, + "parameter-id-mask": parameter_id_mask, + "page": page, + "page-size": page_size, + } + + response = api.get(endpoint, params) + return Data(response) + + +def delete_timeseries_profile( + office_id: str, parameter_id: str, location_id: str +) -> None: + """ + Deletes a specified timeseries profile + + Parameters + ---------- + office_id: string + The owning office of the timeseries profile + parameter_id: string + Name of the key parameter associated with the timeseries profile + location_id: string + The location associated with the timeseries profile + + Returns + ------- + None + """ + + endpoint = f"timeseries/profile/{parameter_id}" + params = { + "office": office_id, + "location-id": location_id, + } + + return api.delete(endpoint, params) + + +def store_timeseries_profile(data: str, fail_if_exists: Optional[bool] = True) -> None: + """ + Stores a new timeseries profile + + Parameters + ---------- + data: string + json for storing a timeseries profile + { + "description": "string", + "parameter-list": [ + "string", + ... + ], + "location-id": { + "office-id": "string", + "name": "string" + }, + "reference-ts-id": { + "office-id": "string", + "name": "string" + }, + "key-parameter": "string" + } + + fail_if_exists: boolean, optional + Throw a ClientError if the profile already exists + Default is `True` + + Returns + ------- + None + """ + + endpoint = "timeseries/profile" + params = { + "fail-if-exists": fail_if_exists, + } + + return api.post(endpoint, data, params) diff --git a/cwms/timeseries/timeseries_profile_instance.py b/cwms/timeseries/timeseries_profile_instance.py new file mode 100644 index 00000000..b395110b --- /dev/null +++ b/cwms/timeseries/timeseries_profile_instance.py @@ -0,0 +1,229 @@ +# Copyright (c) 2024 +# United States Army Corps of Engineers - Hydrologic Engineering Center (USACE/HEC) +# All Rights Reserved. USACE PROPRIETARY/CONFIDENTIAL. +# Source may not be released without written approval from HEC + +from datetime import datetime +from typing import Optional + +import cwms.api as api +from cwms.cwms_types import Data + + +def get_timeseries_profile_instance( + office_id: str, + timeseries_id: str, + parameter_id: str, + version: str, + timezone: Optional[str], + unit: str, + version_date: Optional[datetime], + start: Optional[datetime], + end: Optional[datetime], + page: Optional[str] = None, + page_size: Optional[int] = 1000, + start_inclusive: Optional[bool] = True, + end_inclusive: Optional[bool] = True, + previous: Optional[bool] = False, + next: Optional[bool] = False, + max_version: Optional[bool] = False, +) -> Data: + """ + Returns a timeseries profile instance with associated timeseries values. + + Parameters + ---------- + office_id: string + The owning office of the timeseries profile instance + timeseries_id: string + The name identifier associated with the timeseries profile instance + parameter_id: string + The name of the key parameter associated with the timeseries profile instance + version: str + The version of the timeseries profile instance + timezone: str + The timezone of the timeseries profile instance. The default value is `UTC`. + unit: str + The requested units to use for the key parameter values of the timeseries profile instance + version_date: datetime, optional + The version date associated with the timeseries profile instance + start_inclusive: boolean, optional + Whether the returned timeseries profile instance should include data from the specified + start timestamp. Default is `True`. + end_inclusive: boolean, optional + Whether the returned timeseries profile instance should include data from the specified + end timestamp. Default is `True`. + previous: boolean, optional + The previous timeseries profile instance. Default is `False`. + next: boolean, optional + The next timeseries profile instance. Default is `False`. + max_version: boolean, optional + Whether the provided version is the maximum version of the timeseries profile instance. + Default is `False`. + start: datetime, optional + The start timestamp of the timeseries profile instance. Default is the year 1800. + end: datetime, optional + The end timestamp of the timeseries profile instance. Default is the year 3000. + page: string, optional + The page cursor of the timeseries profile instance. + page_size: string, optional + The number of timeseries profile instance data records retrieved as part of the instance. Default is `1000`. + + Returns + ------- + cwms data type + """ + + endpoint = f"timeseries/instance/{timeseries_id}" + params = { + "office": office_id, + "parameter-id": parameter_id, + "version": version, + "timezone": timezone, + "version-date": version_date.isoformat() if version_date else None, + "unit": unit, + "start-inclusive": start_inclusive, + "end-inclusive": end_inclusive, + "previous": previous, + "next": next, + "max-version": max_version, + "start": start.isoformat() if start else None, + "end": end.isoformat() if end else None, + "page": page, + "page-size": page_size, + } + + response = api.get(endpoint, params) + return Data(response) + + +def get_timeseries_profile_instances( + office_mask: Optional[str], + location_mask: Optional[str], + parameter_id_mask: Optional[str], + version_mask: Optional[str], +) -> Data: + """ + Retrieves a list of timeseries profile instances that match the specified masks. Does not return timeseries values. + + Parameters + ---------- + office_mask: string + A mask to limit the results based on office ID. Uses regex to compare with office IDs in the database. + Default value is `*` + location_mask: string + A mask to limit the results based on location ID. Uses regex to compare with location IDs in the database. + Default value is `*` + parameter_id_mask: string + A mask to limit the results based on the parameter associated with the timeseries profile instance. + Uses regex to compare the parameter IDs in the database. Default value is `*` + version_mask: string + A mask to limit the results based on the version associated with the timeseries profile instance. + Default value is `*` + + Returns + ------- + cwms data type + """ + + endpoint = "timeseries/instance" + params = { + "office-mask": office_mask, + "location-mask": location_mask, + "parameter-id-mask": parameter_id_mask, + "version-mask": version_mask, + } + + response = api.get(endpoint, params) + return Data(response) + + +def delete_timeseries_profile_instance( + office_id: str, + timeseries_id: str, + parameter_id: str, + version: str, + timezone: Optional[str], + version_date: Optional[datetime], + date: Optional[datetime], + override_protection: Optional[bool] = True, +) -> None: + """ + Deletes a timeseries profile instance. + + Parameters + ---------- + office_id: string + The owning office of the timeseries profile instance + timeseries_id: string + The name identifier for the timeseries profile instance to delete + parameter_id: string + The name of the key parameter associated with the timeseries profile instance + version: string + The version of the timeseries profile instance + timezone: string + The timezone used for the timestamps associated with the timeseries profile instance + version_date: datetime, optional + The timestamp of the timeseries profile instance version. Default is current date and time. + date: datetime, optional + The first date of the timeseries profile instance. Default is current date and time + override_protection: boolean, optional + Whether to enable override protection for the timeseries profile instance. Default is `True`. + + Returns + ------- + None + """ + + endpoint = f"timeseries/instance/{timeseries_id}" + params = { + "office": office_id, + "parameter-id": parameter_id, + "version": version, + "timezone": timezone, + "version-date": version_date.isoformat() if version_date else None, + "date": date.isoformat() if date else None, + "override-protection": override_protection, + } + + return api.delete(endpoint, params) + + +def store_timeseries_profile_instance( + profile_data: str, + version: str, + version_date: Optional[datetime] = None, + store_rule: Optional[str] = None, + override_protection: Optional[bool] = False, +) -> None: + """ + Stores a new timeseries profile instance. Requires timeseries profile and parser to already be stored. + + Parameters + ---------- + profile_data: string + The profile data of the timeseries profile instance + store_rule: boolean, optional + The method of storing the timeseries profile instance. Default is `REPLACE_ALL`. + version: string + The version of the timeseries profile instance. + version_date: datetime, optional + The version date of the timeseries profile instance. Default is the current date and time. + override_protection: boolean, optional + Whether to enable override protection for the timeseries profile instance. Default is `False`. + + Returns + ------- + None + """ + + endpoint = "timeseries/instance" + params = { + "profile-data": profile_data, + "method": store_rule, + "version": version, + "version-date": version_date.isoformat() if version_date else None, + "override-protection": override_protection, + } + + return api.post(endpoint, None, params) diff --git a/cwms/timeseries/timeseries_profile_parser.py b/cwms/timeseries/timeseries_profile_parser.py new file mode 100644 index 00000000..eb9b104e --- /dev/null +++ b/cwms/timeseries/timeseries_profile_parser.py @@ -0,0 +1,192 @@ +# Copyright (c) 2024 +# United States Army Corps of Engineers - Hydrologic Engineering Center (USACE/HEC) +# All Rights Reserved. USACE PROPRIETARY/CONFIDENTIAL. +# Source may not be released without written approval from HEC + +from datetime import datetime +from typing import Optional + +import cwms.api as api +from cwms.cwms_types import Data + + +def get_timeseries_profile_parser( + office_id: str, location_id: str, parameter_id: str +) -> Data: + """ + Returns a timeseries profile parser used to interpret timeseries data input. + + Parameters + ---------- + office_id: string + The owning office of the timeseries profile parser + location_id: string + The location name associated with the timeseries profile parser + parameter_id: string + The name of the key parameter associated with the timeseries profile parser + + Returns + ------- + cwms data type + """ + + endpoint = f"timeseries/parser/{parameter_id}" + params = { + "office": office_id, + "location-id": location_id, + } + + response = api.get(endpoint, params) + return Data(response) + + +def get_timeseries_profile_parsers( + office_mask: Optional[str], + location_mask: Optional[str], + parameter_id_mask: Optional[str], +) -> Data: + """ + Returns a list of timeseries profile parsers. + + Parameters + ---------- + office_mask: string, optional + A mask to limit the results based on office. Uses regex to compare with office IDs in the database. + Default value is '*' + location_mask: string, optional + A mask to limit the results based on location. Uses regex to compare with location IDs in the database. + Default value is '*' + parameter_id_mask: string, optional + A mask to limit the results based on the parameter associated with the timeseries profile. Uses regex to + compare the parameter IDs in the database. Default value is '*' + + Returns + ------- + cwms data type + """ + + endpoint = "timeseries/parser" + params = { + "office-mask": office_mask, + "location-mask": location_mask, + "parameter-id-mask": parameter_id_mask, + } + + response = api.get(endpoint, params) + return Data(response) + + +def delete_timeseries_profile_parser( + office_id: str, location_id: str, parameter_id: str +) -> None: + """ + Deletes a specified timeseries profile parser + + Parameters + ---------- + office_id: string + The owning office of the timeseries profile parser + location_id: string + The location associated with the timeseries profile parser + parameter_id: string + The name of the key parameter associated with the timeseries profile parser + + Returns + ------- + None + """ + + endpoint = f"timeseries/parser/{parameter_id}" + params = {"office": office_id, "location-id": location_id} + + return api.delete(endpoint, params) + + +def store_timeseries_profile_parser( + data: str, fail_if_exists: Optional[bool] = True +) -> None: + """ + Stores a new timeseries profile parser. + + Parameters + ---------- + data: string + JSON for storing a timeseries profile parser + + Indexed: + { + "type": "indexed-timeseries-profile-parser", + "location-id": { + "office-id": "string", + "name": "string" + }, + "key-parameter": "string", + "record-delimiter": "char", + "time-format": "MM/DD/YYYY,HH24:MI:SS", + "time-zone": "string", + "parameter-info-list": [ + { + "type": "indexed-parameter-info", + "parameter": "string", + "unit": "string", + "index": int + }, + { + "type": "indexed-parameter-info", + "parameter": "string", + "unit": "string", + "index": int + } + ], + "time-in-two-fields": bool, + "field-delimiter": "char", + "time-field": int + } + + Columnar: + { + "type": "columnar-timeseries-profile-parser", + "location-id": { + "office-id": "string", + "name": "string" + }, + "key-parameter": "string", + "record-delimiter": "char", + "time-format": "MM/DD/YYYY,HH24:MI:SS", + "time-zone": "string", + "parameter-info-list": [ + { + "type": "columnar-parameter-info", + "parameter": "string", + "unit": "string", + "start-column": int, + "end-column": int + }, + { + "type": "columnar-parameter-info", + "parameter": "string", + "unit": "string", + "start-column": int, + "end-column": int + } + ], + "time-in-two-fields": bool, + "time-start-column": int, + "time-end-column": int + } + + fail_if_exists: boolean, optional + Throw a ClientError if the parser already exists + Default is `True` + + Returns + ------- + None + """ + + endpoint = "timeseries/parser" + params = { + "fail-if-exists": fail_if_exists, + } + + return api.post(endpoint, data, params) diff --git a/tests/resources/timeseries_profile.json b/tests/resources/timeseries_profile.json new file mode 100644 index 00000000..49913f5c --- /dev/null +++ b/tests/resources/timeseries_profile.json @@ -0,0 +1,16 @@ +{ + "description": "Description", + "parameter-list": [ + "Temp-Water", + "Depth" + ], + "location-id": { + "office-id": "SWT", + "name": "SWAN" + }, + "reference-ts-id": { + "office-id": "SWT", + "name": "SWAN.Elev.Inst.1Hour.0.DSS-Obs" + }, + "key-parameter": "Depth" +} \ No newline at end of file diff --git a/tests/resources/timeseries_profile_data.txt b/tests/resources/timeseries_profile_data.txt new file mode 100644 index 00000000..e41d591e --- /dev/null +++ b/tests/resources/timeseries_profile_data.txt @@ -0,0 +1,28 @@ +'sep=, Date,Time,Site,Unit ID,User ID,F,mmHg,DO %,DO mg/L,SPC-uS/cm,pH,NTU, +BGA-PCRFU-18H110486,BGA-PCug/L-18H110486,Chl RFU-18H110486,Chl ug/L-18H110486,DEP ft-18H105572,Batt V-18H111574,Lat-18H111574,Lon-18H111574, +09/09/2019,12:48:57,DET,,Holly Bellringer,67.108,719.6,98.3,9.03,43.9,8.54,0.19,0.21,-0.65,0.17,-0.01,3.152,5.35,44.71898,-122.24620, +09/09/2019,12:49:19,DET,,Holly Bellringer,67.563,719.6,97.2,8.88,43.4,8.43,0.12,0.21,-0.65,0.19,0.05,4.028,5.34,44.71898,-122.24620, +09/09/2019,12:52:54,DET,,Holly Bellringer,67.820,719.5,96.8,8.82,43.3,8.04,0.23,0.14,-0.71,0.20,0.10,6.264,5.32,44.71899,-122.24610, +09/09/2019,12:53:17,DET,,Holly Bellringer,67.767,719.6,96.6,8.81,43.3,8.02,0.12,0.15,-0.70,0.24,0.24,8.525,5.31,44.71900,-122.24610, +09/09/2019,12:54:42,DET,,Holly Bellringer,67.718,719.6,96.5,8.80,43.3,7.97,0.11,0.13,-0.72,0.25,0.29,10.789,5.31,44.71898,-122.24620, +09/09/2019,12:55:50,DET,,Holly Bellringer,67.700,719.6,96.4,8.79,43.3,7.95,0.08,0.17,-0.68,0.23,0.19,12.045,5.27,44.71898,-122.24620, +09/09/2019,12:56:27,DET,,Holly Bellringer,67.682,719.5,96.3,8.79,43.2,7.92,0.08,0.16,-0.70,0.24,0.24,14.188,5.28,44.71897,-122.24620, +09/09/2019,12:56:54,DET,,Holly Bellringer,67.669,719.5,96.2,8.78,43.2,7.92,0.17,0.10,-0.75,0.27,0.39,16.218,5.26,44.71897,-122.24620, +09/09/2019,12:57:48,DET,,Holly Bellringer,67.651,719.5,96.1,8.77,43.2,7.86,0.13,0.10,-0.75,0.22,0.16,18.303,5.26,44.71901,-122.24620, +09/09/2019,12:58:57,DET,,Holly Bellringer,67.613,719.6,95.8,8.75,43.2,7.86,0.05,0.15,-0.70,0.24,0.26,23.081,5.24,44.71901,-122.24620, +09/09/2019,12:59:31,DET,,Holly Bellringer,67.572,719.6,95.3,8.71,43.2,7.83,0.12,0.12,-0.74,0.30,0.48,28.262,5.26,44.71900,-122.24620, +09/09/2019,13:00:37,DET,,Holly Bellringer,67.557,719.6,95.1,8.69,43.2,7.82,0.07,0.11,-0.74,0.27,0.39,33.043,5.28,44.71904,-122.24610, +09/09/2019,13:01:08,DET,,Holly Bellringer,67.495,719.6,94.6,8.65,43.2,7.78,0.17,0.15,-0.71,0.26,0.35,38.527,5.27,44.71902,-122.24620, +09/09/2019,13:02:17,DET,,Holly Bellringer,67.003,719.7,91.4,8.40,43.4,7.74,0.14,0.26,-0.59,0.57,1.55,43.937,5.28,44.71902,-122.24620, +09/09/2019,13:02:51,DET,,Holly Bellringer,64.150,719.6,77.9,7.40,44.2,7.63,0.12,0.15,-0.70,0.33,0.62,48.191,5.27,44.71902,-122.24620, +09/09/2019,13:03:50,DET,,Holly Bellringer,62.782,719.6,71.1,6.86,44.2,7.49,0.13,0.26,-0.59,0.40,0.88,53.288,5.28,44.71902,-122.24620, +09/09/2019,13:04:50,DET,,Holly Bellringer,61.698,719.7,65.8,6.43,43.5,7.37,0.19,0.22,-0.63,0.37,0.76,58.457,5.28,44.71902,-122.24620, +09/09/2019,13:07:48,DET,,Holly Bellringer,54.818,719.8,61.8,6.56,39.4,7.12,0.67,0.18,-0.67,0.12,-0.23,88.792,5.29,44.71902,-122.24620, +09/09/2019,13:08:57,DET,,Holly Bellringer,52.003,719.8,68.4,7.53,37.9,7.07,0.08,0.28,-0.58,0.13,-0.19,103.598,5.28,44.71904,-122.24620, +09/09/2019,13:09:46,DET,,Holly Bellringer,49.363,719.8,73.9,8.42,37.4,7.05,0.17,0.23,-0.63,0.13,-0.19,118.692,5.30,44.71901,-122.24620, +09/09/2019,13:11:20,DET,,Holly Bellringer,47.156,719.8,76.6,8.98,37.2,7.03,0.28,0.28,-0.58,0.10,-0.29,133.659,5.32,44.71902,-122.24620, +09/09/2019,13:12:45,DET,,Holly Bellringer,45.468,719.8,75.9,9.11,37.0,7.00,0.43,0.30,-0.55,0.11,-0.26,148.541,5.31,44.71902,-122.24620, +09/09/2019,13:13:33,DET,,Holly Bellringer,44.743,719.8,75.4,9.13,37.0,6.99,0.52,0.35,-0.50,0.10,-0.30,163.665,5.31,44.71900,-122.24620, +09/09/2019,13:14:49,DET,,Holly Bellringer,44.135,719.8,75.5,9.22,36.9,6.98,0.67,0.30,-0.55,0.13,-0.20,178.560,5.31,44.71901,-122.24620, +09/09/2019,13:16:01,DET,,Holly Bellringer,43.701,719.8,75.7,9.30,36.9,6.97,0.88,0.31,-0.54,0.08,-0.38,193.010,5.32,44.71903,-122.24620, +09/09/2019,13:17:20,DET,,Holly Bellringer,43.390,719.8,75.6,9.33,36.9,6.96,1.00,0.32,-0.53,0.10,-0.31,208.607,5.31,44.71902,-122.24620,' diff --git a/tests/resources/timeseries_profile_indexed.json b/tests/resources/timeseries_profile_indexed.json new file mode 100644 index 00000000..864f7312 --- /dev/null +++ b/tests/resources/timeseries_profile_indexed.json @@ -0,0 +1,28 @@ +{ + "type": "indexed-timeseries-profile-parser", + "location-id": { + "office-id": "SWT", + "name": "location" + }, + "key-parameter": "Depth", + "record-delimiter": "\n", + "time-format": "MM/DD/YYYY,HH24:MI:SS", + "time-zone": "UTC", + "parameter-info-list": [ + { + "type": "indexed-parameter-info", + "parameter": "Depth", + "unit": "m", + "index": 3 + }, + { + "type": "indexed-parameter-info", + "parameter": "Temp-Water", + "unit": "F", + "index": 5 + } + ], + "time-in-two-fields": false, + "field-delimiter": ",", + "time-field": 1 +} \ No newline at end of file diff --git a/tests/resources/timeseries_profile_instance.json b/tests/resources/timeseries_profile_instance.json new file mode 100644 index 00000000..4583e043 --- /dev/null +++ b/tests/resources/timeseries_profile_instance.json @@ -0,0 +1,56 @@ +{ + "time-series-profile": { + "location-id": { + "office-id": "SWT", + "name": "SWAN" + }, + "description": "Description", + "parameter-list": [ + "Temp-Water", + "Depth" + ], + "key-parameter": "Depth", + "reference-ts-id": { + "office-id": "SWT", + "name": "SWAN.Elev.Inst.1Hour.0.DSS-Obs" + } + }, + "time-series-list": [ + { + "parameter": "Temp-Water", + "unit": "F", + "time-zone": "PST", + "values": [ + { + "date-time": 1612869582120, + "value": 1.0, + "quality": 0 + }, + { + "date-time": 1612869582220, + "value": 3.0, + "quality": 0 + } + ] + }, + { + "parameter": "Depth", + "unit": "ft", + "time-zone": "PST", + "values": [ + { + "date-time": 1612869582120, + "value": 1.0, + "quality": 0 + }, + { + "date-time": 1612869582220, + "value": 3.0, + "quality": 0 + } + ] + } + ], + "first-date": 1594296000000, + "last-date": 1752062400000 +} \ No newline at end of file diff --git a/tests/resources/timeseries_profile_instances.json b/tests/resources/timeseries_profile_instances.json new file mode 100644 index 00000000..08e5a819 --- /dev/null +++ b/tests/resources/timeseries_profile_instances.json @@ -0,0 +1,38 @@ +[ + { + "time-series-profile": { + "location-id": { + "office-id": "SWT", + "name": "SWAN" + }, + "description": "Description", + "parameter-list": [], + "key-parameter": "Depth", + "reference-ts-id": { + "office-id": "SWT", + "name": "SWAN.Elev.Inst.1Hour.0.DSS-Obs" + } + }, + "time-series-list": [], + "first-date": 1594296000000, + "last-date": 1752062400000 + }, + { + "time-series-profile": { + "location-id": { + "office-id": "SWT", + "name": "SWAN2" + }, + "description": "Description", + "parameter-list": [], + "key-parameter": "Depth", + "reference-ts-id": { + "office-id": "SWT", + "name": "SWAN2.Elev.Inst.1Hour.0.DSS-Obs" + } + }, + "time-series-list": [], + "first-date": 1594296000000, + "last-date": 1752062400000 + } +] \ No newline at end of file diff --git a/tests/resources/timeseries_profiles.json b/tests/resources/timeseries_profiles.json new file mode 100644 index 00000000..68a42231 --- /dev/null +++ b/tests/resources/timeseries_profiles.json @@ -0,0 +1,34 @@ +[ + { + "description": "Description", + "parameter-list": [ + "Temp-Water", + "Depth" + ], + "location-id": { + "office-id": "SWT", + "name": "SWAN" + }, + "reference-ts-id": { + "office-id": "SWT", + "name": "SWAN.Elev.Inst.1Hour.0.DSS-Obs" + }, + "key-parameter": "Depth" + }, + { + "description": "Description 2", + "parameter-list": [ + "Temp", + "Length" + ], + "location-id": { + "office-id": "SWT", + "name": "SWAN2" + }, + "reference-ts-id": { + "office-id": "SWT", + "name": "SWAN2.Elev.Inst.1Hour.0.DSS-Obs" + }, + "key-parameter": "Length" + } +] \ No newline at end of file diff --git a/tests/resources/timeseries_profiles_indexed.json b/tests/resources/timeseries_profiles_indexed.json new file mode 100644 index 00000000..e0b6c0fa --- /dev/null +++ b/tests/resources/timeseries_profiles_indexed.json @@ -0,0 +1,58 @@ +[ + { + "type": "indexed-timeseries-profile-parser", + "location-id": { + "office-id": "SWT", + "name": "location" + }, + "key-parameter": "Depth", + "record-delimiter": "\n", + "time-format": "MM/DD/YYYY,HH24:MI:SS", + "time-zone": "UTC", + "parameter-info-list": [ + { + "type": "indexed-parameter-info", + "parameter": "Depth", + "unit": "m", + "index": 3 + }, + { + "type": "indexed-parameter-info", + "parameter": "Temp-Water", + "unit": "F", + "index": 5 + } + ], + "time-in-two-fields": false, + "field-delimiter": ",", + "time-field": 1 + }, + { + "type": "indexed-timeseries-profile-parser", + "location-id": { + "office-id": "SWT", + "name": "location" + }, + "key-parameter": "Length", + "record-delimiter": "\n", + "time-format": "MM/DD/YYYY,HH24:MI:SS", + "time-zone": "UTC", + "parameter-info-list": [ + { + "type": "indexed-parameter-info", + "parameter": "Length", + "unit": "m", + "index": 4 + }, + { + "type": "indexed-parameter-info", + "parameter": "Temp", + "unit": "C", + "index": 6 + } + ], + "time-in-two-fields": false, + "field-delimiter": ",", + "time-field": 1 + } +] \ No newline at end of file diff --git a/tests/timeseries/timeseries_profile_instance_test.py b/tests/timeseries/timeseries_profile_instance_test.py new file mode 100644 index 00000000..9f7cf2de --- /dev/null +++ b/tests/timeseries/timeseries_profile_instance_test.py @@ -0,0 +1,120 @@ +# Copyright (c) 2024 +# United States Army Corps of Engineers - Hydrologic Engineering Center (USACE/HEC) +# All Rights Reserved. USACE PROPRIETARY/CONFIDENTIAL. +# Source may not be released without written approval from HEC + +import urllib.parse +from datetime import datetime +from pathlib import Path + +import pytest +import pytz + +import cwms.api +import cwms.timeseries.timeseries_profile_instance as timeseries +from tests._test_utils import read_resource_file + +_MOCK_ROOT = "https://mockwebserver.cwms.gov" +_TSP_INST_JSON = read_resource_file("timeseries_profile_instance.json") +_TSP_INST_ARRAY_JSON = read_resource_file("timeseries_profile_instances.json") +current_path = Path(__file__).resolve().parent.parent.parent +resource_path = current_path / "tests" / "resources" / "timeseries_profile_data.txt" +with open(resource_path, "r") as file: + _TSP_PROFILE_DATA = file.read().strip("\n") + +tz = pytz.timezone("UTC") + + +@pytest.fixture(autouse=True) +def init_session(): + cwms.api.init_session(api_root=_MOCK_ROOT) + + +def test_get_timeseries_profile_instance(requests_mock): + requests_mock.get( + f"{_MOCK_ROOT}" + "/timeseries/instance/TEST.Text.Inst.1Hour.0.MockTest?office=SWT&" + "parameter-id=Temp-Water&" + "version=Raw&" + "unit=C", + json=_TSP_INST_JSON, + ) + + timeseries_id = "TEST.Text.Inst.1Hour.0.MockTest" + parameter_id = "Temp-Water" + version = "Raw" + unit = "C" + office_id = "SWT" + timezone = "UTC" + version_date = tz.localize(datetime(2014, 8, 16, 4, 55, 0)) + start = tz.localize(datetime(2015, 3, 3, 6, 45, 0)) + end = tz.localize(datetime(2015, 3, 3, 7, 15, 0)) + + data = timeseries.get_timeseries_profile_instance( + office_id, + timeseries_id, + parameter_id, + version, + timezone, + unit, + version_date, + start, + end, + ) + + assert data.json == _TSP_INST_JSON + + +def test_store_timeseries_profile_instance(requests_mock): + data = urllib.parse.quote_plus(_TSP_PROFILE_DATA) + requests_mock.post( + f"{_MOCK_ROOT}/timeseries/instance" + f"?profile-data={data}&" + "version=Raw&override-protection=False" + "&version-date=2020-01-01T13%3A30%3A00%2B00%3A00" + ) + + version = "Raw" + version_date = tz.localize(datetime(2020, 1, 1, 13, 30, 0)) + + timeseries.store_timeseries_profile_instance( + _TSP_PROFILE_DATA, version, version_date, None, False + ) + + assert requests_mock.called + assert requests_mock.call_count == 1 + + +def test_delete_timeseries_profile_instance(requests_mock): + requests_mock.delete( + f"{_MOCK_ROOT}" + "/timeseries/instance/TEST.Text.Inst.1Hour.0.MockTest?office=SWT&" + "parameter-id=Length", + json=_TSP_INST_JSON, + ) + + timeseries_id = "TEST.Text.Inst.1Hour.0.MockTest" + office_id = "SWT" + parameter_id = "Length" + version = "Raw" + timezone = "UTC" + version_date = tz.localize(datetime(2010, 6, 4, 12, 0, 0)) + date = tz.localize(datetime(2010, 6, 4, 14, 0, 0)) + + timeseries.delete_timeseries_profile_instance( + office_id, timeseries_id, parameter_id, version, timezone, version_date, date + ) + + assert requests_mock.called + assert requests_mock.call_count == 1 + + +def test_get_all_timeseries_profile_instance(requests_mock): + requests_mock.get( + f"{_MOCK_ROOT}" "/timeseries/instance", + json=_TSP_INST_ARRAY_JSON, + ) + + data = timeseries.get_timeseries_profile_instances("*", "*", "*", "*") + + assert data.json == _TSP_INST_ARRAY_JSON diff --git a/tests/timeseries/timeseries_profile_parser_test.py b/tests/timeseries/timeseries_profile_parser_test.py new file mode 100644 index 00000000..ccfdc43e --- /dev/null +++ b/tests/timeseries/timeseries_profile_parser_test.py @@ -0,0 +1,76 @@ +# Copyright (c) 2024 +# United States Army Corps of Engineers - Hydrologic Engineering Center (USACE/HEC) +# All Rights Reserved. USACE PROPRIETARY/CONFIDENTIAL. +# Source may not be released without written approval from HEC + +import pytest + +import cwms.api +import cwms.timeseries.timeseries_profile_parser as timeseries +from tests._test_utils import read_resource_file + +_MOCK_ROOT = "https://mockwebserver.cwms.gov" +_TSP_PARSER_JSON = read_resource_file("timeseries_profile_indexed.json") +_TSP_PARSER_ARRAY_JSON = read_resource_file("timeseries_profiles_indexed.json") + + +@pytest.fixture(autouse=True) +def init_session(): + cwms.api.init_session(api_root=_MOCK_ROOT) + + +def test_get_timeseries_profile_parser(requests_mock): + requests_mock.get( + f"{_MOCK_ROOT}" + "/timeseries/parser/Temp?office=SWT&" + "location-id=SWT.TEST.Text.Inst.1Hour.0.MockTest", + json=_TSP_PARSER_JSON, + ) + + location_id = "SWT.TEST.Text.Inst.1Hour.0.MockTest" + office_id = "SWT" + parameter_id = "Temp" + + data = timeseries.get_timeseries_profile_parser( + office_id, location_id, parameter_id + ) + + assert data.json == _TSP_PARSER_JSON + + +def test_store_timeseries_profile_parser(requests_mock): + requests_mock.post(f"{_MOCK_ROOT}/timeseries/parser?" "fail-if-exists=False") + + data = _TSP_PARSER_JSON + timeseries.store_timeseries_profile_parser(data, False) + + assert requests_mock.called + assert requests_mock.call_count == 1 + + +def test_delete_timeseries_profile_parser(requests_mock): + requests_mock.delete( + f"{_MOCK_ROOT}" + "/timeseries/parser/Length?location-id=TEST.Text.Inst.1Hour.0.MockTest&office=SWT", + json=_TSP_PARSER_JSON, + ) + + parameter_id = "Length" + location_id = "TEST.Text.Inst.1Hour.0.MockTest" + office_id = "SWT" + + timeseries.delete_timeseries_profile_parser(office_id, location_id, parameter_id) + + assert requests_mock.called + assert requests_mock.call_count == 1 + + +def test_get_all_timeseries_profile_parser(requests_mock): + requests_mock.get( + f"{_MOCK_ROOT}" "/timeseries/parser", + json=_TSP_PARSER_ARRAY_JSON, + ) + + data = timeseries.get_timeseries_profile_parsers("*", "*", "*") + + assert data.json == _TSP_PARSER_ARRAY_JSON diff --git a/tests/timeseries/timeseries_profile_test.py b/tests/timeseries/timeseries_profile_test.py new file mode 100644 index 00000000..661e38d7 --- /dev/null +++ b/tests/timeseries/timeseries_profile_test.py @@ -0,0 +1,73 @@ +# Copyright (c) 2024 +# United States Army Corps of Engineers - Hydrologic Engineering Center (USACE/HEC) +# All Rights Reserved. USACE PROPRIETARY/CONFIDENTIAL. +# Source may not be released without written approval from HEC + +import pytest + +import cwms.api +import cwms.timeseries.timeseries_profile as timeseries +from tests._test_utils import read_resource_file + +_MOCK_ROOT = "https://mockwebserver.cwms.gov" +_TSP_JSON = read_resource_file("timeseries_profile.json") +_TSP_ARRAY_JSON = read_resource_file("timeseries_profiles.json") + + +@pytest.fixture(autouse=True) +def init_session(): + cwms.api.init_session(api_root=_MOCK_ROOT) + + +def test_get_timeseries_profile(requests_mock): + requests_mock.get( + f"{_MOCK_ROOT}" + "/timeseries/profile/Depth?office=SWT&" + "location-id=SWT.TEST.Text.Inst.1Hour.0.MockTest", + json=_TSP_JSON, + ) + + parameter_id = "Depth" + office_id = "SWT" + location_id = "SWT.TEST.Text.Inst.1Hour.0.MockTest" + + data = timeseries.get_timeseries_profile(office_id, location_id, parameter_id) + assert data.json == _TSP_JSON + + +def test_store_timeseries_profile(requests_mock): + requests_mock.post(f"{_MOCK_ROOT}/timeseries/profile?fail-if-exists=False") + + data = _TSP_JSON + timeseries.store_timeseries_profile(data, False) + + assert requests_mock.called + assert requests_mock.call_count == 1 + + +def test_get_all_timeseries_profile(requests_mock): + requests_mock.get( + f"{_MOCK_ROOT}/timeseries/profile", + json=_TSP_ARRAY_JSON, + ) + + data = timeseries.get_timeseries_profiles("*", "*", "*") + + assert data.json == _TSP_ARRAY_JSON + + +def test_delete_timeseries_profile(requests_mock): + requests_mock.delete( + f"{_MOCK_ROOT}" + "/timeseries/profile/Depth?location-id=TEST.Text.Inst.1Hour.0.MockTest&office=SWT", + json=_TSP_JSON, + ) + + location_id = "TEST.Text.Inst.1Hour.0.MockTest" + parameter_id = "Depth" + office_id = "SWT" + + timeseries.delete_timeseries_profile(office_id, parameter_id, location_id) + + assert requests_mock.called + assert requests_mock.call_count == 1 From f7ac6632313c28bf681bb470b7fb0ac7617efc92 Mon Sep 17 00:00:00 2001 From: zack-rma Date: Tue, 26 Nov 2024 13:11:12 -0800 Subject: [PATCH 2/6] Updated api endpoint calls to match timeseries profile controller changes --- cwms/timeseries/timeseries_profile.py | 9 +--- .../timeseries/timeseries_profile_instance.py | 50 +++++++++---------- cwms/timeseries/timeseries_profile_parser.py | 12 ++--- .../timeseries_profile_instance_test.py | 20 +++----- .../timeseries_profile_parser_test.py | 15 +++--- tests/timeseries/timeseries_profile_test.py | 11 ++-- 6 files changed, 48 insertions(+), 69 deletions(-) diff --git a/cwms/timeseries/timeseries_profile.py b/cwms/timeseries/timeseries_profile.py index 7461f698..cc14d303 100644 --- a/cwms/timeseries/timeseries_profile.py +++ b/cwms/timeseries/timeseries_profile.py @@ -3,11 +3,8 @@ # All Rights Reserved. USACE PROPRIETARY/CONFIDENTIAL. # Source may not be released without written approval from HEC -from datetime import datetime from typing import Optional -import pandas as pd - import cwms.api as api from cwms.cwms_types import Data @@ -30,10 +27,9 @@ def get_timeseries_profile(office_id: str, location_id: str, parameter_id: str) cwms data type """ - endpoint = f"timeseries/profile/{parameter_id}" + endpoint = f"timeseries/profile/{location_id}/{parameter_id}" params = { "office": office_id, - "location-id": location_id, } response = api.get(endpoint, params) @@ -100,10 +96,9 @@ def delete_timeseries_profile( None """ - endpoint = f"timeseries/profile/{parameter_id}" + endpoint = f"timeseries/profile/{location_id}/{parameter_id}" params = { "office": office_id, - "location-id": location_id, } return api.delete(endpoint, params) diff --git a/cwms/timeseries/timeseries_profile_instance.py b/cwms/timeseries/timeseries_profile_instance.py index b395110b..fd522455 100644 --- a/cwms/timeseries/timeseries_profile_instance.py +++ b/cwms/timeseries/timeseries_profile_instance.py @@ -12,7 +12,7 @@ def get_timeseries_profile_instance( office_id: str, - timeseries_id: str, + location_id: str, parameter_id: str, version: str, timezone: Optional[str], @@ -21,7 +21,7 @@ def get_timeseries_profile_instance( start: Optional[datetime], end: Optional[datetime], page: Optional[str] = None, - page_size: Optional[int] = 1000, + page_size: Optional[int] = 500, start_inclusive: Optional[bool] = True, end_inclusive: Optional[bool] = True, previous: Optional[bool] = False, @@ -35,8 +35,8 @@ def get_timeseries_profile_instance( ---------- office_id: string The owning office of the timeseries profile instance - timeseries_id: string - The name identifier associated with the timeseries profile instance + location_id: string + The location associated with the timeseries profile instance parameter_id: string The name of the key parameter associated with the timeseries profile instance version: str @@ -74,16 +74,14 @@ def get_timeseries_profile_instance( cwms data type """ - endpoint = f"timeseries/instance/{timeseries_id}" + endpoint = f"timeseries/profile-instance/{location_id}/{parameter_id}/{version}" params = { "office": office_id, - "parameter-id": parameter_id, - "version": version, "timezone": timezone, "version-date": version_date.isoformat() if version_date else None, "unit": unit, - "start-inclusive": start_inclusive, - "end-inclusive": end_inclusive, + "start-time-inclusive": start_inclusive, + "end-time-inclusive": end_inclusive, "previous": previous, "next": next, "max-version": max_version, @@ -126,7 +124,7 @@ def get_timeseries_profile_instances( cwms data type """ - endpoint = "timeseries/instance" + endpoint = "timeseries/profile-instance" params = { "office-mask": office_mask, "location-mask": location_mask, @@ -140,12 +138,12 @@ def get_timeseries_profile_instances( def delete_timeseries_profile_instance( office_id: str, - timeseries_id: str, + location_id: str, parameter_id: str, version: str, + version_date: datetime, + first_date: datetime, timezone: Optional[str], - version_date: Optional[datetime], - date: Optional[datetime], override_protection: Optional[bool] = True, ) -> None: """ @@ -155,18 +153,18 @@ def delete_timeseries_profile_instance( ---------- office_id: string The owning office of the timeseries profile instance - timeseries_id: string + location_id: string The name identifier for the timeseries profile instance to delete parameter_id: string The name of the key parameter associated with the timeseries profile instance version: string The version of the timeseries profile instance - timezone: string + version_date: datetime + The timestamp of the timeseries profile instance version + first_date: datetime + The first date of the timeseries profile instance + timezone: string, optional The timezone used for the timestamps associated with the timeseries profile instance - version_date: datetime, optional - The timestamp of the timeseries profile instance version. Default is current date and time. - date: datetime, optional - The first date of the timeseries profile instance. Default is current date and time override_protection: boolean, optional Whether to enable override protection for the timeseries profile instance. Default is `True`. @@ -175,14 +173,12 @@ def delete_timeseries_profile_instance( None """ - endpoint = f"timeseries/instance/{timeseries_id}" + endpoint = f"timeseries/profile-instance/{location_id}/{parameter_id}/{version}" params = { "office": office_id, - "parameter-id": parameter_id, - "version": version, "timezone": timezone, "version-date": version_date.isoformat() if version_date else None, - "date": date.isoformat() if date else None, + "date": first_date.isoformat() if first_date else None, "override-protection": override_protection, } @@ -192,7 +188,7 @@ def delete_timeseries_profile_instance( def store_timeseries_profile_instance( profile_data: str, version: str, - version_date: Optional[datetime] = None, + version_date: datetime, store_rule: Optional[str] = None, override_protection: Optional[bool] = False, ) -> None: @@ -207,8 +203,8 @@ def store_timeseries_profile_instance( The method of storing the timeseries profile instance. Default is `REPLACE_ALL`. version: string The version of the timeseries profile instance. - version_date: datetime, optional - The version date of the timeseries profile instance. Default is the current date and time. + version_date: datetime + The version date of the timeseries profile instance. override_protection: boolean, optional Whether to enable override protection for the timeseries profile instance. Default is `False`. @@ -217,7 +213,7 @@ def store_timeseries_profile_instance( None """ - endpoint = "timeseries/instance" + endpoint = "timeseries/profile-instance" params = { "profile-data": profile_data, "method": store_rule, diff --git a/cwms/timeseries/timeseries_profile_parser.py b/cwms/timeseries/timeseries_profile_parser.py index eb9b104e..43930399 100644 --- a/cwms/timeseries/timeseries_profile_parser.py +++ b/cwms/timeseries/timeseries_profile_parser.py @@ -3,7 +3,6 @@ # All Rights Reserved. USACE PROPRIETARY/CONFIDENTIAL. # Source may not be released without written approval from HEC -from datetime import datetime from typing import Optional import cwms.api as api @@ -30,10 +29,9 @@ def get_timeseries_profile_parser( cwms data type """ - endpoint = f"timeseries/parser/{parameter_id}" + endpoint = f"timeseries/profile-parser/{location_id}/{parameter_id}" params = { "office": office_id, - "location-id": location_id, } response = api.get(endpoint, params) @@ -65,7 +63,7 @@ def get_timeseries_profile_parsers( cwms data type """ - endpoint = "timeseries/parser" + endpoint = "timeseries/profile-parser" params = { "office-mask": office_mask, "location-mask": location_mask, @@ -96,8 +94,8 @@ def delete_timeseries_profile_parser( None """ - endpoint = f"timeseries/parser/{parameter_id}" - params = {"office": office_id, "location-id": location_id} + endpoint = f"timeseries/profile-parser/{location_id}/{parameter_id}" + params = {"office": office_id} return api.delete(endpoint, params) @@ -184,7 +182,7 @@ def store_timeseries_profile_parser( None """ - endpoint = "timeseries/parser" + endpoint = "timeseries/profile-parser" params = { "fail-if-exists": fail_if_exists, } diff --git a/tests/timeseries/timeseries_profile_instance_test.py b/tests/timeseries/timeseries_profile_instance_test.py index 9f7cf2de..98e57389 100644 --- a/tests/timeseries/timeseries_profile_instance_test.py +++ b/tests/timeseries/timeseries_profile_instance_test.py @@ -33,14 +33,12 @@ def init_session(): def test_get_timeseries_profile_instance(requests_mock): requests_mock.get( f"{_MOCK_ROOT}" - "/timeseries/instance/TEST.Text.Inst.1Hour.0.MockTest?office=SWT&" - "parameter-id=Temp-Water&" - "version=Raw&" + "/timeseries/profile-instance/SWAN/Temp-Water/Raw?office=SWT&" "unit=C", json=_TSP_INST_JSON, ) - timeseries_id = "TEST.Text.Inst.1Hour.0.MockTest" + location_id = "SWAN" parameter_id = "Temp-Water" version = "Raw" unit = "C" @@ -52,7 +50,7 @@ def test_get_timeseries_profile_instance(requests_mock): data = timeseries.get_timeseries_profile_instance( office_id, - timeseries_id, + location_id, parameter_id, version, timezone, @@ -68,7 +66,7 @@ def test_get_timeseries_profile_instance(requests_mock): def test_store_timeseries_profile_instance(requests_mock): data = urllib.parse.quote_plus(_TSP_PROFILE_DATA) requests_mock.post( - f"{_MOCK_ROOT}/timeseries/instance" + f"{_MOCK_ROOT}/timeseries/profile-instance" f"?profile-data={data}&" "version=Raw&override-protection=False" "&version-date=2020-01-01T13%3A30%3A00%2B00%3A00" @@ -87,13 +85,11 @@ def test_store_timeseries_profile_instance(requests_mock): def test_delete_timeseries_profile_instance(requests_mock): requests_mock.delete( - f"{_MOCK_ROOT}" - "/timeseries/instance/TEST.Text.Inst.1Hour.0.MockTest?office=SWT&" - "parameter-id=Length", + f"{_MOCK_ROOT}" "/timeseries/profile-instance/SWAN/Length/Raw?office=SWT", json=_TSP_INST_JSON, ) - timeseries_id = "TEST.Text.Inst.1Hour.0.MockTest" + location_id = "SWAN" office_id = "SWT" parameter_id = "Length" version = "Raw" @@ -102,7 +98,7 @@ def test_delete_timeseries_profile_instance(requests_mock): date = tz.localize(datetime(2010, 6, 4, 14, 0, 0)) timeseries.delete_timeseries_profile_instance( - office_id, timeseries_id, parameter_id, version, timezone, version_date, date + office_id, location_id, parameter_id, version, version_date, date, timezone ) assert requests_mock.called @@ -111,7 +107,7 @@ def test_delete_timeseries_profile_instance(requests_mock): def test_get_all_timeseries_profile_instance(requests_mock): requests_mock.get( - f"{_MOCK_ROOT}" "/timeseries/instance", + f"{_MOCK_ROOT}/timeseries/profile-instance", json=_TSP_INST_ARRAY_JSON, ) diff --git a/tests/timeseries/timeseries_profile_parser_test.py b/tests/timeseries/timeseries_profile_parser_test.py index ccfdc43e..6047066c 100644 --- a/tests/timeseries/timeseries_profile_parser_test.py +++ b/tests/timeseries/timeseries_profile_parser_test.py @@ -21,13 +21,11 @@ def init_session(): def test_get_timeseries_profile_parser(requests_mock): requests_mock.get( - f"{_MOCK_ROOT}" - "/timeseries/parser/Temp?office=SWT&" - "location-id=SWT.TEST.Text.Inst.1Hour.0.MockTest", + f"{_MOCK_ROOT}" "/timeseries/profile-parser/SWAN/Temp?office=SWT", json=_TSP_PARSER_JSON, ) - location_id = "SWT.TEST.Text.Inst.1Hour.0.MockTest" + location_id = "SWAN" office_id = "SWT" parameter_id = "Temp" @@ -39,7 +37,7 @@ def test_get_timeseries_profile_parser(requests_mock): def test_store_timeseries_profile_parser(requests_mock): - requests_mock.post(f"{_MOCK_ROOT}/timeseries/parser?" "fail-if-exists=False") + requests_mock.post(f"{_MOCK_ROOT}/timeseries/profile-parser?fail-if-exists=False") data = _TSP_PARSER_JSON timeseries.store_timeseries_profile_parser(data, False) @@ -50,13 +48,12 @@ def test_store_timeseries_profile_parser(requests_mock): def test_delete_timeseries_profile_parser(requests_mock): requests_mock.delete( - f"{_MOCK_ROOT}" - "/timeseries/parser/Length?location-id=TEST.Text.Inst.1Hour.0.MockTest&office=SWT", + f"{_MOCK_ROOT}" "/timeseries/profile-parser/SWAN/Length?office=SWT", json=_TSP_PARSER_JSON, ) parameter_id = "Length" - location_id = "TEST.Text.Inst.1Hour.0.MockTest" + location_id = "SWAN" office_id = "SWT" timeseries.delete_timeseries_profile_parser(office_id, location_id, parameter_id) @@ -67,7 +64,7 @@ def test_delete_timeseries_profile_parser(requests_mock): def test_get_all_timeseries_profile_parser(requests_mock): requests_mock.get( - f"{_MOCK_ROOT}" "/timeseries/parser", + f"{_MOCK_ROOT}" "/timeseries/profile-parser", json=_TSP_PARSER_ARRAY_JSON, ) diff --git a/tests/timeseries/timeseries_profile_test.py b/tests/timeseries/timeseries_profile_test.py index 661e38d7..cabba885 100644 --- a/tests/timeseries/timeseries_profile_test.py +++ b/tests/timeseries/timeseries_profile_test.py @@ -21,15 +21,13 @@ def init_session(): def test_get_timeseries_profile(requests_mock): requests_mock.get( - f"{_MOCK_ROOT}" - "/timeseries/profile/Depth?office=SWT&" - "location-id=SWT.TEST.Text.Inst.1Hour.0.MockTest", + f"{_MOCK_ROOT}" "/timeseries/profile/SWAN/Depth?office=SWT", json=_TSP_JSON, ) parameter_id = "Depth" office_id = "SWT" - location_id = "SWT.TEST.Text.Inst.1Hour.0.MockTest" + location_id = "SWAN" data = timeseries.get_timeseries_profile(office_id, location_id, parameter_id) assert data.json == _TSP_JSON @@ -58,12 +56,11 @@ def test_get_all_timeseries_profile(requests_mock): def test_delete_timeseries_profile(requests_mock): requests_mock.delete( - f"{_MOCK_ROOT}" - "/timeseries/profile/Depth?location-id=TEST.Text.Inst.1Hour.0.MockTest&office=SWT", + f"{_MOCK_ROOT}" "/timeseries/profile/SWAN/Depth?office=SWT", json=_TSP_JSON, ) - location_id = "TEST.Text.Inst.1Hour.0.MockTest" + location_id = "SWAN" parameter_id = "Depth" office_id = "SWT" From 1b2d21bd74870536e9ba8645a53ba23927b9f764 Mon Sep 17 00:00:00 2001 From: zack-rma Date: Mon, 2 Dec 2024 11:03:44 -0800 Subject: [PATCH 3/6] Removed timezone parameter from ts profile instance getOne --- cwms/timeseries/timeseries_profile_instance.py | 4 ---- tests/timeseries/timeseries_profile_instance_test.py | 2 -- 2 files changed, 6 deletions(-) diff --git a/cwms/timeseries/timeseries_profile_instance.py b/cwms/timeseries/timeseries_profile_instance.py index fd522455..5d652995 100644 --- a/cwms/timeseries/timeseries_profile_instance.py +++ b/cwms/timeseries/timeseries_profile_instance.py @@ -15,7 +15,6 @@ def get_timeseries_profile_instance( location_id: str, parameter_id: str, version: str, - timezone: Optional[str], unit: str, version_date: Optional[datetime], start: Optional[datetime], @@ -41,8 +40,6 @@ def get_timeseries_profile_instance( The name of the key parameter associated with the timeseries profile instance version: str The version of the timeseries profile instance - timezone: str - The timezone of the timeseries profile instance. The default value is `UTC`. unit: str The requested units to use for the key parameter values of the timeseries profile instance version_date: datetime, optional @@ -77,7 +74,6 @@ def get_timeseries_profile_instance( endpoint = f"timeseries/profile-instance/{location_id}/{parameter_id}/{version}" params = { "office": office_id, - "timezone": timezone, "version-date": version_date.isoformat() if version_date else None, "unit": unit, "start-time-inclusive": start_inclusive, diff --git a/tests/timeseries/timeseries_profile_instance_test.py b/tests/timeseries/timeseries_profile_instance_test.py index 98e57389..1b288117 100644 --- a/tests/timeseries/timeseries_profile_instance_test.py +++ b/tests/timeseries/timeseries_profile_instance_test.py @@ -43,7 +43,6 @@ def test_get_timeseries_profile_instance(requests_mock): version = "Raw" unit = "C" office_id = "SWT" - timezone = "UTC" version_date = tz.localize(datetime(2014, 8, 16, 4, 55, 0)) start = tz.localize(datetime(2015, 3, 3, 6, 45, 0)) end = tz.localize(datetime(2015, 3, 3, 7, 15, 0)) @@ -53,7 +52,6 @@ def test_get_timeseries_profile_instance(requests_mock): location_id, parameter_id, version, - timezone, unit, version_date, start, From 03ec2a7c9c202c1fcd868431cad22e6c6bfdd245 Mon Sep 17 00:00:00 2001 From: zack-rma Date: Mon, 2 Dec 2024 11:29:13 -0800 Subject: [PATCH 4/6] Removed timezone parameter from ts profile instance delete --- cwms/timeseries/timeseries_profile_instance.py | 4 ---- tests/timeseries/timeseries_profile_instance_test.py | 3 +-- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/cwms/timeseries/timeseries_profile_instance.py b/cwms/timeseries/timeseries_profile_instance.py index 5d652995..1aa67b02 100644 --- a/cwms/timeseries/timeseries_profile_instance.py +++ b/cwms/timeseries/timeseries_profile_instance.py @@ -139,7 +139,6 @@ def delete_timeseries_profile_instance( version: str, version_date: datetime, first_date: datetime, - timezone: Optional[str], override_protection: Optional[bool] = True, ) -> None: """ @@ -159,8 +158,6 @@ def delete_timeseries_profile_instance( The timestamp of the timeseries profile instance version first_date: datetime The first date of the timeseries profile instance - timezone: string, optional - The timezone used for the timestamps associated with the timeseries profile instance override_protection: boolean, optional Whether to enable override protection for the timeseries profile instance. Default is `True`. @@ -172,7 +169,6 @@ def delete_timeseries_profile_instance( endpoint = f"timeseries/profile-instance/{location_id}/{parameter_id}/{version}" params = { "office": office_id, - "timezone": timezone, "version-date": version_date.isoformat() if version_date else None, "date": first_date.isoformat() if first_date else None, "override-protection": override_protection, diff --git a/tests/timeseries/timeseries_profile_instance_test.py b/tests/timeseries/timeseries_profile_instance_test.py index 1b288117..baf68c48 100644 --- a/tests/timeseries/timeseries_profile_instance_test.py +++ b/tests/timeseries/timeseries_profile_instance_test.py @@ -91,12 +91,11 @@ def test_delete_timeseries_profile_instance(requests_mock): office_id = "SWT" parameter_id = "Length" version = "Raw" - timezone = "UTC" version_date = tz.localize(datetime(2010, 6, 4, 12, 0, 0)) date = tz.localize(datetime(2010, 6, 4, 14, 0, 0)) timeseries.delete_timeseries_profile_instance( - office_id, location_id, parameter_id, version, version_date, date, timezone + office_id, location_id, parameter_id, version, version_date, date ) assert requests_mock.called From 6ddec3aa1c4429ccb04da0f37dd0fe689b50cc43 Mon Sep 17 00:00:00 2001 From: zack-rma Date: Wed, 18 Dec 2024 11:15:42 -0800 Subject: [PATCH 5/6] Updated README and pydocs for TimeSeries Profiles with CDA compatability warning --- README.md | 6 ++++++ cwms/timeseries/timeseries_profile.py | 20 +++++++++++++++++++ .../timeseries/timeseries_profile_instance.py | 20 +++++++++++++++++++ cwms/timeseries/timeseries_profile_parser.py | 20 +++++++++++++++++++ 4 files changed, 66 insertions(+) diff --git a/README.md b/README.md index 7e3dd757..4777b995 100644 --- a/README.md +++ b/README.md @@ -71,3 +71,9 @@ print(json) ['2024-04-23T10:00:00', 86.57999999999997, 3]], 'version-date': None} ``` + +## TimeSeries Profile API Compatibility Warning + +Currently, the TimeSeries Profile API may not be fully supported +until a new version of cwms-data-access is released with the updated +endpoint implementation. A new version is expected January 2025. diff --git a/cwms/timeseries/timeseries_profile.py b/cwms/timeseries/timeseries_profile.py index cc14d303..cb4a12bd 100644 --- a/cwms/timeseries/timeseries_profile.py +++ b/cwms/timeseries/timeseries_profile.py @@ -13,6 +13,11 @@ def get_timeseries_profile(office_id: str, location_id: str, parameter_id: str) """ Retrieves a timeseries profile. + Compatibility Warning: + Currently, the TimeSeries Profile API may not be fully supported + until a new version of cwms-data-access is released with the updated + endpoint implementation. A new version is expected January 2025. + Parameters ---------- office_id: string @@ -46,6 +51,11 @@ def get_timeseries_profiles( """ Retrieves all timeseries profiles that fit the provided masks. Does not include time series values. + Compatibility Warning: + Currently, the TimeSeries Profile API may not be fully supported + until a new version of cwms-data-access is released with the updated + endpoint implementation. A new version is expected January 2025. + Parameters ---------- office_mask: string @@ -82,6 +92,11 @@ def delete_timeseries_profile( """ Deletes a specified timeseries profile + Compatibility Warning: + Currently, the TimeSeries Profile API may not be fully supported + until a new version of cwms-data-access is released with the updated + endpoint implementation. A new version is expected January 2025. + Parameters ---------- office_id: string @@ -108,6 +123,11 @@ def store_timeseries_profile(data: str, fail_if_exists: Optional[bool] = True) - """ Stores a new timeseries profile + Compatibility Warning: + Currently, the TimeSeries Profile API may not be fully supported + until a new version of cwms-data-access is released with the updated + endpoint implementation. A new version is expected January 2025. + Parameters ---------- data: string diff --git a/cwms/timeseries/timeseries_profile_instance.py b/cwms/timeseries/timeseries_profile_instance.py index 1aa67b02..3783b0e3 100644 --- a/cwms/timeseries/timeseries_profile_instance.py +++ b/cwms/timeseries/timeseries_profile_instance.py @@ -30,6 +30,11 @@ def get_timeseries_profile_instance( """ Returns a timeseries profile instance with associated timeseries values. + Compatibility Warning: + Currently, the TimeSeries Profile API may not be fully supported + until a new version of cwms-data-access is released with the updated + endpoint implementation. A new version is expected January 2025. + Parameters ---------- office_id: string @@ -100,6 +105,11 @@ def get_timeseries_profile_instances( """ Retrieves a list of timeseries profile instances that match the specified masks. Does not return timeseries values. + Compatibility Warning: + Currently, the TimeSeries Profile API may not be fully supported + until a new version of cwms-data-access is released with the updated + endpoint implementation. A new version is expected January 2025. + Parameters ---------- office_mask: string @@ -144,6 +154,11 @@ def delete_timeseries_profile_instance( """ Deletes a timeseries profile instance. + Compatibility Warning: + Currently, the TimeSeries Profile API may not be fully supported + until a new version of cwms-data-access is released with the updated + endpoint implementation. A new version is expected January 2025. + Parameters ---------- office_id: string @@ -187,6 +202,11 @@ def store_timeseries_profile_instance( """ Stores a new timeseries profile instance. Requires timeseries profile and parser to already be stored. + Compatibility Warning: + Currently, the TimeSeries Profile API may not be fully supported + until a new version of cwms-data-access is released with the updated + endpoint implementation. A new version is expected January 2025. + Parameters ---------- profile_data: string diff --git a/cwms/timeseries/timeseries_profile_parser.py b/cwms/timeseries/timeseries_profile_parser.py index 43930399..1258e0a6 100644 --- a/cwms/timeseries/timeseries_profile_parser.py +++ b/cwms/timeseries/timeseries_profile_parser.py @@ -15,6 +15,11 @@ def get_timeseries_profile_parser( """ Returns a timeseries profile parser used to interpret timeseries data input. + Compatibility Warning: + Currently, the TimeSeries Profile API may not be fully supported + until a new version of cwms-data-access is released with the updated + endpoint implementation. A new version is expected January 2025. + Parameters ---------- office_id: string @@ -46,6 +51,11 @@ def get_timeseries_profile_parsers( """ Returns a list of timeseries profile parsers. + Compatibility Warning: + Currently, the TimeSeries Profile API may not be fully supported + until a new version of cwms-data-access is released with the updated + endpoint implementation. A new version is expected January 2025. + Parameters ---------- office_mask: string, optional @@ -80,6 +90,11 @@ def delete_timeseries_profile_parser( """ Deletes a specified timeseries profile parser + Compatibility Warning: + Currently, the TimeSeries Profile API may not be fully supported + until a new version of cwms-data-access is released with the updated + endpoint implementation. A new version is expected January 2025. + Parameters ---------- office_id: string @@ -106,6 +121,11 @@ def store_timeseries_profile_parser( """ Stores a new timeseries profile parser. + Compatibility Warning: + Currently, the TimeSeries Profile API may not be fully supported + until a new version of cwms-data-access is released with the updated + endpoint implementation. A new version is expected January 2025. + Parameters ---------- data: string From 21fcbc0be971b3fa4e8e12f31e7c61a72898217f Mon Sep 17 00:00:00 2001 From: zack-rma Date: Wed, 18 Dec 2024 11:28:21 -0800 Subject: [PATCH 6/6] Removed date reference for compatibility warning --- README.md | 2 +- cwms/timeseries/timeseries_profile.py | 8 ++++---- cwms/timeseries/timeseries_profile_instance.py | 8 ++++---- cwms/timeseries/timeseries_profile_parser.py | 8 ++++---- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 4777b995..b333c2ad 100644 --- a/README.md +++ b/README.md @@ -76,4 +76,4 @@ print(json) Currently, the TimeSeries Profile API may not be fully supported until a new version of cwms-data-access is released with the updated -endpoint implementation. A new version is expected January 2025. +endpoint implementation. diff --git a/cwms/timeseries/timeseries_profile.py b/cwms/timeseries/timeseries_profile.py index cb4a12bd..87e0ee73 100644 --- a/cwms/timeseries/timeseries_profile.py +++ b/cwms/timeseries/timeseries_profile.py @@ -16,7 +16,7 @@ def get_timeseries_profile(office_id: str, location_id: str, parameter_id: str) Compatibility Warning: Currently, the TimeSeries Profile API may not be fully supported until a new version of cwms-data-access is released with the updated - endpoint implementation. A new version is expected January 2025. + endpoint implementation. Parameters ---------- @@ -54,7 +54,7 @@ def get_timeseries_profiles( Compatibility Warning: Currently, the TimeSeries Profile API may not be fully supported until a new version of cwms-data-access is released with the updated - endpoint implementation. A new version is expected January 2025. + endpoint implementation. Parameters ---------- @@ -95,7 +95,7 @@ def delete_timeseries_profile( Compatibility Warning: Currently, the TimeSeries Profile API may not be fully supported until a new version of cwms-data-access is released with the updated - endpoint implementation. A new version is expected January 2025. + endpoint implementation. Parameters ---------- @@ -126,7 +126,7 @@ def store_timeseries_profile(data: str, fail_if_exists: Optional[bool] = True) - Compatibility Warning: Currently, the TimeSeries Profile API may not be fully supported until a new version of cwms-data-access is released with the updated - endpoint implementation. A new version is expected January 2025. + endpoint implementation. Parameters ---------- diff --git a/cwms/timeseries/timeseries_profile_instance.py b/cwms/timeseries/timeseries_profile_instance.py index 3783b0e3..d696d2ff 100644 --- a/cwms/timeseries/timeseries_profile_instance.py +++ b/cwms/timeseries/timeseries_profile_instance.py @@ -33,7 +33,7 @@ def get_timeseries_profile_instance( Compatibility Warning: Currently, the TimeSeries Profile API may not be fully supported until a new version of cwms-data-access is released with the updated - endpoint implementation. A new version is expected January 2025. + endpoint implementation. Parameters ---------- @@ -108,7 +108,7 @@ def get_timeseries_profile_instances( Compatibility Warning: Currently, the TimeSeries Profile API may not be fully supported until a new version of cwms-data-access is released with the updated - endpoint implementation. A new version is expected January 2025. + endpoint implementation. Parameters ---------- @@ -157,7 +157,7 @@ def delete_timeseries_profile_instance( Compatibility Warning: Currently, the TimeSeries Profile API may not be fully supported until a new version of cwms-data-access is released with the updated - endpoint implementation. A new version is expected January 2025. + endpoint implementation. Parameters ---------- @@ -205,7 +205,7 @@ def store_timeseries_profile_instance( Compatibility Warning: Currently, the TimeSeries Profile API may not be fully supported until a new version of cwms-data-access is released with the updated - endpoint implementation. A new version is expected January 2025. + endpoint implementation. Parameters ---------- diff --git a/cwms/timeseries/timeseries_profile_parser.py b/cwms/timeseries/timeseries_profile_parser.py index 1258e0a6..1d844378 100644 --- a/cwms/timeseries/timeseries_profile_parser.py +++ b/cwms/timeseries/timeseries_profile_parser.py @@ -18,7 +18,7 @@ def get_timeseries_profile_parser( Compatibility Warning: Currently, the TimeSeries Profile API may not be fully supported until a new version of cwms-data-access is released with the updated - endpoint implementation. A new version is expected January 2025. + endpoint implementation. Parameters ---------- @@ -54,7 +54,7 @@ def get_timeseries_profile_parsers( Compatibility Warning: Currently, the TimeSeries Profile API may not be fully supported until a new version of cwms-data-access is released with the updated - endpoint implementation. A new version is expected January 2025. + endpoint implementation. Parameters ---------- @@ -93,7 +93,7 @@ def delete_timeseries_profile_parser( Compatibility Warning: Currently, the TimeSeries Profile API may not be fully supported until a new version of cwms-data-access is released with the updated - endpoint implementation. A new version is expected January 2025. + endpoint implementation. Parameters ---------- @@ -124,7 +124,7 @@ def store_timeseries_profile_parser( Compatibility Warning: Currently, the TimeSeries Profile API may not be fully supported until a new version of cwms-data-access is released with the updated - endpoint implementation. A new version is expected January 2025. + endpoint implementation. Parameters ----------