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,34 @@
1
+ from typing import Union, TYPE_CHECKING
2
+ from uuid import UUID
3
+ from pydantic import BaseModel
4
+
5
+ if TYPE_CHECKING:
6
+ from hydroserverpy.api.models.iam.account import Account
7
+ from hydroserverpy.api.models.iam.role import Role
8
+
9
+
10
+ class CollaboratorFields(BaseModel):
11
+ user: "Account"
12
+ role: "Role"
13
+ workspace_id: Union[UUID, str]
14
+
15
+
16
+ class Collaborator(CollaboratorFields):
17
+ def __init__(self, _connection, **data):
18
+ super().__init__(**data)
19
+ self._connection = _connection
20
+
21
+ def edit_role(self, role: Union["Role", UUID, str]):
22
+ """Edit the role of this workspace collaborator."""
23
+
24
+ response = self._connection.workspaces.edit_collaborator_role(
25
+ uid=self.workspace_id, email=self.user.email, role=role
26
+ )
27
+ self.role = response.role
28
+
29
+ def remove(self):
30
+ """Remove this collaborator from the workspace."""
31
+
32
+ self._connection.workspaces.remove_collaborator(
33
+ uid=self.workspace_id, email=self.user.email
34
+ )
@@ -0,0 +1,10 @@
1
+ from typing import Optional, Union
2
+ from uuid import UUID
3
+ from pydantic import BaseModel, Field
4
+
5
+
6
+ class Role(BaseModel):
7
+ uid: UUID = Field(..., alias="id")
8
+ name: str = Field(..., max_length=255)
9
+ description: str
10
+ workspace_id: Optional[Union[UUID, str]] = None
@@ -0,0 +1,238 @@
1
+ from typing import List, Union, Optional, TYPE_CHECKING
2
+ from uuid import UUID
3
+ from pydantic import BaseModel, Field, EmailStr
4
+ from ..base import HydroServerModel
5
+
6
+ if TYPE_CHECKING:
7
+ from hydroserverpy import HydroServer
8
+ from hydroserverpy.api.models import (
9
+ Role,
10
+ Collaborator,
11
+ Account,
12
+ Thing,
13
+ ObservedProperty,
14
+ Sensor,
15
+ Unit,
16
+ ProcessingLevel,
17
+ ResultQualifier,
18
+ Datastream,
19
+ OrchestrationSystem,
20
+ DataSource,
21
+ DataArchive,
22
+ )
23
+
24
+
25
+ class WorkspaceFields(BaseModel):
26
+ name: str = Field(..., max_length=255)
27
+ is_private: bool
28
+ owner: "Account" = Field(..., json_schema_extra={"read_only": True})
29
+ collaborator_role: Optional["Role"] = None
30
+ pending_transfer_to: Optional["Account"] = None
31
+
32
+
33
+ class Workspace(HydroServerModel, WorkspaceFields):
34
+ def __init__(self, _connection: "HydroServer", _uid: Union[UUID, str], **data):
35
+ super().__init__(
36
+ _connection=_connection, _model_ref="workspaces", _uid=_uid, **data
37
+ )
38
+ self._roles = None
39
+ self._collaborators = None
40
+ self._things = None
41
+ self._observedproperties = None
42
+ self._processinglevels = None
43
+ self._resultqualifiers = None
44
+ self._units = None
45
+ self._sensors = None
46
+ self._datastreams = None
47
+ self._orchestrationsystems = None
48
+ self._datasources = None
49
+ self._dataarchives = None
50
+
51
+ @property
52
+ def roles(self) -> List["Role"]:
53
+ """The roles that can be assigned for this workspace."""
54
+
55
+ if self._roles is None:
56
+ self._roles = self._connection.workspaces.list_roles(uid=self.uid)
57
+
58
+ return self._roles
59
+
60
+ @property
61
+ def collaborators(self) -> List["Collaborator"]:
62
+ """The collaborators associated with this workspace."""
63
+
64
+ if self._collaborators is None:
65
+ self._collaborators = self._connection.workspaces.list_collaborators(
66
+ uid=self.uid
67
+ )
68
+
69
+ return self._collaborators
70
+
71
+ @property
72
+ def things(self) -> List["Thing"]:
73
+ """The things associated with this workspace."""
74
+
75
+ if self._things is None:
76
+ self._things = self._connection.things.list(workspace=self.uid)
77
+
78
+ return self._things
79
+
80
+ @property
81
+ def observedproperties(self) -> List["ObservedProperty"]:
82
+ """The observed properties associated with this workspace."""
83
+
84
+ if self._observedproperties is None:
85
+ self._observedproperties = self._connection.observedproperties.list(
86
+ workspace=self.uid
87
+ )
88
+
89
+ return self._observedproperties
90
+
91
+ @property
92
+ def processinglevels(self) -> List["ProcessingLevel"]:
93
+ """The processing levels associated with this workspace."""
94
+
95
+ if self._processinglevels is None:
96
+ self._processinglevels = self._connection.processinglevels.list(
97
+ workspace=self.uid
98
+ )
99
+
100
+ return self._processinglevels
101
+
102
+ @property
103
+ def resultqualifiers(self) -> List["ResultQualifier"]:
104
+ """The result qualifiers associated with this workspace."""
105
+
106
+ if self._resultqualifiers is None:
107
+ self._resultqualifiers = self._connection.resultqualifiers.list(
108
+ workspace=self.uid
109
+ )
110
+
111
+ return self._resultqualifiers
112
+
113
+ @property
114
+ def units(self) -> List["Unit"]:
115
+ """The units associated with this workspace."""
116
+
117
+ if self._units is None:
118
+ self._units = self._connection.units.list(workspace=self.uid)
119
+
120
+ return self._units
121
+
122
+ @property
123
+ def sensors(self) -> List["Sensor"]:
124
+ """The sensors associated with this workspace."""
125
+
126
+ if self._sensors is None:
127
+ self._sensors = self._connection.sensors.list(workspace=self.uid)
128
+
129
+ return self._sensors
130
+
131
+ @property
132
+ def datastreams(self) -> List["Datastream"]:
133
+ """The datastreams associated with this workspace."""
134
+
135
+ if self._datastreams is None:
136
+ self._datastreams = self._connection.datastreams.list(workspace=self.uid)
137
+
138
+ return self._datastreams
139
+
140
+ @property
141
+ def orchestrationsystems(self) -> List["OrchestrationSystem"]:
142
+ """The orchestration systems associated with this workspace."""
143
+
144
+ if self._orchestrationsystems is None:
145
+ self._orchestrationsystems = self._connection.orchestrationsystems.list(
146
+ workspace=self.uid
147
+ )
148
+
149
+ return self._orchestrationsystems
150
+
151
+ @property
152
+ def datasources(self) -> List["DataSource"]:
153
+ """The data sources associated with this workspace."""
154
+
155
+ if self._datasources is None:
156
+ self._datasources = self._connection.datasources.list(workspace=self.uid)
157
+
158
+ return self._datasources
159
+
160
+ @property
161
+ def dataarchives(self) -> List["DataArchive"]:
162
+ """The data archives associated with this workspace."""
163
+
164
+ if self._dataarchives is None:
165
+ self._dataarchives = self._connection.dataarchives.list(workspace=self.uid)
166
+
167
+ return self._dataarchives
168
+
169
+ def refresh(self) -> None:
170
+ """Refresh the workspace details from HydroServer."""
171
+
172
+ self._roles = None
173
+ self._collaborators = None
174
+ self._things = None
175
+ self._observedproperties = None
176
+ self._processinglevels = None
177
+ self._units = None
178
+ self._sensors = None
179
+ self._datastreams = None
180
+ super()._refresh()
181
+
182
+ def save(self):
183
+ """Save changes to this workspace to HydroServer."""
184
+
185
+ super()._save()
186
+
187
+ def delete(self):
188
+ """Delete this workspace from HydroServer."""
189
+
190
+ super()._delete()
191
+
192
+ def add_collaborator(
193
+ self, email: EmailStr, role: Union["Role", UUID, str]
194
+ ) -> "Collaborator":
195
+ """Add a new collaborator to the workspace."""
196
+
197
+ response = self._connection.workspaces.add_collaborator(
198
+ uid=self.uid, email=email, role=role
199
+ )
200
+ self._collaborators = None
201
+
202
+ return response
203
+
204
+ def edit_collaborator_role(
205
+ self, email: EmailStr, role: Union["Role", UUID, str]
206
+ ) -> "Collaborator":
207
+ """Edit a collaborator's role in this workspace."""
208
+
209
+ response = self._connection.workspaces.edit_collaborator_role(
210
+ uid=self.uid, email=email, role=role
211
+ )
212
+ self._collaborators = None
213
+
214
+ return response
215
+
216
+ def remove_collaborator(self, email: EmailStr) -> None:
217
+ """Remove a collaborator from the workspace."""
218
+
219
+ self._connection.workspaces.remove_collaborator(uid=self.uid, email=email)
220
+ self._collaborators = None
221
+
222
+ def transfer_ownership(self, email: EmailStr) -> None:
223
+ """Transfer ownership of this workspace to another HydroServer user."""
224
+
225
+ self._connection.workspaces.transfer_ownership(uid=self.uid, email=email)
226
+ self.refresh()
227
+
228
+ def accept_ownership_transfer(self) -> None:
229
+ """Accept ownership transfer of this workspace."""
230
+
231
+ self._connection.workspaces.accept_ownership_transfer(uid=self.uid)
232
+ self.refresh()
233
+
234
+ def cancel_ownership_transfer(self) -> None:
235
+ """Cancel ownership transfer of this workspace."""
236
+
237
+ self._connection.workspaces.cancel_ownership_transfer(uid=self.uid)
238
+ self.refresh()
File without changes
@@ -0,0 +1,338 @@
1
+ from typing import Union, Optional, Literal, TYPE_CHECKING
2
+ from pydantic import BaseModel, Field, AliasChoices, AliasPath, field_validator
3
+ from pandas import DataFrame
4
+ from uuid import UUID
5
+ from datetime import datetime
6
+ from ..base import HydroServerModel
7
+
8
+ if TYPE_CHECKING:
9
+ from hydroserverpy import HydroServer
10
+ from hydroserverpy.api.models import (
11
+ Workspace,
12
+ Thing,
13
+ Sensor,
14
+ ObservedProperty,
15
+ Unit,
16
+ ProcessingLevel,
17
+ )
18
+
19
+
20
+ class DatastreamFields(BaseModel):
21
+ name: str = Field(..., max_length=255)
22
+ description: str
23
+ observation_type: str = Field(..., max_length=255)
24
+ sampled_medium: str = Field(
25
+ ...,
26
+ max_length=255,
27
+ validation_alias=AliasChoices(
28
+ "sampledMedium", AliasPath("properties", "sampledMedium")
29
+ ),
30
+ )
31
+ no_data_value: float = Field(
32
+ ...,
33
+ validation_alias=AliasChoices(
34
+ "noDataValue", AliasPath("properties", "noDataValue")
35
+ ),
36
+ )
37
+ aggregation_statistic: str = Field(
38
+ ...,
39
+ max_length=255,
40
+ validation_alias=AliasChoices(
41
+ "aggregationStatistic", AliasPath("properties", "aggregationStatistic")
42
+ ),
43
+ )
44
+ time_aggregation_interval: float = Field(
45
+ ...,
46
+ validation_alias=AliasChoices(
47
+ "timeAggregationInterval",
48
+ AliasPath("properties", "timeAggregationInterval"),
49
+ ),
50
+ )
51
+ status: Optional[str] = Field(
52
+ None,
53
+ max_length=255,
54
+ validation_alias=AliasChoices("status", AliasPath("properties", "status")),
55
+ )
56
+ result_type: str = Field(
57
+ ...,
58
+ max_length=255,
59
+ validation_alias=AliasChoices(
60
+ "resultType", AliasPath("properties", "resultType")
61
+ ),
62
+ )
63
+ value_count: Optional[int] = Field(
64
+ None,
65
+ ge=0,
66
+ validation_alias=AliasChoices(
67
+ "valueCount", AliasPath("properties", "valueCount")
68
+ ),
69
+ )
70
+ phenomenon_begin_time: Optional[datetime] = Field(
71
+ None, validation_alias=AliasChoices("phenomenonBeginTime", "phenomenonTime")
72
+ )
73
+ phenomenon_end_time: Optional[datetime] = Field(
74
+ None, validation_alias=AliasChoices("phenomenonEndTime", "phenomenonTime")
75
+ )
76
+ result_begin_time: Optional[datetime] = Field(
77
+ None, validation_alias=AliasChoices("resultBeginTime", "resultTime")
78
+ )
79
+ result_end_time: Optional[datetime] = Field(
80
+ None, validation_alias=AliasChoices("resultEndTime", "resultTime")
81
+ )
82
+ is_private: bool = Field(
83
+ False,
84
+ validation_alias=AliasChoices(
85
+ "isPrivate", AliasPath("properties", "isPrivate")
86
+ ),
87
+ )
88
+ is_visible: bool = Field(
89
+ True,
90
+ validation_alias=AliasChoices(
91
+ "isVisible", AliasPath("properties", "isVisible")
92
+ ),
93
+ )
94
+ time_aggregation_interval_unit: Literal["seconds", "minutes", "hours", "days"] = (
95
+ Field(
96
+ ...,
97
+ validation_alias=AliasChoices(
98
+ "timeAggregationIntervalUnit",
99
+ AliasPath("properties", "timeAggregationIntervalUnitOfMeasurement"),
100
+ ),
101
+ )
102
+ )
103
+ intended_time_spacing: Optional[float] = Field(
104
+ None,
105
+ validation_alias=AliasChoices(
106
+ "intendedTimeSpacing", AliasPath("properties", "intendedTimeSpacing")
107
+ ),
108
+ )
109
+ intended_time_spacing_unit: Optional[
110
+ Literal["seconds", "minutes", "hours", "days"]
111
+ ] = Field(
112
+ None,
113
+ validation_alias=AliasChoices(
114
+ "intendedTimeSpacingUnit",
115
+ AliasPath("properties", "intendedTimeSpacingUnit"),
116
+ ),
117
+ )
118
+
119
+ @field_validator(
120
+ "phenomenon_begin_time",
121
+ "phenomenon_end_time",
122
+ "result_begin_time",
123
+ "result_end_time",
124
+ mode="before",
125
+ )
126
+ def split_time(cls, value: str, info) -> str:
127
+ if isinstance(value, str):
128
+ parts = value.split("/")
129
+ return parts[0] if "begin" in info.field_name else parts[-1]
130
+ return value
131
+
132
+
133
+ class Datastream(HydroServerModel, DatastreamFields):
134
+ def __init__(
135
+ self,
136
+ _connection: "HydroServer",
137
+ _uid: Union[UUID, str],
138
+ **data,
139
+ ):
140
+ super().__init__(
141
+ _connection=_connection, _model_ref="datastreams", _uid=_uid, **data
142
+ )
143
+
144
+ self._workspace_id = str(
145
+ data.get("workspace_id")
146
+ or data.get("workspaceId")
147
+ or data["properties"]["workspace"]["id"]
148
+ )
149
+ self._processing_level_id = str(
150
+ data.get("workspace_id")
151
+ or data.get("workspaceId")
152
+ or data["properties"]["processingLevelId"]
153
+ )
154
+ self._unit_id = str(
155
+ data.get("unit_id") or data.get("unitId") or data["properties"]["unitId"]
156
+ )
157
+
158
+ self._workspace = None
159
+ self._thing = None
160
+ self._observed_property = None
161
+ self._unit = None
162
+ self._processing_level = None
163
+ self._sensor = None
164
+
165
+ @property
166
+ def workspace(self) -> "Workspace":
167
+ """The workspace this datastream belongs to."""
168
+
169
+ if self._workspace is None:
170
+ self._workspace = self._connection.workspaces.get(uid=self._workspace_id)
171
+
172
+ return self._workspace
173
+
174
+ @property
175
+ def thing(self) -> "Thing":
176
+ """The thing this datastream belongs to."""
177
+
178
+ if self._thing is None:
179
+ self._thing = self._connection.things.get(
180
+ uid=self.uid,
181
+ fetch_by_datastream_uid=True,
182
+ )
183
+ self._original_data["thing"] = self._thing
184
+
185
+ return self._thing
186
+
187
+ @thing.setter
188
+ def thing(self, thing: Union["Thing", UUID, str]):
189
+ if not thing:
190
+ raise ValueError("Thing of datastream cannot be None.")
191
+ if str(getattr(thing, "uid", thing)) != str(self.thing.uid):
192
+ self._thing = self._connection.things.get(
193
+ uid=str(getattr(thing, "uid", thing))
194
+ )
195
+
196
+ @property
197
+ def sensor(self) -> "Sensor":
198
+ """The sensor this datastream uses."""
199
+
200
+ if self._sensor is None:
201
+ self._sensor = self._connection.sensors.get(
202
+ uid=self.uid,
203
+ fetch_by_datastream_uid=True,
204
+ )
205
+ self._original_data["sensor"] = self._sensor
206
+
207
+ return self._sensor
208
+
209
+ @sensor.setter
210
+ def sensor(self, sensor: Union["Sensor", UUID, str]):
211
+ if not sensor:
212
+ raise ValueError("Sensor of datastream cannot be None.")
213
+ if str(getattr(sensor, "uid", sensor)) != str(self.sensor.uid):
214
+ self._sensor = self._connection.sensors.get(
215
+ uid=str(getattr(sensor, "uid", sensor))
216
+ )
217
+
218
+ @property
219
+ def observed_property(self) -> "Thing":
220
+ """The observed property of this datastream."""
221
+
222
+ if self._observed_property is None:
223
+ self._observed_property = self._connection.observedproperties.get(
224
+ uid=self.uid,
225
+ fetch_by_datastream_uid=True,
226
+ )
227
+ self._original_data["observed_property"] = self._observed_property
228
+
229
+ return self._observed_property
230
+
231
+ @observed_property.setter
232
+ def observed_property(
233
+ self, observed_property: Union["ObservedProperty", UUID, str]
234
+ ):
235
+ if not observed_property:
236
+ raise ValueError("Observed property of datastream cannot be None.")
237
+ if str(getattr(observed_property, "uid", observed_property)) != str(
238
+ self.observed_property.uid
239
+ ):
240
+ self._observed_property = self._connection.observedproperties.get(
241
+ uid=str(getattr(observed_property, "uid", observed_property))
242
+ )
243
+
244
+ @property
245
+ def unit(self) -> "Unit":
246
+ """The unit this datastream uses."""
247
+
248
+ if self._unit is None:
249
+ self._unit = self._connection.units.get(uid=self._unit_id)
250
+ self._original_data["unit"] = self._unit
251
+
252
+ return self._unit
253
+
254
+ @unit.setter
255
+ def unit(self, unit: Union["Unit", UUID, str]):
256
+ if not unit:
257
+ raise ValueError("Unit of datastream cannot be None.")
258
+ if str(getattr(unit, "uid", unit)) != str(self.unit.uid):
259
+ self._unit = self._connection.units.get(uid=str(getattr(unit, "uid", unit)))
260
+
261
+ @property
262
+ def processing_level(self) -> "Thing":
263
+ """The processing level of this datastream."""
264
+
265
+ if self._processing_level is None:
266
+ self._processing_level = self._connection.processinglevels.get(
267
+ uid=self._processing_level_id
268
+ )
269
+ self._original_data["processing_level"] = self._processing_level
270
+
271
+ return self._processing_level
272
+
273
+ @processing_level.setter
274
+ def processing_level(self, processing_level: Union["ProcessingLevel", UUID, str]):
275
+ if not processing_level:
276
+ raise ValueError("Processing level of datastream cannot be None.")
277
+ if str(getattr(processing_level, "uid", processing_level)) != str(
278
+ self.processing_level.uid
279
+ ):
280
+ self._processing_level = self._connection.processinglevels.get(
281
+ uid=str(getattr(processing_level, "uid", processing_level))
282
+ )
283
+
284
+ def refresh(self):
285
+ """Refresh this datastream from HydroServer."""
286
+
287
+ self._workspace = None
288
+ self._workspace_id = None
289
+ self._thing = None
290
+ self._observed_property = None
291
+ self._unit = None
292
+ self._unit_id = None
293
+ self._processing_level = None
294
+ self._processing_level_id = None
295
+ self._sensor = None
296
+ super()._refresh()
297
+
298
+ def save(self):
299
+ """Save changes to this datastream to HydroServer."""
300
+
301
+ super()._save()
302
+
303
+ def delete(self):
304
+ """Delete this datastream from HydroServer."""
305
+
306
+ super()._delete()
307
+
308
+ def get_observations(
309
+ self,
310
+ start_time: datetime = None,
311
+ end_time: datetime = None,
312
+ page: int = 1,
313
+ page_size: int = 100000,
314
+ include_quality: bool = False,
315
+ fetch_all: bool = False,
316
+ ) -> DataFrame:
317
+ """Retrieve the observations for this datastream."""
318
+
319
+ return self._connection.datastreams.get_observations(
320
+ uid=self.uid,
321
+ start_time=start_time,
322
+ end_time=end_time,
323
+ page=page,
324
+ page_size=page_size,
325
+ include_quality=include_quality,
326
+ fetch_all=fetch_all,
327
+ )
328
+
329
+ def load_observations(
330
+ self,
331
+ observations: DataFrame,
332
+ ) -> None:
333
+ """Load a DataFrame of observations to the datastream."""
334
+
335
+ return self._connection.datastreams.load_observations(
336
+ uid=self.uid,
337
+ observations=observations,
338
+ )
@@ -0,0 +1,72 @@
1
+ from typing import Union, 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 ObservedPropertyFields(BaseModel):
12
+ name: str = Field(..., max_length=255)
13
+ definition: str
14
+ description: str
15
+ observed_property_type: str = Field(
16
+ ...,
17
+ max_length=255,
18
+ serialization_alias="type",
19
+ validation_alias=AliasChoices("type", AliasPath("properties", "variableType")),
20
+ )
21
+ code: str = Field(
22
+ ...,
23
+ max_length=255,
24
+ validation_alias=AliasChoices("code", AliasPath("properties", "variableCode")),
25
+ )
26
+
27
+
28
+ class ObservedProperty(HydroServerModel, ObservedPropertyFields):
29
+ def __init__(self, _connection: "HydroServer", _uid: Union[UUID, str], **data):
30
+ super().__init__(
31
+ _connection=_connection, _model_ref="observedproperties", _uid=_uid, **data
32
+ )
33
+
34
+ self._workspace_id = (
35
+ data.get("workspace_id")
36
+ or data.get("workspaceId")
37
+ or (
38
+ None
39
+ if data.get("properties", {}).get("workspace") is None
40
+ else data.get("properties", {}).get("workspace", {}).get("id")
41
+ )
42
+ )
43
+ self._workspace_id = (
44
+ str(self._workspace_id) if self._workspace_id is not None else None
45
+ )
46
+
47
+ self._workspace = None
48
+
49
+ @property
50
+ def workspace(self) -> "Workspace":
51
+ """The workspace this observed property belongs to."""
52
+
53
+ if self._workspace is None and self._workspace_id:
54
+ self._workspace = self._connection.workspaces.get(uid=self._workspace_id)
55
+
56
+ return self._workspace
57
+
58
+ def refresh(self):
59
+ """Refresh this observed property from HydroServer."""
60
+
61
+ super()._refresh()
62
+ self._workspace = None
63
+
64
+ def save(self):
65
+ """Save changes to this observed property to HydroServer."""
66
+
67
+ super()._save()
68
+
69
+ def delete(self):
70
+ """Delete this observed property from HydroServer."""
71
+
72
+ super()._delete()