hydroserverpy 0.4.0__tar.gz → 0.5.0b1__tar.gz
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-0.4.0/src/hydroserverpy.egg-info → hydroserverpy-0.5.0b1}/PKG-INFO +4 -3
- {hydroserverpy-0.4.0 → hydroserverpy-0.5.0b1}/setup.cfg +2 -2
- {hydroserverpy-0.4.0 → hydroserverpy-0.5.0b1}/src/hydroserverpy/__init__.py +2 -3
- hydroserverpy-0.5.0b1/src/hydroserverpy/api/http.py +24 -0
- hydroserverpy-0.5.0b1/src/hydroserverpy/api/main.py +152 -0
- hydroserverpy-0.5.0b1/src/hydroserverpy/api/models/__init__.py +18 -0
- hydroserverpy-0.5.0b1/src/hydroserverpy/api/models/base.py +74 -0
- hydroserverpy-0.5.0b1/src/hydroserverpy/api/models/iam/account.py +12 -0
- hydroserverpy-0.5.0b1/src/hydroserverpy/api/models/iam/collaborator.py +34 -0
- hydroserverpy-0.5.0b1/src/hydroserverpy/api/models/iam/role.py +10 -0
- hydroserverpy-0.5.0b1/src/hydroserverpy/api/models/iam/workspace.py +203 -0
- hydroserverpy-0.5.0b1/src/hydroserverpy/api/models/sta/datastream.py +336 -0
- hydroserverpy-0.5.0b1/src/hydroserverpy/api/models/sta/observed_property.py +72 -0
- hydroserverpy-0.5.0b1/src/hydroserverpy/api/models/sta/processing_level.py +50 -0
- hydroserverpy-0.5.0b1/src/hydroserverpy/api/models/sta/result_qualifier.py +49 -0
- hydroserverpy-0.5.0b1/src/hydroserverpy/api/models/sta/sensor.py +105 -0
- hydroserverpy-0.5.0b1/src/hydroserverpy/api/models/sta/thing.py +217 -0
- hydroserverpy-0.5.0b1/src/hydroserverpy/api/models/sta/unit.py +49 -0
- hydroserverpy-0.5.0b1/src/hydroserverpy/api/services/__init__.py +8 -0
- hydroserverpy-0.5.0b1/src/hydroserverpy/api/services/base.py +92 -0
- hydroserverpy-0.5.0b1/src/hydroserverpy/api/services/iam/__init__.py +0 -0
- hydroserverpy-0.5.0b1/src/hydroserverpy/api/services/iam/workspace.py +126 -0
- hydroserverpy-0.5.0b1/src/hydroserverpy/api/services/sta/__init__.py +0 -0
- hydroserverpy-0.5.0b1/src/hydroserverpy/api/services/sta/datastream.py +354 -0
- hydroserverpy-0.5.0b1/src/hydroserverpy/api/services/sta/observed_property.py +98 -0
- hydroserverpy-0.5.0b1/src/hydroserverpy/api/services/sta/processing_level.py +78 -0
- hydroserverpy-0.5.0b1/src/hydroserverpy/api/services/sta/result_qualifier.py +74 -0
- hydroserverpy-0.5.0b1/src/hydroserverpy/api/services/sta/sensor.py +116 -0
- hydroserverpy-0.5.0b1/src/hydroserverpy/api/services/sta/thing.py +188 -0
- hydroserverpy-0.5.0b1/src/hydroserverpy/api/services/sta/unit.py +82 -0
- hydroserverpy-0.5.0b1/src/hydroserverpy/etl/extractors/__init__.py +0 -0
- hydroserverpy-0.5.0b1/src/hydroserverpy/etl/loaders/__init__.py +0 -0
- {hydroserverpy-0.4.0 → hydroserverpy-0.5.0b1}/src/hydroserverpy/etl/loaders/hydroserver_loader.py +1 -1
- hydroserverpy-0.5.0b1/src/hydroserverpy/etl/transformers/__init__.py +0 -0
- hydroserverpy-0.5.0b1/src/hydroserverpy/etl_csv/__init__.py +0 -0
- {hydroserverpy-0.4.0 → hydroserverpy-0.5.0b1}/src/hydroserverpy/etl_csv/hydroserver_etl_csv.py +1 -1
- {hydroserverpy-0.4.0 → hydroserverpy-0.5.0b1/src/hydroserverpy.egg-info}/PKG-INFO +4 -3
- hydroserverpy-0.5.0b1/src/hydroserverpy.egg-info/SOURCES.txt +64 -0
- {hydroserverpy-0.4.0 → hydroserverpy-0.5.0b1}/src/hydroserverpy.egg-info/requires.txt +1 -1
- hydroserverpy-0.4.0/src/hydroserverpy/core/endpoints/__init__.py +0 -9
- hydroserverpy-0.4.0/src/hydroserverpy/core/endpoints/base.py +0 -146
- hydroserverpy-0.4.0/src/hydroserverpy/core/endpoints/data_loaders.py +0 -93
- hydroserverpy-0.4.0/src/hydroserverpy/core/endpoints/data_sources.py +0 -93
- hydroserverpy-0.4.0/src/hydroserverpy/core/endpoints/datastreams.py +0 -225
- hydroserverpy-0.4.0/src/hydroserverpy/core/endpoints/observed_properties.py +0 -111
- hydroserverpy-0.4.0/src/hydroserverpy/core/endpoints/processing_levels.py +0 -111
- hydroserverpy-0.4.0/src/hydroserverpy/core/endpoints/result_qualifiers.py +0 -111
- hydroserverpy-0.4.0/src/hydroserverpy/core/endpoints/sensors.py +0 -111
- hydroserverpy-0.4.0/src/hydroserverpy/core/endpoints/things.py +0 -261
- hydroserverpy-0.4.0/src/hydroserverpy/core/endpoints/units.py +0 -111
- hydroserverpy-0.4.0/src/hydroserverpy/core/schemas/__init__.py +0 -9
- hydroserverpy-0.4.0/src/hydroserverpy/core/schemas/base.py +0 -124
- hydroserverpy-0.4.0/src/hydroserverpy/core/schemas/data_loaders.py +0 -73
- hydroserverpy-0.4.0/src/hydroserverpy/core/schemas/data_sources.py +0 -223
- hydroserverpy-0.4.0/src/hydroserverpy/core/schemas/datastreams.py +0 -330
- hydroserverpy-0.4.0/src/hydroserverpy/core/schemas/observed_properties.py +0 -43
- hydroserverpy-0.4.0/src/hydroserverpy/core/schemas/processing_levels.py +0 -31
- hydroserverpy-0.4.0/src/hydroserverpy/core/schemas/result_qualifiers.py +0 -26
- hydroserverpy-0.4.0/src/hydroserverpy/core/schemas/sensors.py +0 -68
- hydroserverpy-0.4.0/src/hydroserverpy/core/schemas/things.py +0 -346
- hydroserverpy-0.4.0/src/hydroserverpy/core/schemas/units.py +0 -29
- hydroserverpy-0.4.0/src/hydroserverpy/core/service.py +0 -200
- hydroserverpy-0.4.0/src/hydroserverpy.egg-info/SOURCES.txt +0 -56
- {hydroserverpy-0.4.0 → hydroserverpy-0.5.0b1}/LICENSE +0 -0
- {hydroserverpy-0.4.0 → hydroserverpy-0.5.0b1}/README.md +0 -0
- {hydroserverpy-0.4.0 → hydroserverpy-0.5.0b1}/pyproject.toml +0 -0
- {hydroserverpy-0.4.0 → hydroserverpy-0.5.0b1}/setup.py +0 -0
- {hydroserverpy-0.4.0/src/hydroserverpy/core → hydroserverpy-0.5.0b1/src/hydroserverpy/api}/__init__.py +0 -0
- {hydroserverpy-0.4.0/src/hydroserverpy/etl/extractors → hydroserverpy-0.5.0b1/src/hydroserverpy/api/models/etl}/__init__.py +0 -0
- {hydroserverpy-0.4.0/src/hydroserverpy/etl/loaders → hydroserverpy-0.5.0b1/src/hydroserverpy/api/models/iam}/__init__.py +0 -0
- {hydroserverpy-0.4.0/src/hydroserverpy/etl/transformers → hydroserverpy-0.5.0b1/src/hydroserverpy/api/models/sta}/__init__.py +0 -0
- {hydroserverpy-0.4.0/src/hydroserverpy/etl_csv → hydroserverpy-0.5.0b1/src/hydroserverpy/api/services/etl}/__init__.py +0 -0
- {hydroserverpy-0.4.0 → hydroserverpy-0.5.0b1}/src/hydroserverpy/etl/__init__.py +0 -0
- {hydroserverpy-0.4.0 → hydroserverpy-0.5.0b1}/src/hydroserverpy/etl/extractors/base.py +0 -0
- {hydroserverpy-0.4.0 → hydroserverpy-0.5.0b1}/src/hydroserverpy/etl/extractors/ftp_extractor.py +0 -0
- {hydroserverpy-0.4.0 → hydroserverpy-0.5.0b1}/src/hydroserverpy/etl/extractors/http_extractor.py +0 -0
- {hydroserverpy-0.4.0 → hydroserverpy-0.5.0b1}/src/hydroserverpy/etl/extractors/local_file_extractor.py +0 -0
- {hydroserverpy-0.4.0 → hydroserverpy-0.5.0b1}/src/hydroserverpy/etl/hydroserver_etl.py +0 -0
- {hydroserverpy-0.4.0 → hydroserverpy-0.5.0b1}/src/hydroserverpy/etl/loaders/base.py +0 -0
- {hydroserverpy-0.4.0 → hydroserverpy-0.5.0b1}/src/hydroserverpy/etl/transformers/base.py +0 -0
- {hydroserverpy-0.4.0 → hydroserverpy-0.5.0b1}/src/hydroserverpy/etl/transformers/csv_transformer.py +0 -0
- {hydroserverpy-0.4.0 → hydroserverpy-0.5.0b1}/src/hydroserverpy/etl/transformers/json_transformer.py +0 -0
- {hydroserverpy-0.4.0 → hydroserverpy-0.5.0b1}/src/hydroserverpy/etl/types.py +0 -0
- {hydroserverpy-0.4.0 → hydroserverpy-0.5.0b1}/src/hydroserverpy/etl_csv/exceptions.py +0 -0
- {hydroserverpy-0.4.0 → hydroserverpy-0.5.0b1}/src/hydroserverpy/quality/__init__.py +0 -0
- {hydroserverpy-0.4.0 → hydroserverpy-0.5.0b1}/src/hydroserverpy/quality/service.py +0 -0
- {hydroserverpy-0.4.0 → hydroserverpy-0.5.0b1}/src/hydroserverpy.egg-info/dependency_links.txt +0 -0
- {hydroserverpy-0.4.0 → hydroserverpy-0.5.0b1}/src/hydroserverpy.egg-info/top_level.txt +0 -0
- {hydroserverpy-0.4.0 → hydroserverpy-0.5.0b1}/src/hydroserverpy.egg-info/zip-safe +0 -0
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: hydroserverpy
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.0b1
|
|
4
4
|
Requires-Python: <4,>=3.9
|
|
5
5
|
License-File: LICENSE
|
|
6
6
|
Requires-Dist: requests>=2
|
|
7
7
|
Requires-Dist: pydantic>=2.6
|
|
8
|
+
Requires-Dist: pydantic[email]>=2.6
|
|
8
9
|
Requires-Dist: pandas>=2.2
|
|
9
10
|
Requires-Dist: numpy>=2.0
|
|
10
11
|
Requires-Dist: pyyaml>=5
|
|
@@ -12,7 +13,7 @@ Requires-Dist: simplejson>=3
|
|
|
12
13
|
Requires-Dist: crontab>=1
|
|
13
14
|
Requires-Dist: python-dateutil>=2.8.2
|
|
14
15
|
Requires-Dist: croniter>=2.0.1
|
|
15
|
-
Requires-Dist: country-list>=1.1.0
|
|
16
16
|
Requires-Dist: jmespath>=1.0.1
|
|
17
17
|
Provides-Extra: docs
|
|
18
18
|
Requires-Dist: sphinx_autodoc_typehints; extra == "docs"
|
|
19
|
+
Dynamic: license-file
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[metadata]
|
|
2
2
|
name = hydroserverpy
|
|
3
|
-
version = 0.
|
|
3
|
+
version = 0.5.0b1
|
|
4
4
|
|
|
5
5
|
[options]
|
|
6
6
|
package_dir =
|
|
@@ -11,6 +11,7 @@ python_requires = >=3.9, <4
|
|
|
11
11
|
install_requires =
|
|
12
12
|
requests >= 2
|
|
13
13
|
pydantic >= 2.6
|
|
14
|
+
pydantic[email] >= 2.6
|
|
14
15
|
pandas >= 2.2
|
|
15
16
|
numpy >= 2.0
|
|
16
17
|
pyyaml >= 5
|
|
@@ -18,7 +19,6 @@ install_requires =
|
|
|
18
19
|
crontab >= 1
|
|
19
20
|
python-dateutil >= 2.8.2
|
|
20
21
|
croniter >= 2.0.1
|
|
21
|
-
country-list >= 1.1.0
|
|
22
22
|
jmespath >= 1.0.1
|
|
23
23
|
|
|
24
24
|
[options.extras_require]
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
from .
|
|
2
|
-
from .quality.service import HydroServerQualityControl
|
|
1
|
+
from .api.main import HydroServer
|
|
3
2
|
from .etl.hydroserver_etl import HydroServerETL
|
|
4
|
-
|
|
3
|
+
from .quality import HydroServerQualityControl
|
|
5
4
|
|
|
6
5
|
__all__ = [
|
|
7
6
|
"HydroServer",
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from requests import HTTPError
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def raise_for_hs_status(response):
|
|
6
|
+
""""""
|
|
7
|
+
|
|
8
|
+
try:
|
|
9
|
+
response.raise_for_status()
|
|
10
|
+
except HTTPError as e:
|
|
11
|
+
try:
|
|
12
|
+
http_error_msg = (
|
|
13
|
+
f"{response.status_code} Client Error: "
|
|
14
|
+
f"{str(json.loads(response.content).get('detail'))}"
|
|
15
|
+
)
|
|
16
|
+
except (
|
|
17
|
+
ValueError,
|
|
18
|
+
TypeError,
|
|
19
|
+
):
|
|
20
|
+
http_error_msg = e
|
|
21
|
+
if 400 <= response.status_code < 500:
|
|
22
|
+
raise HTTPError(http_error_msg, response=response)
|
|
23
|
+
else:
|
|
24
|
+
raise HTTPError(str(e), response=response)
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import requests
|
|
2
|
+
from typing import Optional, Tuple
|
|
3
|
+
from hydroserverpy.api.http import raise_for_hs_status
|
|
4
|
+
from hydroserverpy.api.services import (
|
|
5
|
+
WorkspaceService,
|
|
6
|
+
ThingService,
|
|
7
|
+
ObservedPropertyService,
|
|
8
|
+
UnitService,
|
|
9
|
+
ProcessingLevelService,
|
|
10
|
+
ResultQualifierService,
|
|
11
|
+
SensorService,
|
|
12
|
+
DatastreamService,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class HydroServer:
|
|
17
|
+
def __init__(
|
|
18
|
+
self,
|
|
19
|
+
host: str,
|
|
20
|
+
email: Optional[str] = None,
|
|
21
|
+
password: Optional[str] = None,
|
|
22
|
+
apikey: Optional[str] = None,
|
|
23
|
+
):
|
|
24
|
+
self.host = host.strip("/")
|
|
25
|
+
self.auth = (
|
|
26
|
+
(
|
|
27
|
+
email or "__key__",
|
|
28
|
+
password or apikey,
|
|
29
|
+
)
|
|
30
|
+
if (email and password) or apikey
|
|
31
|
+
else None
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
self._auth_url = f"{self.host}/api/auth/app/session"
|
|
35
|
+
|
|
36
|
+
self._session = None
|
|
37
|
+
self._timeout = 60
|
|
38
|
+
self._auth_header = None
|
|
39
|
+
|
|
40
|
+
self._init_session()
|
|
41
|
+
|
|
42
|
+
def login(self, email: str, password: str) -> None:
|
|
43
|
+
"""Provide your HydroServer credentials to log in to your account."""
|
|
44
|
+
|
|
45
|
+
self._init_session(auth=(email, password))
|
|
46
|
+
|
|
47
|
+
def logout(self) -> None:
|
|
48
|
+
"""End your HydroServer session."""
|
|
49
|
+
|
|
50
|
+
self._session.delete(self._auth_url, timeout=self._timeout)
|
|
51
|
+
|
|
52
|
+
def _init_session(self, auth: Optional[Tuple[str, str]] = None) -> None:
|
|
53
|
+
if self._session is not None:
|
|
54
|
+
self.logout()
|
|
55
|
+
self._session.close()
|
|
56
|
+
|
|
57
|
+
self._session = requests.Session()
|
|
58
|
+
|
|
59
|
+
auth = auth or self.auth
|
|
60
|
+
|
|
61
|
+
if auth and auth[0] == "__key__":
|
|
62
|
+
self._session.headers.update({"key": auth[1]})
|
|
63
|
+
elif auth:
|
|
64
|
+
self._session.headers.update(
|
|
65
|
+
{"Authorization": f"Bearer {self._authenticate(auth[0], auth[1])}"}
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
def _authenticate(self, email: str, password: str) -> None:
|
|
69
|
+
response = self._session.post(
|
|
70
|
+
self._auth_url,
|
|
71
|
+
json={"email": email, "password": password},
|
|
72
|
+
timeout=self._timeout,
|
|
73
|
+
)
|
|
74
|
+
response.raise_for_status()
|
|
75
|
+
session_token = response.json().get("meta", {}).get("session_token")
|
|
76
|
+
|
|
77
|
+
if not session_token:
|
|
78
|
+
raise ValueError("Authentication failed: No access token returned.")
|
|
79
|
+
|
|
80
|
+
return session_token
|
|
81
|
+
|
|
82
|
+
def request(self, method, path, *args, **kwargs) -> requests.Response:
|
|
83
|
+
"""Sends a request to HydroServer's API."""
|
|
84
|
+
|
|
85
|
+
for attempt in range(2):
|
|
86
|
+
try:
|
|
87
|
+
response = getattr(self._session, method)(
|
|
88
|
+
f"{self.host}/{path.strip('/')}",
|
|
89
|
+
timeout=self._timeout,
|
|
90
|
+
*args,
|
|
91
|
+
**kwargs,
|
|
92
|
+
)
|
|
93
|
+
raise_for_hs_status(response)
|
|
94
|
+
except (
|
|
95
|
+
requests.exceptions.HTTPError,
|
|
96
|
+
requests.exceptions.ConnectionError,
|
|
97
|
+
) as e:
|
|
98
|
+
if attempt == 0:
|
|
99
|
+
self._init_session()
|
|
100
|
+
continue
|
|
101
|
+
else:
|
|
102
|
+
raise e
|
|
103
|
+
|
|
104
|
+
return response
|
|
105
|
+
|
|
106
|
+
@property
|
|
107
|
+
def workspaces(self):
|
|
108
|
+
"""Utilities for managing HydroServer workspaces."""
|
|
109
|
+
|
|
110
|
+
return WorkspaceService(self)
|
|
111
|
+
|
|
112
|
+
@property
|
|
113
|
+
def things(self):
|
|
114
|
+
"""Utilities for managing HydroServer things."""
|
|
115
|
+
|
|
116
|
+
return ThingService(self)
|
|
117
|
+
|
|
118
|
+
@property
|
|
119
|
+
def observedproperties(self):
|
|
120
|
+
"""Utilities for managing HydroServer observed properties."""
|
|
121
|
+
|
|
122
|
+
return ObservedPropertyService(self)
|
|
123
|
+
|
|
124
|
+
@property
|
|
125
|
+
def units(self):
|
|
126
|
+
"""Utilities for managing HydroServer units."""
|
|
127
|
+
|
|
128
|
+
return UnitService(self)
|
|
129
|
+
|
|
130
|
+
@property
|
|
131
|
+
def processinglevels(self):
|
|
132
|
+
"""Utilities for managing HydroServer processing levels."""
|
|
133
|
+
|
|
134
|
+
return ProcessingLevelService(self)
|
|
135
|
+
|
|
136
|
+
@property
|
|
137
|
+
def resultqualifiers(self):
|
|
138
|
+
"""Utilities for managing HydroServer result qualifiers."""
|
|
139
|
+
|
|
140
|
+
return ResultQualifierService(self)
|
|
141
|
+
|
|
142
|
+
@property
|
|
143
|
+
def sensors(self):
|
|
144
|
+
"""Utilities for managing HydroServer sensors."""
|
|
145
|
+
|
|
146
|
+
return SensorService(self)
|
|
147
|
+
|
|
148
|
+
@property
|
|
149
|
+
def datastreams(self):
|
|
150
|
+
"""Utilities for managing HydroServer datastreams."""
|
|
151
|
+
|
|
152
|
+
return DatastreamService(self)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from .iam.account import Account
|
|
2
|
+
from .iam.workspace import Workspace
|
|
3
|
+
from .iam.role import Role
|
|
4
|
+
from .iam.collaborator import Collaborator
|
|
5
|
+
from .iam.account import Account
|
|
6
|
+
from .sta.datastream import Datastream
|
|
7
|
+
from .sta.observed_property import ObservedProperty
|
|
8
|
+
from .sta.processing_level import ProcessingLevel
|
|
9
|
+
from .sta.result_qualifier import ResultQualifier
|
|
10
|
+
from .sta.sensor import Sensor
|
|
11
|
+
from .sta.thing import Thing
|
|
12
|
+
from .sta.unit import Unit
|
|
13
|
+
|
|
14
|
+
Workspace.model_rebuild()
|
|
15
|
+
Role.model_rebuild()
|
|
16
|
+
Collaborator.model_rebuild()
|
|
17
|
+
|
|
18
|
+
Unit.model_rebuild()
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
from uuid import UUID
|
|
3
|
+
from pydantic import BaseModel, PrivateAttr, ConfigDict, computed_field
|
|
4
|
+
from pydantic.alias_generators import to_camel
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class HydroServerBaseModel(BaseModel):
|
|
8
|
+
_uid: Optional[UUID] = PrivateAttr()
|
|
9
|
+
|
|
10
|
+
def __init__(self, _uid: Optional[UUID] = None, **data):
|
|
11
|
+
super().__init__(**data)
|
|
12
|
+
self._uid = _uid
|
|
13
|
+
|
|
14
|
+
@computed_field
|
|
15
|
+
@property
|
|
16
|
+
def uid(self) -> Optional[UUID]:
|
|
17
|
+
"""The unique identifier for this resource."""
|
|
18
|
+
|
|
19
|
+
return self._uid
|
|
20
|
+
|
|
21
|
+
model_config = ConfigDict(
|
|
22
|
+
validate_assignment=True,
|
|
23
|
+
populate_by_name=True,
|
|
24
|
+
str_strip_whitespace=True,
|
|
25
|
+
alias_generator=to_camel,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class HydroServerModel(HydroServerBaseModel):
|
|
30
|
+
_model_ref: str = PrivateAttr()
|
|
31
|
+
_original_data: Optional[dict] = PrivateAttr()
|
|
32
|
+
|
|
33
|
+
def __init__(self, _connection, _model_ref, _uid: Optional[UUID] = None, **data):
|
|
34
|
+
if isinstance(_uid, str):
|
|
35
|
+
_uid = UUID(_uid)
|
|
36
|
+
|
|
37
|
+
super().__init__(_uid=_uid, **data)
|
|
38
|
+
|
|
39
|
+
self._connection = _connection
|
|
40
|
+
self._model_ref = _model_ref
|
|
41
|
+
self._original_data = self.dict(by_alias=False).copy()
|
|
42
|
+
|
|
43
|
+
@property
|
|
44
|
+
def _patch_data(self) -> dict:
|
|
45
|
+
return {
|
|
46
|
+
key: getattr(self, key)
|
|
47
|
+
for key, value in self._original_data.items()
|
|
48
|
+
if hasattr(self, key) and getattr(self, key) != value
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
def _refresh(self) -> None:
|
|
52
|
+
"""Refresh this resource from HydroServer."""
|
|
53
|
+
|
|
54
|
+
self._original_data = (
|
|
55
|
+
getattr(self._connection, self._model_ref)
|
|
56
|
+
.get(uid=self.uid)
|
|
57
|
+
.model_dump(exclude=["uid"])
|
|
58
|
+
)
|
|
59
|
+
self.__dict__.update(self._original_data)
|
|
60
|
+
|
|
61
|
+
def _save(self) -> None:
|
|
62
|
+
if self._patch_data:
|
|
63
|
+
entity = getattr(self._connection, self._model_ref).update(
|
|
64
|
+
uid=self.uid, **self._patch_data
|
|
65
|
+
)
|
|
66
|
+
self._original_data = entity.dict(by_alias=False, exclude=["uid"])
|
|
67
|
+
self.__dict__.update(self._original_data)
|
|
68
|
+
|
|
69
|
+
def _delete(self) -> None:
|
|
70
|
+
if not self._uid:
|
|
71
|
+
raise AttributeError("This resource cannot be deleted: UID is not set.")
|
|
72
|
+
|
|
73
|
+
getattr(self._connection, self._model_ref).delete(uid=self._uid)
|
|
74
|
+
self._uid = None
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
from pydantic import BaseModel, Field, EmailStr
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class Account(BaseModel):
|
|
6
|
+
name: str = Field(..., max_length=255)
|
|
7
|
+
email: EmailStr
|
|
8
|
+
organization_name: Optional[str] = None
|
|
9
|
+
phone: Optional[str] = Field(None, max_length=15)
|
|
10
|
+
address: Optional[str] = Field(None, max_length=255)
|
|
11
|
+
link: Optional[str] = Field(None, max_length=2000)
|
|
12
|
+
user_type: str = Field(..., max_length=255, alias="type")
|
|
@@ -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,203 @@
|
|
|
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
|
+
)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class WorkspaceFields(BaseModel):
|
|
23
|
+
name: str = Field(..., max_length=255)
|
|
24
|
+
is_private: bool
|
|
25
|
+
owner: "Account" = Field(..., json_schema_extra={"read_only": True})
|
|
26
|
+
collaborator_role: Optional["Role"] = None
|
|
27
|
+
pending_transfer_to: Optional["Account"] = None
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class Workspace(HydroServerModel, WorkspaceFields):
|
|
31
|
+
def __init__(self, _connection: "HydroServer", _uid: Union[UUID, str], **data):
|
|
32
|
+
super().__init__(
|
|
33
|
+
_connection=_connection, _model_ref="workspaces", _uid=_uid, **data
|
|
34
|
+
)
|
|
35
|
+
self._roles = None
|
|
36
|
+
self._collaborators = None
|
|
37
|
+
self._things = None
|
|
38
|
+
self._observedproperties = None
|
|
39
|
+
self._processinglevels = None
|
|
40
|
+
self._resultqualifiers = None
|
|
41
|
+
self._units = None
|
|
42
|
+
self._sensors = None
|
|
43
|
+
self._datastreams = None
|
|
44
|
+
|
|
45
|
+
@property
|
|
46
|
+
def roles(self) -> List["Role"]:
|
|
47
|
+
"""The roles that can be assigned for this workspace."""
|
|
48
|
+
|
|
49
|
+
if self._roles is None:
|
|
50
|
+
self._roles = self._connection.workspaces.list_roles(uid=self.uid)
|
|
51
|
+
|
|
52
|
+
return self._roles
|
|
53
|
+
|
|
54
|
+
@property
|
|
55
|
+
def collaborators(self) -> List["Collaborator"]:
|
|
56
|
+
"""The collaborators associated with this workspace."""
|
|
57
|
+
|
|
58
|
+
if self._collaborators is None:
|
|
59
|
+
self._collaborators = self._connection.workspaces.list_collaborators(
|
|
60
|
+
uid=self.uid
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
return self._collaborators
|
|
64
|
+
|
|
65
|
+
@property
|
|
66
|
+
def things(self) -> List["Thing"]:
|
|
67
|
+
"""The things associated with this workspace."""
|
|
68
|
+
|
|
69
|
+
if self._things is None:
|
|
70
|
+
self._things = self._connection.things.list(workspace=self.uid)
|
|
71
|
+
|
|
72
|
+
return self._things
|
|
73
|
+
|
|
74
|
+
@property
|
|
75
|
+
def observedproperties(self) -> List["ObservedProperty"]:
|
|
76
|
+
"""The observed properties associated with this workspace."""
|
|
77
|
+
|
|
78
|
+
if self._observedproperties is None:
|
|
79
|
+
self._observedproperties = self._connection.observedproperties.list(
|
|
80
|
+
workspace=self.uid
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
return self._observedproperties
|
|
84
|
+
|
|
85
|
+
@property
|
|
86
|
+
def processinglevels(self) -> List["ProcessingLevel"]:
|
|
87
|
+
"""The processing levels associated with this workspace."""
|
|
88
|
+
|
|
89
|
+
if self._processinglevels is None:
|
|
90
|
+
self._processinglevels = self._connection.processinglevels.list(
|
|
91
|
+
workspace=self.uid
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
return self._processinglevels
|
|
95
|
+
|
|
96
|
+
@property
|
|
97
|
+
def resultqualifiers(self) -> List["ResultQualifier"]:
|
|
98
|
+
"""The result qualifiers associated with this workspace."""
|
|
99
|
+
|
|
100
|
+
if self._resultqualifiers is None:
|
|
101
|
+
self._resultqualifiers = self._connection.resultqualifiers.list(
|
|
102
|
+
workspace=self.uid
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
return self._resultqualifiers
|
|
106
|
+
|
|
107
|
+
@property
|
|
108
|
+
def units(self) -> List["Unit"]:
|
|
109
|
+
"""The units associated with this workspace."""
|
|
110
|
+
|
|
111
|
+
if self._units is None:
|
|
112
|
+
self._units = self._connection.units.list(workspace=self.uid)
|
|
113
|
+
|
|
114
|
+
return self._units
|
|
115
|
+
|
|
116
|
+
@property
|
|
117
|
+
def sensors(self) -> List["Sensor"]:
|
|
118
|
+
"""The sensors associated with this workspace."""
|
|
119
|
+
|
|
120
|
+
if self._sensors is None:
|
|
121
|
+
self._sensors = self._connection.sensors.list(workspace=self.uid)
|
|
122
|
+
|
|
123
|
+
return self._sensors
|
|
124
|
+
|
|
125
|
+
@property
|
|
126
|
+
def datastreams(self) -> List["Datastream"]:
|
|
127
|
+
"""The datastreams associated with this workspace."""
|
|
128
|
+
|
|
129
|
+
if self._datastreams is None:
|
|
130
|
+
self._datastreams = self._connection.datastreams.list(workspace=self.uid)
|
|
131
|
+
|
|
132
|
+
return self._datastreams
|
|
133
|
+
|
|
134
|
+
def refresh(self) -> None:
|
|
135
|
+
"""Refresh the workspace details from HydroServer."""
|
|
136
|
+
|
|
137
|
+
self._roles = None
|
|
138
|
+
self._collaborators = None
|
|
139
|
+
self._things = None
|
|
140
|
+
self._observedproperties = None
|
|
141
|
+
self._processinglevels = None
|
|
142
|
+
self._units = None
|
|
143
|
+
self._sensors = None
|
|
144
|
+
self._datastreams = None
|
|
145
|
+
super()._refresh()
|
|
146
|
+
|
|
147
|
+
def save(self):
|
|
148
|
+
"""Save changes to this workspace to HydroServer."""
|
|
149
|
+
|
|
150
|
+
super()._save()
|
|
151
|
+
|
|
152
|
+
def delete(self):
|
|
153
|
+
"""Delete this workspace from HydroServer."""
|
|
154
|
+
|
|
155
|
+
super()._delete()
|
|
156
|
+
|
|
157
|
+
def add_collaborator(
|
|
158
|
+
self, email: EmailStr, role: Union["Role", UUID, str]
|
|
159
|
+
) -> "Collaborator":
|
|
160
|
+
"""Add a new collaborator to the workspace."""
|
|
161
|
+
|
|
162
|
+
response = self._connection.workspaces.add_collaborator(
|
|
163
|
+
uid=self.uid, email=email, role=role
|
|
164
|
+
)
|
|
165
|
+
self._collaborators = None
|
|
166
|
+
|
|
167
|
+
return response
|
|
168
|
+
|
|
169
|
+
def edit_collaborator_role(
|
|
170
|
+
self, email: EmailStr, role: Union["Role", UUID, str]
|
|
171
|
+
) -> "Collaborator":
|
|
172
|
+
"""Edit a collaborator's role in this workspace."""
|
|
173
|
+
|
|
174
|
+
response = self._connection.workspaces.edit_collaborator_role(
|
|
175
|
+
uid=self.uid, email=email, role=role
|
|
176
|
+
)
|
|
177
|
+
self._collaborators = None
|
|
178
|
+
|
|
179
|
+
return response
|
|
180
|
+
|
|
181
|
+
def remove_collaborator(self, email: EmailStr) -> None:
|
|
182
|
+
"""Remove a collaborator from the workspace."""
|
|
183
|
+
|
|
184
|
+
self._connection.workspaces.remove_collaborator(uid=self.uid, email=email)
|
|
185
|
+
self._collaborators = None
|
|
186
|
+
|
|
187
|
+
def transfer_ownership(self, email: EmailStr) -> None:
|
|
188
|
+
"""Transfer ownership of this workspace to another HydroServer user."""
|
|
189
|
+
|
|
190
|
+
self._connection.workspaces.transfer_ownership(uid=self.uid, email=email)
|
|
191
|
+
self.refresh()
|
|
192
|
+
|
|
193
|
+
def accept_ownership_transfer(self) -> None:
|
|
194
|
+
"""Accept ownership transfer of this workspace."""
|
|
195
|
+
|
|
196
|
+
self._connection.workspaces.accept_ownership_transfer(uid=self.uid)
|
|
197
|
+
self.refresh()
|
|
198
|
+
|
|
199
|
+
def cancel_ownership_transfer(self) -> None:
|
|
200
|
+
"""Cancel ownership transfer of this workspace."""
|
|
201
|
+
|
|
202
|
+
self._connection.workspaces.cancel_ownership_transfer(uid=self.uid)
|
|
203
|
+
self.refresh()
|