hydroserverpy 0.4.0__py3-none-any.whl → 0.5.0b2__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.

Files changed (72) hide show
  1. hydroserverpy/__init__.py +2 -3
  2. hydroserverpy/api/http.py +22 -0
  3. hydroserverpy/api/main.py +173 -0
  4. hydroserverpy/api/models/__init__.py +21 -0
  5. hydroserverpy/api/models/base.py +74 -0
  6. hydroserverpy/api/models/etl/__init__.py +0 -0
  7. hydroserverpy/api/models/etl/data_archive.py +105 -0
  8. hydroserverpy/api/models/etl/data_source.py +150 -0
  9. hydroserverpy/api/models/etl/orchestration_configuration.py +35 -0
  10. hydroserverpy/api/models/etl/orchestration_system.py +78 -0
  11. hydroserverpy/api/models/iam/__init__.py +0 -0
  12. hydroserverpy/api/models/iam/account.py +12 -0
  13. hydroserverpy/api/models/iam/collaborator.py +34 -0
  14. hydroserverpy/api/models/iam/role.py +10 -0
  15. hydroserverpy/api/models/iam/workspace.py +238 -0
  16. hydroserverpy/api/models/sta/__init__.py +0 -0
  17. hydroserverpy/api/models/sta/datastream.py +338 -0
  18. hydroserverpy/api/models/sta/observed_property.py +72 -0
  19. hydroserverpy/api/models/sta/processing_level.py +50 -0
  20. hydroserverpy/api/models/sta/result_qualifier.py +49 -0
  21. hydroserverpy/api/models/sta/sensor.py +105 -0
  22. hydroserverpy/api/models/sta/thing.py +217 -0
  23. hydroserverpy/api/models/sta/unit.py +49 -0
  24. hydroserverpy/api/services/__init__.py +11 -0
  25. hydroserverpy/api/services/base.py +102 -0
  26. hydroserverpy/api/services/etl/__init__.py +0 -0
  27. hydroserverpy/api/services/etl/data_archive.py +196 -0
  28. hydroserverpy/api/services/etl/data_source.py +196 -0
  29. hydroserverpy/api/services/etl/orchestration_system.py +74 -0
  30. hydroserverpy/api/services/iam/__init__.py +0 -0
  31. hydroserverpy/api/services/iam/workspace.py +126 -0
  32. hydroserverpy/api/services/sta/__init__.py +0 -0
  33. hydroserverpy/api/services/sta/datastream.py +354 -0
  34. hydroserverpy/api/services/sta/observed_property.py +100 -0
  35. hydroserverpy/api/services/sta/processing_level.py +78 -0
  36. hydroserverpy/api/services/sta/result_qualifier.py +74 -0
  37. hydroserverpy/api/services/sta/sensor.py +116 -0
  38. hydroserverpy/api/services/sta/thing.py +188 -0
  39. hydroserverpy/api/services/sta/unit.py +82 -0
  40. hydroserverpy/etl/loaders/hydroserver_loader.py +1 -1
  41. hydroserverpy/etl_csv/hydroserver_etl_csv.py +49 -34
  42. {hydroserverpy-0.4.0.dist-info → hydroserverpy-0.5.0b2.dist-info}/METADATA +4 -3
  43. hydroserverpy-0.5.0b2.dist-info/RECORD +66 -0
  44. {hydroserverpy-0.4.0.dist-info → hydroserverpy-0.5.0b2.dist-info}/WHEEL +1 -1
  45. hydroserverpy/core/endpoints/__init__.py +0 -9
  46. hydroserverpy/core/endpoints/base.py +0 -146
  47. hydroserverpy/core/endpoints/data_loaders.py +0 -93
  48. hydroserverpy/core/endpoints/data_sources.py +0 -93
  49. hydroserverpy/core/endpoints/datastreams.py +0 -225
  50. hydroserverpy/core/endpoints/observed_properties.py +0 -111
  51. hydroserverpy/core/endpoints/processing_levels.py +0 -111
  52. hydroserverpy/core/endpoints/result_qualifiers.py +0 -111
  53. hydroserverpy/core/endpoints/sensors.py +0 -111
  54. hydroserverpy/core/endpoints/things.py +0 -261
  55. hydroserverpy/core/endpoints/units.py +0 -111
  56. hydroserverpy/core/schemas/__init__.py +0 -9
  57. hydroserverpy/core/schemas/base.py +0 -124
  58. hydroserverpy/core/schemas/data_loaders.py +0 -73
  59. hydroserverpy/core/schemas/data_sources.py +0 -223
  60. hydroserverpy/core/schemas/datastreams.py +0 -330
  61. hydroserverpy/core/schemas/observed_properties.py +0 -43
  62. hydroserverpy/core/schemas/processing_levels.py +0 -31
  63. hydroserverpy/core/schemas/result_qualifiers.py +0 -26
  64. hydroserverpy/core/schemas/sensors.py +0 -68
  65. hydroserverpy/core/schemas/things.py +0 -346
  66. hydroserverpy/core/schemas/units.py +0 -29
  67. hydroserverpy/core/service.py +0 -200
  68. hydroserverpy-0.4.0.dist-info/RECORD +0 -51
  69. /hydroserverpy/{core → api}/__init__.py +0 -0
  70. {hydroserverpy-0.4.0.dist-info → hydroserverpy-0.5.0b2.dist-info/licenses}/LICENSE +0 -0
  71. {hydroserverpy-0.4.0.dist-info → hydroserverpy-0.5.0b2.dist-info}/top_level.txt +0 -0
  72. {hydroserverpy-0.4.0.dist-info → hydroserverpy-0.5.0b2.dist-info}/zip-safe +0 -0
