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.
- hydroserverpy/__init__.py +2 -3
- hydroserverpy/api/http.py +22 -0
- hydroserverpy/api/main.py +173 -0
- hydroserverpy/api/models/__init__.py +21 -0
- hydroserverpy/api/models/base.py +74 -0
- hydroserverpy/api/models/etl/__init__.py +0 -0
- hydroserverpy/api/models/etl/data_archive.py +105 -0
- hydroserverpy/api/models/etl/data_source.py +150 -0
- hydroserverpy/api/models/etl/orchestration_configuration.py +35 -0
- hydroserverpy/api/models/etl/orchestration_system.py +78 -0
- hydroserverpy/api/models/iam/__init__.py +0 -0
- hydroserverpy/api/models/iam/account.py +12 -0
- hydroserverpy/api/models/iam/collaborator.py +34 -0
- hydroserverpy/api/models/iam/role.py +10 -0
- hydroserverpy/api/models/iam/workspace.py +238 -0
- hydroserverpy/api/models/sta/__init__.py +0 -0
- hydroserverpy/api/models/sta/datastream.py +338 -0
- hydroserverpy/api/models/sta/observed_property.py +72 -0
- hydroserverpy/api/models/sta/processing_level.py +50 -0
- hydroserverpy/api/models/sta/result_qualifier.py +49 -0
- hydroserverpy/api/models/sta/sensor.py +105 -0
- hydroserverpy/api/models/sta/thing.py +217 -0
- hydroserverpy/api/models/sta/unit.py +49 -0
- hydroserverpy/api/services/__init__.py +11 -0
- hydroserverpy/api/services/base.py +102 -0
- hydroserverpy/api/services/etl/__init__.py +0 -0
- hydroserverpy/api/services/etl/data_archive.py +196 -0
- hydroserverpy/api/services/etl/data_source.py +196 -0
- hydroserverpy/api/services/etl/orchestration_system.py +74 -0
- hydroserverpy/api/services/iam/__init__.py +0 -0
- hydroserverpy/api/services/iam/workspace.py +126 -0
- hydroserverpy/api/services/sta/__init__.py +0 -0
- hydroserverpy/api/services/sta/datastream.py +354 -0
- hydroserverpy/api/services/sta/observed_property.py +100 -0
- hydroserverpy/api/services/sta/processing_level.py +78 -0
- hydroserverpy/api/services/sta/result_qualifier.py +74 -0
- hydroserverpy/api/services/sta/sensor.py +116 -0
- hydroserverpy/api/services/sta/thing.py +188 -0
- hydroserverpy/api/services/sta/unit.py +82 -0
- hydroserverpy/etl/loaders/hydroserver_loader.py +1 -1
- hydroserverpy/etl_csv/hydroserver_etl_csv.py +49 -34
- {hydroserverpy-0.4.0.dist-info → hydroserverpy-0.5.0b2.dist-info}/METADATA +4 -3
- hydroserverpy-0.5.0b2.dist-info/RECORD +66 -0
- {hydroserverpy-0.4.0.dist-info → hydroserverpy-0.5.0b2.dist-info}/WHEEL +1 -1
- hydroserverpy/core/endpoints/__init__.py +0 -9
- hydroserverpy/core/endpoints/base.py +0 -146
- hydroserverpy/core/endpoints/data_loaders.py +0 -93
- hydroserverpy/core/endpoints/data_sources.py +0 -93
- hydroserverpy/core/endpoints/datastreams.py +0 -225
- hydroserverpy/core/endpoints/observed_properties.py +0 -111
- hydroserverpy/core/endpoints/processing_levels.py +0 -111
- hydroserverpy/core/endpoints/result_qualifiers.py +0 -111
- hydroserverpy/core/endpoints/sensors.py +0 -111
- hydroserverpy/core/endpoints/things.py +0 -261
- hydroserverpy/core/endpoints/units.py +0 -111
- hydroserverpy/core/schemas/__init__.py +0 -9
- hydroserverpy/core/schemas/base.py +0 -124
- hydroserverpy/core/schemas/data_loaders.py +0 -73
- hydroserverpy/core/schemas/data_sources.py +0 -223
- hydroserverpy/core/schemas/datastreams.py +0 -330
- hydroserverpy/core/schemas/observed_properties.py +0 -43
- hydroserverpy/core/schemas/processing_levels.py +0 -31
- hydroserverpy/core/schemas/result_qualifiers.py +0 -26
- hydroserverpy/core/schemas/sensors.py +0 -68
- hydroserverpy/core/schemas/things.py +0 -346
- hydroserverpy/core/schemas/units.py +0 -29
- hydroserverpy/core/service.py +0 -200
- hydroserverpy-0.4.0.dist-info/RECORD +0 -51
- /hydroserverpy/{core → api}/__init__.py +0 -0
- {hydroserverpy-0.4.0.dist-info → hydroserverpy-0.5.0b2.dist-info/licenses}/LICENSE +0 -0
- {hydroserverpy-0.4.0.dist-info → hydroserverpy-0.5.0b2.dist-info}/top_level.txt +0 -0
- {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()
|