hydroserverpy 1.2.1__py3-none-any.whl → 1.3.0b1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of hydroserverpy might be problematic. Click here for more details.
- hydroserverpy/__init__.py +1 -1
- hydroserverpy/api/{main.py → client.py} +52 -22
- hydroserverpy/api/models/__init__.py +1 -2
- hydroserverpy/api/models/base.py +180 -47
- hydroserverpy/api/models/etl/data_archive.py +31 -59
- hydroserverpy/api/models/etl/data_source.py +34 -76
- hydroserverpy/api/models/etl/orchestration_system.py +21 -36
- hydroserverpy/api/models/iam/apikey.py +57 -38
- hydroserverpy/api/models/iam/collaborator.py +55 -19
- hydroserverpy/api/models/iam/role.py +32 -4
- hydroserverpy/api/models/iam/workspace.py +58 -86
- hydroserverpy/api/models/sta/datastream.py +122 -214
- hydroserverpy/api/models/sta/observation.py +101 -0
- hydroserverpy/api/models/sta/observed_property.py +18 -53
- hydroserverpy/api/models/sta/processing_level.py +16 -31
- hydroserverpy/api/models/sta/result_qualifier.py +16 -31
- hydroserverpy/api/models/sta/sensor.py +27 -88
- hydroserverpy/api/models/sta/thing.py +48 -152
- hydroserverpy/api/models/sta/unit.py +16 -29
- hydroserverpy/api/services/__init__.py +1 -0
- hydroserverpy/api/services/base.py +92 -76
- hydroserverpy/api/services/etl/data_archive.py +42 -72
- hydroserverpy/api/services/etl/data_source.py +42 -72
- hydroserverpy/api/services/etl/orchestration_system.py +25 -33
- hydroserverpy/api/services/iam/role.py +38 -0
- hydroserverpy/api/services/iam/workspace.py +96 -99
- hydroserverpy/api/services/sta/datastream.py +151 -210
- hydroserverpy/api/services/sta/observed_property.py +31 -49
- hydroserverpy/api/services/sta/processing_level.py +30 -36
- hydroserverpy/api/services/sta/result_qualifier.py +24 -34
- hydroserverpy/api/services/sta/sensor.py +34 -48
- hydroserverpy/api/services/sta/thing.py +96 -89
- hydroserverpy/api/services/sta/unit.py +30 -34
- hydroserverpy/api/utils.py +22 -0
- hydroserverpy/etl/extractors/base.py +2 -4
- hydroserverpy/etl/loaders/hydroserver_loader.py +1 -0
- hydroserverpy/etl/timestamp_parser.py +82 -48
- hydroserverpy/etl/transformers/base.py +5 -9
- hydroserverpy/etl_csv/hydroserver_etl_csv.py +1 -1
- {hydroserverpy-1.2.1.dist-info → hydroserverpy-1.3.0b1.dist-info}/METADATA +1 -1
- hydroserverpy-1.3.0b1.dist-info/RECORD +70 -0
- hydroserverpy/api/http.py +0 -22
- hydroserverpy-1.2.1.dist-info/RECORD +0 -68
- {hydroserverpy-1.2.1.dist-info → hydroserverpy-1.3.0b1.dist-info}/WHEEL +0 -0
- {hydroserverpy-1.2.1.dist-info → hydroserverpy-1.3.0b1.dist-info}/licenses/LICENSE +0 -0
- {hydroserverpy-1.2.1.dist-info → hydroserverpy-1.3.0b1.dist-info}/top_level.txt +0 -0
- {hydroserverpy-1.2.1.dist-info → hydroserverpy-1.3.0b1.dist-info}/zip-safe +0 -0
|
@@ -1,55 +1,52 @@
|
|
|
1
1
|
from typing import Optional, Union, List, TYPE_CHECKING
|
|
2
2
|
from uuid import UUID
|
|
3
|
-
from ..base import EndpointService
|
|
4
3
|
from hydroserverpy.api.models import ResultQualifier
|
|
5
|
-
|
|
4
|
+
from hydroserverpy.api.utils import normalize_uuid
|
|
5
|
+
from ..base import HydroServerBaseService
|
|
6
6
|
|
|
7
7
|
if TYPE_CHECKING:
|
|
8
8
|
from hydroserverpy import HydroServer
|
|
9
9
|
from hydroserverpy.api.models import Workspace
|
|
10
10
|
|
|
11
11
|
|
|
12
|
-
class ResultQualifierService(
|
|
13
|
-
def __init__(self,
|
|
14
|
-
self.
|
|
15
|
-
|
|
16
|
-
self._endpoint_route = "result-qualifiers"
|
|
17
|
-
|
|
18
|
-
super().__init__(connection)
|
|
12
|
+
class ResultQualifierService(HydroServerBaseService):
|
|
13
|
+
def __init__(self, client: "HydroServer"):
|
|
14
|
+
self.model = ResultQualifier
|
|
15
|
+
super().__init__(client)
|
|
19
16
|
|
|
20
17
|
def list(
|
|
21
18
|
self,
|
|
22
|
-
|
|
19
|
+
page: int = ...,
|
|
20
|
+
page_size: int = ...,
|
|
21
|
+
order_by: List[str] = ...,
|
|
22
|
+
workspace: Optional[Union["Workspace", UUID, str]] = ...,
|
|
23
|
+
fetch_all: bool = False,
|
|
23
24
|
) -> List["ResultQualifier"]:
|
|
24
25
|
"""Fetch a collection of result qualifiers."""
|
|
25
26
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
27
|
+
return super().list(
|
|
28
|
+
page=page,
|
|
29
|
+
page_size=page_size,
|
|
30
|
+
order_by=order_by,
|
|
31
|
+
workspace_id=normalize_uuid(workspace),
|
|
32
|
+
fetch_all=fetch_all,
|
|
31
33
|
)
|
|
32
34
|
|
|
33
|
-
def get(self, uid: Union[UUID, str]) -> "ResultQualifier":
|
|
34
|
-
"""Get a result qualifier by ID."""
|
|
35
|
-
|
|
36
|
-
return super()._get(uid=str(uid))
|
|
37
|
-
|
|
38
35
|
def create(
|
|
39
36
|
self,
|
|
40
|
-
workspace: Union["Workspace", UUID, str],
|
|
41
37
|
code: str,
|
|
42
|
-
description: str,
|
|
38
|
+
description: Optional[str] = None,
|
|
39
|
+
workspace: Optional[Union["Workspace", UUID, str]] = None,
|
|
43
40
|
) -> "ResultQualifier":
|
|
44
41
|
"""Create a new result qualifier."""
|
|
45
42
|
|
|
46
|
-
|
|
43
|
+
body = {
|
|
47
44
|
"code": code,
|
|
48
45
|
"description": description,
|
|
49
|
-
"workspaceId":
|
|
46
|
+
"workspaceId": normalize_uuid(workspace),
|
|
50
47
|
}
|
|
51
48
|
|
|
52
|
-
return super().
|
|
49
|
+
return super().create(**body)
|
|
53
50
|
|
|
54
51
|
def update(
|
|
55
52
|
self,
|
|
@@ -59,16 +56,9 @@ class ResultQualifierService(EndpointService):
|
|
|
59
56
|
) -> "ResultQualifier":
|
|
60
57
|
"""Update a result qualifier."""
|
|
61
58
|
|
|
62
|
-
|
|
59
|
+
body = {
|
|
63
60
|
"code": code,
|
|
64
61
|
"description": description,
|
|
65
62
|
}
|
|
66
63
|
|
|
67
|
-
return super().
|
|
68
|
-
uid=str(uid), **{k: v for k, v in kwargs.items() if v is not ...}
|
|
69
|
-
)
|
|
70
|
-
|
|
71
|
-
def delete(self, uid: Union[UUID, str]) -> None:
|
|
72
|
-
"""Delete a result qualifier."""
|
|
73
|
-
|
|
74
|
-
super()._delete(uid=str(uid))
|
|
64
|
+
return super().update(uid=str(uid), **body)
|
|
@@ -1,57 +1,49 @@
|
|
|
1
1
|
from typing import Optional, Union, List, TYPE_CHECKING
|
|
2
2
|
from uuid import UUID
|
|
3
|
-
from ..base import SensorThingsService
|
|
4
3
|
from hydroserverpy.api.models import Sensor
|
|
5
|
-
|
|
4
|
+
from hydroserverpy.api.utils import normalize_uuid
|
|
5
|
+
from ..base import HydroServerBaseService
|
|
6
6
|
|
|
7
7
|
if TYPE_CHECKING:
|
|
8
8
|
from hydroserverpy import HydroServer
|
|
9
|
-
from hydroserverpy.api.models import Workspace
|
|
10
|
-
|
|
9
|
+
from hydroserverpy.api.models import Workspace, Thing, Datastream
|
|
11
10
|
|
|
12
|
-
class SensorService(SensorThingsService):
|
|
13
|
-
def __init__(self, connection: "HydroServer"):
|
|
14
|
-
self._model = Sensor
|
|
15
|
-
self._api_route = "api/data"
|
|
16
|
-
self._endpoint_route = "sensors"
|
|
17
|
-
self._sta_route = "api/sensorthings/v1.1/Sensors"
|
|
18
11
|
|
|
19
|
-
|
|
12
|
+
class SensorService(HydroServerBaseService):
|
|
13
|
+
def __init__(self, client: "HydroServer"):
|
|
14
|
+
self.model = Sensor
|
|
15
|
+
super().__init__(client)
|
|
20
16
|
|
|
21
17
|
def list(
|
|
22
18
|
self,
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
19
|
+
page: int = ...,
|
|
20
|
+
page_size: int = ...,
|
|
21
|
+
order_by: List[str] = ...,
|
|
22
|
+
workspace: Optional[Union["Workspace", UUID, str]] = ...,
|
|
23
|
+
thing: Optional[Union["Thing", UUID, str]] = ...,
|
|
24
|
+
datastream: Optional[Union["Datastream", UUID, str]] = ...,
|
|
25
|
+
encoding_type: str = ...,
|
|
26
|
+
manufacturer: Optional[str] = ...,
|
|
27
|
+
method_type: str = ...,
|
|
28
|
+
fetch_all: bool = False,
|
|
26
29
|
) -> List["Sensor"]:
|
|
27
30
|
"""Fetch a collection of sensors."""
|
|
28
31
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
)
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
) -> "Sensor":
|
|
41
|
-
"""Get a sensor by ID."""
|
|
42
|
-
|
|
43
|
-
return self._get(
|
|
44
|
-
uid=str(uid),
|
|
45
|
-
path=(
|
|
46
|
-
f"api/sensorthings/v1.1/Datastreams('{str(uid)}')/Sensor"
|
|
47
|
-
if fetch_by_datastream_uid
|
|
48
|
-
else None
|
|
49
|
-
),
|
|
32
|
+
return super().list(
|
|
33
|
+
page=page,
|
|
34
|
+
page_size=page_size,
|
|
35
|
+
order_by=order_by,
|
|
36
|
+
workspace_id=normalize_uuid(workspace),
|
|
37
|
+
thing_id=normalize_uuid(thing),
|
|
38
|
+
datastream_id=normalize_uuid(datastream),
|
|
39
|
+
encoding_type=encoding_type,
|
|
40
|
+
manufacturer=manufacturer,
|
|
41
|
+
method_type=method_type,
|
|
42
|
+
fetch_all=fetch_all,
|
|
50
43
|
)
|
|
51
44
|
|
|
52
45
|
def create(
|
|
53
46
|
self,
|
|
54
|
-
workspace: Union["Workspace", UUID, str],
|
|
55
47
|
name: str,
|
|
56
48
|
description: str,
|
|
57
49
|
encoding_type: str,
|
|
@@ -61,10 +53,11 @@ class SensorService(SensorThingsService):
|
|
|
61
53
|
sensor_model_link: Optional[str] = None,
|
|
62
54
|
method_link: Optional[str] = None,
|
|
63
55
|
method_code: Optional[str] = None,
|
|
56
|
+
workspace: Optional[Union["Workspace", UUID, str]] = None,
|
|
64
57
|
) -> "Sensor":
|
|
65
58
|
"""Create a new sensor."""
|
|
66
59
|
|
|
67
|
-
|
|
60
|
+
body = {
|
|
68
61
|
"name": name,
|
|
69
62
|
"description": description,
|
|
70
63
|
"encodingType": encoding_type,
|
|
@@ -74,10 +67,10 @@ class SensorService(SensorThingsService):
|
|
|
74
67
|
"modelLink": sensor_model_link,
|
|
75
68
|
"methodLink": method_link,
|
|
76
69
|
"methodCode": method_code,
|
|
77
|
-
"workspaceId":
|
|
70
|
+
"workspaceId": normalize_uuid(workspace),
|
|
78
71
|
}
|
|
79
72
|
|
|
80
|
-
return super().
|
|
73
|
+
return super().create(**body)
|
|
81
74
|
|
|
82
75
|
def update(
|
|
83
76
|
self,
|
|
@@ -94,7 +87,7 @@ class SensorService(SensorThingsService):
|
|
|
94
87
|
) -> "Sensor":
|
|
95
88
|
"""Update a sensor."""
|
|
96
89
|
|
|
97
|
-
|
|
90
|
+
body = {
|
|
98
91
|
"name": name,
|
|
99
92
|
"description": description,
|
|
100
93
|
"encodingType": encoding_type,
|
|
@@ -106,11 +99,4 @@ class SensorService(SensorThingsService):
|
|
|
106
99
|
"methodCode": method_code,
|
|
107
100
|
}
|
|
108
101
|
|
|
109
|
-
return super().
|
|
110
|
-
uid=str(uid), **{k: v for k, v in kwargs.items() if v is not ...}
|
|
111
|
-
)
|
|
112
|
-
|
|
113
|
-
def delete(self, uid: Union[UUID, str]) -> None:
|
|
114
|
-
"""Delete a sensor."""
|
|
115
|
-
|
|
116
|
-
super()._delete(uid=str(uid))
|
|
102
|
+
return super().update(uid=str(uid), **body)
|
|
@@ -1,59 +1,54 @@
|
|
|
1
1
|
import json
|
|
2
|
-
from typing import TYPE_CHECKING, Union, IO, List, Dict, Optional
|
|
2
|
+
from typing import TYPE_CHECKING, Union, IO, List, Dict, Optional, Tuple
|
|
3
3
|
from uuid import UUID
|
|
4
|
-
from ..base import SensorThingsService
|
|
5
4
|
from hydroserverpy.api.models import Thing
|
|
6
|
-
|
|
5
|
+
from hydroserverpy.api.utils import normalize_uuid
|
|
6
|
+
from ..base import HydroServerBaseService
|
|
7
7
|
|
|
8
8
|
if TYPE_CHECKING:
|
|
9
9
|
from hydroserverpy import HydroServer
|
|
10
10
|
from hydroserverpy.api.models import Workspace
|
|
11
11
|
|
|
12
12
|
|
|
13
|
-
class ThingService(
|
|
14
|
-
def __init__(self,
|
|
15
|
-
self.
|
|
16
|
-
|
|
17
|
-
self._endpoint_route = "things"
|
|
18
|
-
self._sta_route = "api/sensorthings/v1.1/Things"
|
|
19
|
-
|
|
20
|
-
super().__init__(connection)
|
|
13
|
+
class ThingService(HydroServerBaseService):
|
|
14
|
+
def __init__(self, client: "HydroServer"):
|
|
15
|
+
self.model = Thing
|
|
16
|
+
super().__init__(client)
|
|
21
17
|
|
|
22
18
|
def list(
|
|
23
19
|
self,
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
20
|
+
page: int = ...,
|
|
21
|
+
page_size: int = ...,
|
|
22
|
+
order_by: List[str] = ...,
|
|
23
|
+
workspace: Union["Workspace", UUID, str] = ...,
|
|
24
|
+
bbox: Tuple[float, float, float, float] = ...,
|
|
25
|
+
state: str = ...,
|
|
26
|
+
county: str = ...,
|
|
27
|
+
country: str = ...,
|
|
28
|
+
site_type: str = ...,
|
|
29
|
+
sampling_feature_type: str = ...,
|
|
30
|
+
sampling_feature_code: str = ...,
|
|
31
|
+
tag: Tuple[str, str] = ...,
|
|
32
|
+
is_private: bool = ...,
|
|
33
|
+
fetch_all: bool = False,
|
|
27
34
|
) -> List["Thing"]:
|
|
28
35
|
"""Fetch a collection of things."""
|
|
29
36
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
) -> "Thing":
|
|
46
|
-
"""Get a thing by ID."""
|
|
47
|
-
|
|
48
|
-
params = {"$expand": "Locations"}
|
|
49
|
-
return self._get(
|
|
50
|
-
uid=str(uid),
|
|
51
|
-
path=(
|
|
52
|
-
f"api/sensorthings/v1.1/Datastreams('{str(uid)}')/Thing"
|
|
53
|
-
if fetch_by_datastream_uid
|
|
54
|
-
else None
|
|
55
|
-
),
|
|
56
|
-
params=params,
|
|
37
|
+
return super().list(
|
|
38
|
+
page=page,
|
|
39
|
+
page_size=page_size,
|
|
40
|
+
order_by=order_by,
|
|
41
|
+
workspace_id=normalize_uuid(workspace),
|
|
42
|
+
bbox=",".join([str(i) for i in bbox]) if bbox is not ... else bbox,
|
|
43
|
+
state=state,
|
|
44
|
+
county=county,
|
|
45
|
+
country=country,
|
|
46
|
+
site_type=site_type,
|
|
47
|
+
sampling_feature_type=sampling_feature_type,
|
|
48
|
+
sampling_feature_code=sampling_feature_code,
|
|
49
|
+
tag=[f"{tag[0]}:{tag[1]}"] if tag is not ... else tag,
|
|
50
|
+
is_private=is_private,
|
|
51
|
+
fetch_all=fetch_all,
|
|
57
52
|
)
|
|
58
53
|
|
|
59
54
|
def create(
|
|
@@ -76,25 +71,27 @@ class ThingService(SensorThingsService):
|
|
|
76
71
|
) -> "Thing":
|
|
77
72
|
"""Create a new thing."""
|
|
78
73
|
|
|
79
|
-
|
|
74
|
+
body = {
|
|
80
75
|
"name": name,
|
|
81
76
|
"description": description,
|
|
82
77
|
"samplingFeatureType": sampling_feature_type,
|
|
83
78
|
"samplingFeatureCode": sampling_feature_code,
|
|
84
79
|
"siteType": site_type,
|
|
85
80
|
"isPrivate": is_private,
|
|
86
|
-
"latitude": latitude,
|
|
87
|
-
"longitude": longitude,
|
|
88
|
-
"elevation_m": elevation_m,
|
|
89
|
-
"elevationDatum": elevation_datum,
|
|
90
|
-
"state": state,
|
|
91
|
-
"county": county,
|
|
92
|
-
"country": country,
|
|
93
81
|
"dataDisclaimer": data_disclaimer,
|
|
94
|
-
"workspaceId":
|
|
82
|
+
"workspaceId": normalize_uuid(workspace),
|
|
83
|
+
"location": {
|
|
84
|
+
"latitude": latitude,
|
|
85
|
+
"longitude": longitude,
|
|
86
|
+
"elevation_m": elevation_m,
|
|
87
|
+
"elevationDatum": elevation_datum,
|
|
88
|
+
"state": state,
|
|
89
|
+
"county": county,
|
|
90
|
+
"country": country,
|
|
91
|
+
}
|
|
95
92
|
}
|
|
96
93
|
|
|
97
|
-
return super().
|
|
94
|
+
return super().create(**body)
|
|
98
95
|
|
|
99
96
|
def update(
|
|
100
97
|
self,
|
|
@@ -104,7 +101,7 @@ class ThingService(SensorThingsService):
|
|
|
104
101
|
sampling_feature_type: str = ...,
|
|
105
102
|
sampling_feature_code: str = ...,
|
|
106
103
|
site_type: str = ...,
|
|
107
|
-
is_private:
|
|
104
|
+
is_private: bool = ...,
|
|
108
105
|
latitude: float = ...,
|
|
109
106
|
longitude: float = ...,
|
|
110
107
|
elevation_m: Optional[float] = ...,
|
|
@@ -116,73 +113,83 @@ class ThingService(SensorThingsService):
|
|
|
116
113
|
) -> "Thing":
|
|
117
114
|
"""Update a thing."""
|
|
118
115
|
|
|
119
|
-
|
|
116
|
+
body = {
|
|
120
117
|
"name": name,
|
|
121
118
|
"description": description,
|
|
122
119
|
"samplingFeatureType": sampling_feature_type,
|
|
123
120
|
"samplingFeatureCode": sampling_feature_code,
|
|
124
121
|
"siteType": site_type,
|
|
125
122
|
"isPrivate": is_private,
|
|
126
|
-
"latitude": latitude,
|
|
127
|
-
"longitude": longitude,
|
|
128
|
-
"elevation_m": elevation_m,
|
|
129
|
-
"elevationDatum": elevation_datum,
|
|
130
|
-
"state": state,
|
|
131
|
-
"county": county,
|
|
132
|
-
"country": country,
|
|
133
123
|
"dataDisclaimer": data_disclaimer,
|
|
124
|
+
"location": {
|
|
125
|
+
"latitude": latitude,
|
|
126
|
+
"longitude": longitude,
|
|
127
|
+
"elevation_m": elevation_m,
|
|
128
|
+
"elevationDatum": elevation_datum,
|
|
129
|
+
"state": state,
|
|
130
|
+
"county": county,
|
|
131
|
+
"country": country,
|
|
132
|
+
}
|
|
134
133
|
}
|
|
135
134
|
|
|
136
|
-
return super().
|
|
137
|
-
uid=str(uid), **{k: v for k, v in kwargs.items() if v is not ...}
|
|
138
|
-
)
|
|
139
|
-
|
|
140
|
-
def delete(self, uid: Union[UUID, str]) -> None:
|
|
141
|
-
"""Delete a thing."""
|
|
142
|
-
|
|
143
|
-
super()._delete(uid=str(uid))
|
|
135
|
+
return super().update(uid=str(uid), **body)
|
|
144
136
|
|
|
145
137
|
def add_tag(self, uid: Union[UUID, str], key: str, value: str) -> Dict[str, str]:
|
|
146
138
|
"""Tag a HydroServer thing."""
|
|
147
139
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
140
|
+
path = f"/{self.client.base_route}/{self.model.get_route()}/{str(uid)}/tags"
|
|
141
|
+
headers = {"Content-type": "application/json"}
|
|
142
|
+
body = {
|
|
143
|
+
"key": key,
|
|
144
|
+
"value": value
|
|
145
|
+
}
|
|
146
|
+
return self.client.request(
|
|
147
|
+
"post", path, headers=headers, data=json.dumps(body, default=self.default_serializer)
|
|
152
148
|
).json()
|
|
153
149
|
|
|
154
150
|
def update_tag(self, uid: Union[UUID, str], key: str, value: str) -> Dict[str, str]:
|
|
155
151
|
"""Update the tag of a HydroServer thing."""
|
|
156
152
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
153
|
+
path = f"/{self.client.base_route}/{self.model.get_route()}/{str(uid)}/tags"
|
|
154
|
+
headers = {"Content-type": "application/json"}
|
|
155
|
+
body = {
|
|
156
|
+
"key": key,
|
|
157
|
+
"value": value
|
|
158
|
+
}
|
|
159
|
+
return self.client.request(
|
|
160
|
+
"put", path, headers=headers, data=json.dumps(body, default=self.default_serializer)
|
|
161
161
|
).json()
|
|
162
162
|
|
|
163
|
-
def delete_tag(self, uid: Union[UUID, str], key: str) -> None:
|
|
163
|
+
def delete_tag(self, uid: Union[UUID, str], key: str, value: str) -> None:
|
|
164
164
|
"""Remove a tag from a HydroServer thing."""
|
|
165
165
|
|
|
166
|
-
self.
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
166
|
+
path = f"/{self.client.base_route}/{self.model.get_route()}/{str(uid)}/tags"
|
|
167
|
+
headers = {"Content-type": "application/json"}
|
|
168
|
+
body = {
|
|
169
|
+
"key": key,
|
|
170
|
+
"value": value
|
|
171
|
+
}
|
|
172
|
+
self.client.request(
|
|
173
|
+
"delete", path, headers=headers, data=json.dumps(body, default=self.default_serializer)
|
|
170
174
|
)
|
|
171
175
|
|
|
172
176
|
def add_photo(self, uid: Union[UUID, str], file: IO[bytes]) -> Dict[str, str]:
|
|
173
177
|
"""Add a photo of a HydroServer thing."""
|
|
174
178
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
files={"file": file}
|
|
179
|
+
path = f"/{self.client.base_route}/{self.model.get_route()}/{str(uid)}/photos"
|
|
180
|
+
|
|
181
|
+
return self.client.request(
|
|
182
|
+
"post", path, files={"file": file}
|
|
179
183
|
).json()
|
|
180
184
|
|
|
181
185
|
def delete_photo(self, uid: Union[UUID, str], name: str) -> None:
|
|
182
186
|
"""Delete a photo of a HydroServer thing."""
|
|
183
187
|
|
|
184
|
-
self.
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
+
path = f"/{self.client.base_route}/{self.model.get_route()}/{str(uid)}/photos"
|
|
189
|
+
headers = {"Content-type": "application/json"}
|
|
190
|
+
body = {
|
|
191
|
+
"name": name
|
|
192
|
+
}
|
|
193
|
+
self.client.request(
|
|
194
|
+
"delete", path, headers=headers, data=json.dumps(body, default=self.default_serializer)
|
|
188
195
|
)
|
|
@@ -1,59 +1,62 @@
|
|
|
1
1
|
from typing import Optional, Union, List, TYPE_CHECKING
|
|
2
2
|
from uuid import UUID
|
|
3
|
-
from ..base import EndpointService
|
|
4
3
|
from hydroserverpy.api.models import Unit
|
|
5
|
-
|
|
4
|
+
from hydroserverpy.api.utils import normalize_uuid
|
|
5
|
+
from ..base import HydroServerBaseService
|
|
6
6
|
|
|
7
7
|
if TYPE_CHECKING:
|
|
8
8
|
from hydroserverpy import HydroServer
|
|
9
|
-
from hydroserverpy.api.models import Workspace
|
|
10
|
-
|
|
9
|
+
from hydroserverpy.api.models import Workspace, Thing, Datastream
|
|
11
10
|
|
|
12
|
-
class UnitService(EndpointService):
|
|
13
|
-
def __init__(self, connection: "HydroServer"):
|
|
14
|
-
self._model = Unit
|
|
15
|
-
self._api_route = "api/data"
|
|
16
|
-
self._endpoint_route = "units"
|
|
17
11
|
|
|
18
|
-
|
|
12
|
+
class UnitService(HydroServerBaseService):
|
|
13
|
+
def __init__(self, client: "HydroServer"):
|
|
14
|
+
self.model = Unit
|
|
15
|
+
super().__init__(client)
|
|
19
16
|
|
|
20
17
|
def list(
|
|
21
18
|
self,
|
|
22
|
-
|
|
19
|
+
page: int = ...,
|
|
20
|
+
page_size: int = ...,
|
|
21
|
+
order_by: List[str] = ...,
|
|
22
|
+
workspace: Union["Workspace", UUID, str] = ...,
|
|
23
|
+
thing: Optional[Union["Thing", UUID, str]] = ...,
|
|
24
|
+
datastream: Optional[Union["Datastream", UUID, str]] = ...,
|
|
25
|
+
unit_type: str = ...,
|
|
26
|
+
fetch_all: bool = False,
|
|
23
27
|
) -> List["Unit"]:
|
|
24
28
|
"""Fetch a collection of units."""
|
|
25
29
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
30
|
+
return super().list(
|
|
31
|
+
page=page,
|
|
32
|
+
page_size=page_size,
|
|
33
|
+
order_by=order_by,
|
|
34
|
+
workspace_id=normalize_uuid(workspace),
|
|
35
|
+
thing_id=normalize_uuid(thing),
|
|
36
|
+
datastream_id=normalize_uuid(datastream),
|
|
37
|
+
unit_type=unit_type,
|
|
38
|
+
fetch_all=fetch_all,
|
|
31
39
|
)
|
|
32
40
|
|
|
33
|
-
def get(self, uid: Union[UUID, str]) -> "Unit":
|
|
34
|
-
"""Get a unit by ID."""
|
|
35
|
-
|
|
36
|
-
return super()._get(uid=str(uid))
|
|
37
|
-
|
|
38
41
|
def create(
|
|
39
42
|
self,
|
|
40
|
-
workspace: Union["Workspace", UUID, str],
|
|
41
43
|
name: str,
|
|
42
44
|
symbol: str,
|
|
43
45
|
definition: str,
|
|
44
46
|
unit_type: str,
|
|
47
|
+
workspace: Optional[Union["Workspace", UUID, str]] = None,
|
|
45
48
|
) -> "Unit":
|
|
46
49
|
"""Create a new unit."""
|
|
47
50
|
|
|
48
|
-
|
|
51
|
+
body = {
|
|
49
52
|
"name": name,
|
|
50
53
|
"symbol": symbol,
|
|
51
54
|
"definition": definition,
|
|
52
55
|
"type": unit_type,
|
|
53
|
-
"workspaceId":
|
|
56
|
+
"workspaceId": normalize_uuid(workspace),
|
|
54
57
|
}
|
|
55
58
|
|
|
56
|
-
return super().
|
|
59
|
+
return super().create(**body)
|
|
57
60
|
|
|
58
61
|
def update(
|
|
59
62
|
self,
|
|
@@ -65,18 +68,11 @@ class UnitService(EndpointService):
|
|
|
65
68
|
) -> "Unit":
|
|
66
69
|
"""Update a unit."""
|
|
67
70
|
|
|
68
|
-
|
|
71
|
+
body = {
|
|
69
72
|
"name": name,
|
|
70
73
|
"symbol": symbol,
|
|
71
74
|
"definition": definition,
|
|
72
75
|
"type": unit_type,
|
|
73
76
|
}
|
|
74
77
|
|
|
75
|
-
return super().
|
|
76
|
-
uid=str(uid), **{k: v for k, v in kwargs.items() if v is not ...}
|
|
77
|
-
)
|
|
78
|
-
|
|
79
|
-
def delete(self, uid: Union[UUID, str]) -> None:
|
|
80
|
-
"""Delete a unit."""
|
|
81
|
-
|
|
82
|
-
super()._delete(uid=str(uid))
|
|
78
|
+
return super().update(uid=str(uid), **body)
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
from uuid import UUID
|
|
2
|
+
from typing import Union, Optional, Any
|
|
3
|
+
from pydantic.alias_generators import to_camel
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def normalize_uuid(
|
|
7
|
+
obj: Optional[Union[str, UUID, Any]],
|
|
8
|
+
attr: str = "uid"
|
|
9
|
+
):
|
|
10
|
+
if obj is ...:
|
|
11
|
+
return ...
|
|
12
|
+
if obj is None:
|
|
13
|
+
return None
|
|
14
|
+
if obj and hasattr(obj, attr):
|
|
15
|
+
return str(getattr(obj, attr))
|
|
16
|
+
return str(obj)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def order_by_to_camel(s: str) -> str:
|
|
20
|
+
if s.startswith('-'):
|
|
21
|
+
return '-' + to_camel(s[1:])
|
|
22
|
+
return to_camel(s)
|
|
@@ -34,10 +34,8 @@ class Extractor:
|
|
|
34
34
|
continue
|
|
35
35
|
|
|
36
36
|
if isinstance(value, (datetime, pd.Timestamp)):
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
parser = TimestampParser(fmt, offset)
|
|
40
|
-
value = parser.format(value)
|
|
37
|
+
parser = TimestampParser(var["timestamp"])
|
|
38
|
+
value = parser.utc_to_string(value)
|
|
41
39
|
|
|
42
40
|
filled[name] = value
|
|
43
41
|
if not filled:
|
|
@@ -46,6 +46,7 @@ class HydroServerLoader(HydroServer, Loader):
|
|
|
46
46
|
logging.info(f"dtypes: {df.dtypes}")
|
|
47
47
|
|
|
48
48
|
df["value"] = pd.to_numeric(df["value"], errors="raise")
|
|
49
|
+
df = df.rename(columns={"timestamp": "phenomenon_time", "value": "result"})
|
|
49
50
|
self.datastreams.load_observations(uid=col, observations=df)
|
|
50
51
|
|
|
51
52
|
def _fetch_earliest_begin(self, mappings: list[dict]) -> pd.Timestamp:
|