@@ -0,0 +1,50 @@
1
+ from typing import Union, Optional, TYPE_CHECKING
2
+ from uuid import UUID
3
+ from pydantic import BaseModel, Field
4
+ from ..base import HydroServerModel
5
+
6
+ if TYPE_CHECKING:
7
+ from hydroserverpy import HydroServer
8
+ from hydroserverpy.api.models import Workspace
9
+
10
+
11
+ class ProcessingLevelFields(BaseModel):
12
+ code: str = Field(..., max_length=255)
13
+ definition: Optional[str] = None
14
+ explanation: Optional[str] = None
15
+
16
+
17
+ class ProcessingLevel(HydroServerModel, ProcessingLevelFields):
18
+ def __init__(self, _connection: "HydroServer", _uid: Union[UUID, str], **data):
19
+ super().__init__(
20
+ _connection=_connection, _model_ref="processinglevels", _uid=_uid, **data
21
+ )
22
+
23
+ self._workspace_id = str(data.get("workspace_id") or data["workspaceId"])
24
+
25
+ self._workspace = None
26
+
27
+ @property
28
+ def workspace(self) -> "Workspace":
29
+ """The workspace this processing level belongs to."""
30
+
31
+ if self._workspace is None and self._workspace_id:
32
+ self._workspace = self._connection.workspaces.get(uid=self._workspace_id)
33
+
34
+ return self._workspace
35
+
36
+ def refresh(self):
37
+ """Refresh this processing level from HydroServer."""
38
+
39
+ super()._refresh()
40
+ self._workspace = None
41
+
42
+ def save(self):
43
+ """Save changes to this processing level to HydroServer."""
44
+
45
+ super()._save()
46
+
47
+ def delete(self):
48
+ """Delete this processing level from HydroServer."""
49
+
50
+ super()._delete()
@@ -0,0 +1,49 @@
1
+ from typing import Union, Optional, TYPE_CHECKING
2
+ from uuid import UUID
3
+ from pydantic import BaseModel, Field
4
+ from ..base import HydroServerModel
5
+
6
+ if TYPE_CHECKING:
7
+ from hydroserverpy import HydroServer
8
+ from hydroserverpy.api.models import Workspace
9
+
10
+
11
+ class ResultQualifierFields(BaseModel):
12
+ code: str = Field(..., max_length=255)
13
+ description: str
14
+
15
+
16
+ class ResultQualifier(HydroServerModel, ResultQualifierFields):
17
+ def __init__(self, _connection: "HydroServer", _uid: Union[UUID, str], **data):
18
+ super().__init__(
19
+ _connection=_connection, _model_ref="resultqualifiers", _uid=_uid, **data
20
+ )
21
+
22
+ self._workspace_id = str(data.get("workspace_id") or data["workspaceId"])
23
+
24
+ self._workspace = None
25
+
26
+ @property
27
+ def workspace(self) -> "Workspace":
28
+ """The workspace this result qualifier belongs to."""
29
+
30
+ if self._workspace is None and self._workspace_id:
31
+ self._workspace = self._connection.workspaces.get(uid=self._workspace_id)
32
+
33
+ return self._workspace
34
+
35
+ def refresh(self):
36
+ """Refresh this result qualifier from HydroServer."""
37
+
38
+ super()._refresh()
39
+ self._workspace = None
40
+
41
+ def save(self):
42
+ """Save changes to this result qualifier to HydroServer."""
43
+
44
+ super()._save()
45
+
46
+ def delete(self):
47
+ """Delete this result qualifier from HydroServer."""
48
+
49
+ super()._delete()
@@ -0,0 +1,105 @@
1
+ from typing import Union, Optional, TYPE_CHECKING
2
+ from uuid import UUID
3
+ from pydantic import BaseModel, Field, AliasChoices, AliasPath
4
+ from ..base import HydroServerModel
5
+
6
+ if TYPE_CHECKING:
7
+ from hydroserverpy import HydroServer
8
+ from hydroserverpy.api.models import Workspace
9
+
10
+
11
+ class SensorFields(BaseModel):
12
+ name: str = Field(..., max_length=255)
13
+ description: str
14
+ encoding_type: str = Field(..., max_length=255)
15
+ manufacturer: Optional[str] = Field(
16
+ None,
17
+ max_length=255,
18
+ validation_alias=AliasChoices(
19
+ "manufacturer", AliasPath("metadata", "sensorModel", "sensorManufacturer")
20
+ ),
21
+ )
22
+ sensor_model: Optional[str] = Field(
23
+ None,
24
+ max_length=255,
25
+ alias="model",
26
+ validation_alias=AliasChoices(
27
+ "model", AliasPath("metadata", "sensorModel", "sensorModelName")
28
+ ),
29
+ )
30
+ sensor_model_link: Optional[str] = Field(
31
+ None,
32
+ max_length=500,
33
+ alias="modelLink",
34
+ validation_alias=AliasChoices(
35
+ "modelLink", AliasPath("metadata", "sensorModel", "sensorModelUrl")
36
+ ),
37
+ )
38
+ method_type: str = Field(
39
+ ...,
40
+ max_length=100,
41
+ validation_alias=AliasChoices(
42
+ "methodType", AliasPath("metadata", "methodType")
43
+ ),
44
+ )
45
+ method_link: Optional[str] = Field(
46
+ None,
47
+ max_length=500,
48
+ validation_alias=AliasChoices(
49
+ "methodLink", AliasPath("metadata", "methodLink")
50
+ ),
51
+ )
52
+ method_code: Optional[str] = Field(
53
+ None,
54
+ max_length=50,
55
+ validation_alias=AliasChoices(
56
+ "methodCode", AliasPath("metadata", "methodCode")
57
+ ),
58
+ )
59
+
60
+
61
+ class Sensor(HydroServerModel, SensorFields):
62
+ def __init__(self, _connection: "HydroServer", _uid: Union[UUID, str], **data):
63
+ super().__init__(
64
+ _connection=_connection, _model_ref="sensors", _uid=_uid, **data
65
+ )
66
+
67
+ self._workspace_id = (
68
+ data.get("workspace_id")
69
+ or data.get("workspaceId")
70
+ or (
71
+ None
72
+ if data.get("properties", {}).get("workspace") is None
73
+ else data.get("properties", {}).get("workspace", {}).get("id")
74
+ )
75
+ )
76
+ self._workspace_id = (
77
+ str(self._workspace_id) if self._workspace_id is not None else None
78
+ )
79
+
80
+ self._workspace = None
81
+
82
+ @property
83
+ def workspace(self) -> "Workspace":
84
+ """The workspace this sensor belongs to."""
85
+
86
+ if self._workspace is None and self._workspace_id:
87
+ self._workspace = self._connection.workspaces.get(uid=self._workspace_id)
88
+
89
+ return self._workspace
90
+
91
+ def refresh(self):
92
+ """Refresh this sensor from HydroServer."""
93
+
94
+ super()._refresh()
95
+ self._workspace = None
96
+
97
+ def save(self):
98
+ """Save changes to this sensor to HydroServer."""
99
+
100
+ super()._save()
101
+
102
+ def delete(self):
103
+ """Delete this sensor from HydroServer."""
104
+
105
+ super()._delete()
@@ -0,0 +1,217 @@
1
+ from typing import Optional, Union, List, Dict, IO, TYPE_CHECKING
2
+ from uuid import UUID
3
+ from pydantic import (
4
+ BaseModel,
5
+ Field,
6
+ AliasChoices,
7
+ AliasPath,
8
+ AnyHttpUrl,
9
+ field_validator,
10
+ )
11
+ from ..base import HydroServerModel
12
+
13
+ if TYPE_CHECKING:
14
+ from hydroserverpy import HydroServer
15
+ from hydroserverpy.api.models import Workspace, Datastream
16
+
17
+
18
+ class ThingFields(BaseModel):
19
+ name: str = Field(..., max_length=200)
20
+ description: str
21
+ sampling_feature_type: str = Field(
22
+ ...,
23
+ max_length=200,
24
+ validation_alias=AliasChoices(
25
+ "samplingFeatureType", AliasPath("properties", "samplingFeatureType")
26
+ ),
27
+ )
28
+ sampling_feature_code: str = Field(
29
+ ...,
30
+ max_length=200,
31
+ validation_alias=AliasChoices(
32
+ "samplingFeatureCode", AliasPath("properties", "samplingFeatureCode")
33
+ ),
34
+ )
35
+ site_type: str = Field(
36
+ ...,
37
+ max_length=200,
38
+ validation_alias=AliasChoices("siteType", AliasPath("properties", "siteType")),
39
+ )
40
+ tags: Dict[str, str] = Field(
41
+ ...,
42
+ json_schema_extra={"editable": False, "read_only": True},
43
+ validation_alias=AliasChoices("tags", AliasPath("properties", "tags")),
44
+ )
45
+ photos: Dict[str, AnyHttpUrl] = Field(
46
+ ...,
47
+ json_schema_extra={"editable": False, "read_only": True},
48
+ validation_alias=AliasChoices("photos", AliasPath("properties", "photos")),
49
+ )
50
+ data_disclaimer: Optional[str] = Field(
51
+ None,
52
+ validation_alias=AliasChoices(
53
+ "dataDisclaimer", AliasPath("properties", "dataDisclaimer")
54
+ ),
55
+ )
56
+ is_private: bool = Field(
57
+ ...,
58
+ validation_alias=AliasChoices(
59
+ "isPrivate", AliasPath("properties", "isPrivate")
60
+ ),
61
+ )
62
+
63
+ @field_validator("tags", mode="before")
64
+ def convert_tags(
65
+ cls, value: Union[List[Dict[str, str]], Dict[str, str]]
66
+ ) -> Dict[str, str]:
67
+ if isinstance(value, list):
68
+ return {item["key"]: item["value"] for item in value}
69
+ return value
70
+
71
+ @field_validator("photos", mode="before")
72
+ def convert_photos(
73
+ cls, value: Union[List[Dict[str, str]], Dict[str, str]]
74
+ ) -> Dict[str, str]:
75
+ if isinstance(value, list):
76
+ return {item["name"]: item["link"] for item in value}
77
+ return value
78
+
79
+
80
+ class LocationFields(BaseModel):
81
+ latitude: float = Field(
82
+ ...,
83
+ ge=-90,
84
+ le=90,
85
+ validation_alias=AliasChoices(
86
+ "latitude",
87
+ AliasPath("Locations", 0, "location", "geometry", "coordinates", 0),
88
+ ),
89
+ )
90
+ longitude: float = Field(
91
+ ...,
92
+ ge=-180,
93
+ le=180,
94
+ validation_alias=AliasChoices(
95
+ "longitude",
96
+ AliasPath("Locations", 0, "location", "geometry", "coordinates", 1),
97
+ ),
98
+ )
99
+ elevation_m: Optional[float] = Field(
100
+ None,
101
+ ge=-99999,
102
+ le=99999,
103
+ validation_alias=AliasChoices(
104
+ "elevation_m", AliasPath("Locations", 0, "properties", "elevation_m")
105
+ ),
106
+ )
107
+ elevation_datum: Optional[str] = Field(
108
+ None,
109
+ max_length=255,
110
+ validation_alias=AliasChoices(
111
+ "elevationDatum", AliasPath("Locations", 0, "properties", "elevationDatum")
112
+ ),
113
+ )
114
+ state: Optional[str] = Field(
115
+ None,
116
+ max_length=200,
117
+ validation_alias=AliasChoices(
118
+ "state", AliasPath("Locations", 0, "properties", "state")
119
+ ),
120
+ )
121
+ county: Optional[str] = Field(
122
+ None,
123
+ max_length=200,
124
+ validation_alias=AliasChoices(
125
+ "county", AliasPath("Locations", 0, "properties", "county")
126
+ ),
127
+ )
128
+ country: Optional[str] = Field(
129
+ None,
130
+ max_length=2,
131
+ validation_alias=AliasChoices(
132
+ "country", AliasPath("Locations", 0, "properties", "country")
133
+ ),
134
+ )
135
+
136
+
137
+ class Thing(HydroServerModel, ThingFields, LocationFields):
138
+ def __init__(self, _connection: "HydroServer", _uid: Union[UUID, str], **data):
139
+ super().__init__(
140
+ _connection=_connection, _model_ref="things", _uid=_uid, **data
141
+ )
142
+
143
+ self._workspace_id = str(
144
+ data.get("workspace_id")
145
+ or data.get("workspaceId")
146
+ or data["properties"]["workspace"]["id"]
147
+ )
148
+
149
+ self._workspace = None
150
+ self._datastreams = None
151
+ self._photos = None
152
+ self._tags = None
153
+
154
+ @property
155
+ def workspace(self) -> "Workspace":
156
+ """The workspace this thing belongs to."""
157
+
158
+ if self._workspace is None:
159
+ self._workspace = self._connection.workspaces.get(uid=self._workspace_id)
160
+
161
+ return self._workspace
162
+
163
+ @property
164
+ def datastreams(self) -> List["Datastream"]:
165
+ """The datastreams collected at this thing."""
166
+
167
+ if self._datastreams is None:
168
+ self._datastreams = self._connection.datastreams.list(thing=self.uid)
169
+
170
+ return self._datastreams
171
+
172
+ def refresh(self):
173
+ """Refresh this thing from HydroServer."""
174
+
175
+ super()._refresh()
176
+ self._workspace = None
177
+ self._datastreams = None
178
+
179
+ def save(self):
180
+ """Save changes to this thing to HydroServer."""
181
+
182
+ super()._save()
183
+
184
+ def delete(self):
185
+ """Delete this thing from HydroServer."""
186
+
187
+ super()._delete()
188
+
189
+ def add_tag(self, key: str, value: str):
190
+ """Add a tag to this thing."""
191
+
192
+ self._connection.things.add_tag(uid=self.uid, key=key, value=value)
193
+ self.tags[key] = value
194
+
195
+ def update_tag(self, key: str, value: str):
196
+ """Edit a tag of this thing."""
197
+
198
+ self._connection.things.update_tag(uid=self.uid, key=key, value=value)
199
+ self.tags[key] = value
200
+
201
+ def delete_tag(self, key: str):
202
+ """Delete a tag of this thing."""
203
+
204
+ self._connection.things.delete_tag(uid=self.uid, key=key)
205
+ del self.tags[key]
206
+
207
+ def add_photo(self, file: IO[bytes]):
208
+ """Add a photo of this thing."""
209
+
210
+ photo = self._connection.things.add_photo(uid=self.uid, file=file)
211
+ self.photos[photo["name"]] = photo["link"]
212
+
213
+ def delete_photo(self, name: str):
214
+ """Delete a photo of this thing."""
215
+
216
+ self._connection.things.delete_photo(uid=self.uid, name=name)
217
+ del self.photos[name]
@@ -0,0 +1,49 @@
1
+ from typing import Union, TYPE_CHECKING
2
+ from uuid import UUID
3
+ from pydantic import BaseModel, Field
4
+ from ..base import HydroServerModel
5
+
6
+ if TYPE_CHECKING:
7
+ from hydroserverpy import HydroServer
8
+ from hydroserverpy.api.models import Workspace
9
+
10
+
11
+ class UnitFields(BaseModel):
12
+ name: str = Field(..., max_length=255)
13
+ symbol: str = Field(..., max_length=255)
14
+ definition: str
15
+ unit_type: str = Field(..., max_length=255, alias="type")
16
+
17
+
18
+ class Unit(HydroServerModel, UnitFields):
19
+ def __init__(self, _connection: "HydroServer", _uid: Union[UUID, str], **data):
20
+ super().__init__(_connection=_connection, _model_ref="units", _uid=_uid, **data)
21
+
22
+ self._workspace_id = str(data.get("workspace_id") or data["workspaceId"])
23
+
24
+ self._workspace = None
25
+
26
+ @property
27
+ def workspace(self) -> "Workspace":
28
+ """The workspace this unit belongs to."""
29
+
30
+ if self._workspace is None and self._workspace_id:
31
+ self._workspace = self._connection.workspaces.get(uid=self._workspace_id)
32
+
33
+ return self._workspace
34
+
35
+ def refresh(self):
36
+ """Refresh this unit from HydroServer."""
37
+
38
+ super()._refresh()
39
+ self._workspace = None
40
+
41
+ def save(self):
42
+ """Save changes to this unit to HydroServer."""
43
+
44
+ super()._save()
45
+
46
+ def delete(self):
47
+ """Delete this unit from HydroServer."""
48
+
49
+ super()._delete()
@@ -0,0 +1,11 @@
1
+ from .iam.workspace import WorkspaceService
2
+ from .sta.thing import ThingService
3
+ from .sta.observed_property import ObservedPropertyService
4
+ from .sta.unit import UnitService
5
+ from .sta.processing_level import ProcessingLevelService
6
+ from .sta.result_qualifier import ResultQualifierService
7
+ from .sta.sensor import SensorService
8
+ from .sta.datastream import DatastreamService
9
+ from .etl.orchestration_system import OrchestrationSystemService
10
+ from .etl.data_source import DataSourceService
11
+ from .etl.data_archive import DataArchiveService
@@ -0,0 +1,102 @@
1
+ from typing import TYPE_CHECKING, Type, Union, Optional
2
+ from datetime import datetime
3
+ from uuid import UUID
4
+
5
+ if TYPE_CHECKING:
6
+ from hydroserverpy import HydroServer
7
+ from hydroserverpy.api.models.base import HydroServerModel
8
+
9
+
10
+ class EndpointService:
11
+ _model: Type["HydroServerModel"]
12
+ _api_route: str
13
+ _endpoint_route: str
14
+
15
+ def __init__(self, connection: "HydroServer") -> None:
16
+ self._connection = connection
17
+
18
+ def _list(self, params: Optional[dict] = None):
19
+ path = f"/{self._api_route}/{self._endpoint_route}"
20
+
21
+ response = self._connection.request("get", path, params=params).json()
22
+
23
+ return [
24
+ self._model(
25
+ _connection=self._connection, _uid=UUID(str(obj.pop("id"))), **obj
26
+ )
27
+ for obj in response
28
+ ]
29
+
30
+ def _get(self, uid: Union[UUID, str]):
31
+ path = f"/{self._api_route}/{self._endpoint_route}/{str(uid)}"
32
+ response = self._connection.request("get", path).json()
33
+
34
+ return self._model(
35
+ _connection=self._connection, _uid=UUID(str(response.pop("id"))), **response
36
+ )
37
+
38
+ def _create(self, **kwargs):
39
+ path = f"/{self._api_route}/{self._endpoint_route}"
40
+ headers = {"Content-type": "application/json"}
41
+ response = self._connection.request(
42
+ "post", path, headers=headers, json=self._to_iso_time(kwargs)
43
+ ).json()
44
+
45
+ return self._model(
46
+ _connection=self._connection, _uid=UUID(str(response.pop("id"))), **response
47
+ )
48
+
49
+ def _update(self, uid: Union[UUID, str], **kwargs):
50
+ path = f"/{self._api_route}/{self._endpoint_route}/{str(uid)}"
51
+ headers = {"Content-type": "application/json"}
52
+ response = self._connection.request(
53
+ "patch", path, headers=headers, json=self._to_iso_time(kwargs)
54
+ ).json()
55
+
56
+ return self._model(
57
+ _connection=self._connection, _uid=UUID(str(response.pop("id"))), **response
58
+ )
59
+
60
+ def _delete(self, uid: Union[UUID, str]):
61
+ path = f"/{self._api_route}/{self._endpoint_route}/{str(uid)}"
62
+ response = self._connection.request("delete", path)
63
+
64
+ return response
65
+
66
+ def _to_iso_time(self, obj):
67
+ if isinstance(obj, dict):
68
+ return {k: self._to_iso_time(v) for k, v in obj.items()}
69
+ elif isinstance(obj, list):
70
+ return [self._to_iso_time(i) for i in obj]
71
+ elif isinstance(obj, datetime):
72
+ return obj.isoformat()
73
+ return obj
74
+
75
+
76
+ class SensorThingsService(EndpointService):
77
+ _sta_route: str
78
+
79
+ def __init__(self, connection) -> None:
80
+ super().__init__(connection)
81
+
82
+ def _list(self, path: Optional[str] = None, params: Optional[dict] = None):
83
+ path = path or f"/{self._sta_route}"
84
+ response = self._connection.request("get", path, params=params)
85
+
86
+ return [
87
+ self._model(_connection=self._connection, _uid=obj.pop("@iot.id"), **obj)
88
+ for obj in response.json()["value"]
89
+ ]
90
+
91
+ def _get(
92
+ self,
93
+ uid: Union[UUID, str],
94
+ path: Optional[str] = None,
95
+ params: Optional[dict] = None,
96
+ ):
97
+ path = path or f"/{self._sta_route}('{str(uid)}')"
98
+ response = self._connection.request("get", path, params=params).json()
99
+
100
+ return self._model(
101
+ _connection=self._connection, _uid=response.pop("@iot.id"), **response
102
+ )
File without changes