hydroserverpy 1.5.1__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.
Files changed (69) hide show
  1. hydroserverpy/__init__.py +7 -0
  2. hydroserverpy/api/__init__.py +0 -0
  3. hydroserverpy/api/client.py +203 -0
  4. hydroserverpy/api/models/__init__.py +22 -0
  5. hydroserverpy/api/models/base.py +207 -0
  6. hydroserverpy/api/models/etl/__init__.py +26 -0
  7. hydroserverpy/api/models/etl/data_archive.py +77 -0
  8. hydroserverpy/api/models/etl/data_source.py +146 -0
  9. hydroserverpy/api/models/etl/etl_configuration.py +224 -0
  10. hydroserverpy/api/models/etl/extractors/__init__.py +6 -0
  11. hydroserverpy/api/models/etl/extractors/base.py +52 -0
  12. hydroserverpy/api/models/etl/extractors/ftp_extractor.py +50 -0
  13. hydroserverpy/api/models/etl/extractors/http_extractor.py +28 -0
  14. hydroserverpy/api/models/etl/extractors/local_file_extractor.py +20 -0
  15. hydroserverpy/api/models/etl/factories.py +23 -0
  16. hydroserverpy/api/models/etl/loaders/__init__.py +4 -0
  17. hydroserverpy/api/models/etl/loaders/base.py +11 -0
  18. hydroserverpy/api/models/etl/loaders/hydroserver_loader.py +98 -0
  19. hydroserverpy/api/models/etl/orchestration_configuration.py +35 -0
  20. hydroserverpy/api/models/etl/orchestration_system.py +63 -0
  21. hydroserverpy/api/models/etl/schedule.py +16 -0
  22. hydroserverpy/api/models/etl/status.py +14 -0
  23. hydroserverpy/api/models/etl/timestamp_parser.py +112 -0
  24. hydroserverpy/api/models/etl/transformers/__init__.py +5 -0
  25. hydroserverpy/api/models/etl/transformers/base.py +135 -0
  26. hydroserverpy/api/models/etl/transformers/csv_transformer.py +88 -0
  27. hydroserverpy/api/models/etl/transformers/json_transformer.py +48 -0
  28. hydroserverpy/api/models/etl/types.py +7 -0
  29. hydroserverpy/api/models/iam/__init__.py +0 -0
  30. hydroserverpy/api/models/iam/account.py +12 -0
  31. hydroserverpy/api/models/iam/apikey.py +96 -0
  32. hydroserverpy/api/models/iam/collaborator.py +70 -0
  33. hydroserverpy/api/models/iam/role.py +38 -0
  34. hydroserverpy/api/models/iam/workspace.py +297 -0
  35. hydroserverpy/api/models/sta/__init__.py +0 -0
  36. hydroserverpy/api/models/sta/datastream.py +254 -0
  37. hydroserverpy/api/models/sta/observation.py +103 -0
  38. hydroserverpy/api/models/sta/observed_property.py +37 -0
  39. hydroserverpy/api/models/sta/processing_level.py +35 -0
  40. hydroserverpy/api/models/sta/result_qualifier.py +34 -0
  41. hydroserverpy/api/models/sta/sensor.py +44 -0
  42. hydroserverpy/api/models/sta/thing.py +113 -0
  43. hydroserverpy/api/models/sta/unit.py +36 -0
  44. hydroserverpy/api/services/__init__.py +12 -0
  45. hydroserverpy/api/services/base.py +118 -0
  46. hydroserverpy/api/services/etl/__init__.py +0 -0
  47. hydroserverpy/api/services/etl/data_archive.py +166 -0
  48. hydroserverpy/api/services/etl/data_source.py +163 -0
  49. hydroserverpy/api/services/etl/orchestration_system.py +66 -0
  50. hydroserverpy/api/services/iam/__init__.py +0 -0
  51. hydroserverpy/api/services/iam/role.py +38 -0
  52. hydroserverpy/api/services/iam/workspace.py +232 -0
  53. hydroserverpy/api/services/sta/__init__.py +0 -0
  54. hydroserverpy/api/services/sta/datastream.py +296 -0
  55. hydroserverpy/api/services/sta/observed_property.py +82 -0
  56. hydroserverpy/api/services/sta/processing_level.py +72 -0
  57. hydroserverpy/api/services/sta/result_qualifier.py +64 -0
  58. hydroserverpy/api/services/sta/sensor.py +102 -0
  59. hydroserverpy/api/services/sta/thing.py +195 -0
  60. hydroserverpy/api/services/sta/unit.py +78 -0
  61. hydroserverpy/api/utils.py +22 -0
  62. hydroserverpy/quality/__init__.py +1 -0
  63. hydroserverpy/quality/service.py +405 -0
  64. hydroserverpy-1.5.1.dist-info/METADATA +66 -0
  65. hydroserverpy-1.5.1.dist-info/RECORD +69 -0
  66. hydroserverpy-1.5.1.dist-info/WHEEL +5 -0
  67. hydroserverpy-1.5.1.dist-info/licenses/LICENSE +28 -0
  68. hydroserverpy-1.5.1.dist-info/top_level.txt +1 -0
  69. hydroserverpy-1.5.1.dist-info/zip-safe +1 -0
@@ -0,0 +1,163 @@
1
+ from typing import Optional, Union, List, Literal, TYPE_CHECKING
2
+ from uuid import UUID
3
+ from datetime import datetime
4
+ from hydroserverpy.api.models import DataSource
5
+ from hydroserverpy.api.utils import normalize_uuid
6
+ from ..base import HydroServerBaseService
7
+
8
+ if TYPE_CHECKING:
9
+ from hydroserverpy import HydroServer
10
+ from hydroserverpy.api.models import Workspace, OrchestrationSystem, Datastream
11
+
12
+
13
+ class DataSourceService(HydroServerBaseService):
14
+ def __init__(self, client: "HydroServer"):
15
+ self.model = DataSource
16
+ super().__init__(client)
17
+
18
+ def list(
19
+ self,
20
+ page: int = ...,
21
+ page_size: int = ...,
22
+ order_by: List[str] = ...,
23
+ workspace: Optional[Union["Workspace", UUID, str]] = ...,
24
+ datastream: Optional[Union["Datastream", UUID, str]] = ...,
25
+ orchestration_system: Optional[Union["OrchestrationSystem", UUID, str]] = ...,
26
+ fetch_all: bool = False,
27
+ ) -> List["DataSource"]:
28
+ """Fetch a collection of data sources."""
29
+
30
+ return super().list(
31
+ page=page,
32
+ page_size=page_size,
33
+ order_by=order_by,
34
+ workspace_id=normalize_uuid(workspace),
35
+ datastream_id=normalize_uuid(datastream),
36
+ orchestration_system_id=normalize_uuid(orchestration_system),
37
+ fetch_all=fetch_all,
38
+ )
39
+
40
+ def create(
41
+ self,
42
+ name: str,
43
+ workspace: Union["Workspace", UUID, str],
44
+ orchestration_system: Union["OrchestrationSystem", UUID, str],
45
+ settings: Optional[dict] = None,
46
+ interval: Optional[int] = None,
47
+ interval_units: Optional[Literal["minutes", "hours", "days"]] = None,
48
+ crontab: Optional[str] = None,
49
+ start_time: Optional[datetime] = None,
50
+ end_time: Optional[datetime] = None,
51
+ last_run_successful: Optional[bool] = None,
52
+ last_run_message: Optional[str] = None,
53
+ last_run: Optional[datetime] = None,
54
+ next_run: Optional[datetime] = None,
55
+ paused: bool = False,
56
+ datastreams: Optional[List[Union["Datastream", UUID, str]]] = None,
57
+ ) -> "DataSource":
58
+ """Create a new data source."""
59
+
60
+ body = {
61
+ "name": name,
62
+ "workspaceId": normalize_uuid(workspace),
63
+ "orchestrationSystemId": normalize_uuid(orchestration_system),
64
+ "settings": settings,
65
+ "schedule": {
66
+ "interval": interval,
67
+ "intervalUnits": interval_units,
68
+ "crontab": crontab,
69
+ "startTime": start_time,
70
+ "endTime": end_time,
71
+ },
72
+ "status": {
73
+ "lastRunSuccessful": last_run_successful,
74
+ "lastRunMessage": last_run_message,
75
+ "lastRun": last_run,
76
+ "nextRun": next_run,
77
+ "paused": paused,
78
+ },
79
+ "datastreamIds": (
80
+ [normalize_uuid(datastream) for datastream in datastreams]
81
+ if datastreams
82
+ else []
83
+ ),
84
+ }
85
+
86
+ return super().create(**body)
87
+
88
+ def update(
89
+ self,
90
+ uid: Union[UUID, str],
91
+ name: str = ...,
92
+ orchestration_system: Union["OrchestrationSystem", UUID, str] = ...,
93
+ settings: Optional[dict] = ...,
94
+ interval: Optional[int] = ...,
95
+ interval_units: Optional[Literal["minutes", "hours", "days"]] = ...,
96
+ crontab: Optional[str] = ...,
97
+ start_time: Optional[datetime] = ...,
98
+ end_time: Optional[datetime] = ...,
99
+ last_run_successful: Optional[bool] = ...,
100
+ last_run_message: Optional[str] = ...,
101
+ last_run: Optional[datetime] = ...,
102
+ next_run: Optional[datetime] = ...,
103
+ paused: bool = ...,
104
+ ) -> "DataSource":
105
+ """Update a data source."""
106
+
107
+ status_body = {
108
+ k: v
109
+ for k, v in {
110
+ "lastRunSuccessful": last_run_successful,
111
+ "lastRunMessage": last_run_message,
112
+ "lastRun": last_run,
113
+ "nextRun": next_run,
114
+ "paused": paused,
115
+ }.items()
116
+ if v is not ...
117
+ }
118
+ status_body = status_body if status_body else ...
119
+
120
+ schedule_body = {
121
+ k: v
122
+ for k, v in {
123
+ "interval": interval,
124
+ "intervalUnits": interval_units,
125
+ "crontab": crontab,
126
+ "startTime": start_time,
127
+ "endTime": end_time,
128
+ }.items()
129
+ if v is not ...
130
+ }
131
+ schedule_body = schedule_body if schedule_body else ...
132
+
133
+ body = {
134
+ k: v
135
+ for k, v in {
136
+ "name": name,
137
+ "orchestrationSystemId": getattr(
138
+ orchestration_system, "uid", orchestration_system
139
+ ),
140
+ "settings": settings,
141
+ "schedule": schedule_body,
142
+ "status": status_body,
143
+ }.items()
144
+ if v is not ...
145
+ }
146
+
147
+ return super().update(uid=str(uid), **body)
148
+
149
+ def add_datastream(
150
+ self, uid: Union[UUID, str], datastream: Union["Datastream", UUID, str]
151
+ ) -> None:
152
+ """Add a datastream to this data source."""
153
+
154
+ path = f"/{self.client.base_route}/{self.model.get_route()}/{str(uid)}/datastreams/{normalize_uuid(datastream)}"
155
+ self.client.request("put", path)
156
+
157
+ def remove_datastream(
158
+ self, uid: Union[UUID, str], datastream: Union["Datastream", UUID, str]
159
+ ) -> None:
160
+ """Remove a datastream from this data source."""
161
+
162
+ path = f"/{self.client.base_route}/{self.model.get_route()}/{str(uid)}/datastreams/{normalize_uuid(datastream)}"
163
+ self.client.request("delete", path)
@@ -0,0 +1,66 @@
1
+ from typing import Optional, Union, List, TYPE_CHECKING
2
+ from uuid import UUID
3
+ from hydroserverpy.api.models import OrchestrationSystem
4
+ from hydroserverpy.api.utils import normalize_uuid
5
+ from ..base import HydroServerBaseService
6
+
7
+ if TYPE_CHECKING:
8
+ from hydroserverpy import HydroServer
9
+ from hydroserverpy.api.models import Workspace
10
+
11
+
12
+ class OrchestrationSystemService(HydroServerBaseService):
13
+ def __init__(self, client: "HydroServer"):
14
+ self.model = OrchestrationSystem
15
+ super().__init__(client)
16
+
17
+ def list(
18
+ self,
19
+ page: int = ...,
20
+ page_size: int = ...,
21
+ order_by: List[str] = ...,
22
+ workspace: Optional[Union["Workspace", UUID, str]] = ...,
23
+ orchestration_system_type: str = ...,
24
+ fetch_all: bool = False,
25
+ ) -> List["OrchestrationSystem"]:
26
+ """Fetch a collection of orchestration systems."""
27
+
28
+ return super().list(
29
+ page=page,
30
+ page_size=page_size,
31
+ order_by=order_by,
32
+ workspace_id=normalize_uuid(workspace),
33
+ type=orchestration_system_type,
34
+ fetch_all=fetch_all,
35
+ )
36
+
37
+ def create(
38
+ self,
39
+ name: str,
40
+ orchestration_system_type: str,
41
+ workspace: Optional[Union["Workspace", UUID, str]] = None,
42
+ ) -> "OrchestrationSystem":
43
+ """Create a new orchestration system."""
44
+
45
+ body = {
46
+ "name": name,
47
+ "type": orchestration_system_type,
48
+ "workspaceId": normalize_uuid(workspace),
49
+ }
50
+
51
+ return super().create(**body)
52
+
53
+ def update(
54
+ self,
55
+ uid: Union[UUID, str],
56
+ name: str = ...,
57
+ orchestration_system_type: str = ...,
58
+ ) -> "OrchestrationSystem":
59
+ """Update an orchestration system."""
60
+
61
+ body = {
62
+ "name": name,
63
+ "type": orchestration_system_type,
64
+ }
65
+
66
+ return super().update(uid=str(uid), **body)
File without changes
@@ -0,0 +1,38 @@
1
+ from typing import TYPE_CHECKING, Union, List, Optional
2
+ from uuid import UUID
3
+ from hydroserverpy.api.models import Role
4
+ from hydroserverpy.api.utils import normalize_uuid
5
+ from ..base import HydroServerBaseService
6
+
7
+
8
+ if TYPE_CHECKING:
9
+ from hydroserverpy import HydroServer
10
+ from hydroserverpy.api.models import Workspace
11
+
12
+
13
+ class RoleService(HydroServerBaseService):
14
+ def __init__(self, client: "HydroServer"):
15
+ self.model = Role
16
+ super().__init__(client)
17
+
18
+ def list(
19
+ self,
20
+ page: int = ...,
21
+ page_size: int = ...,
22
+ order_by: List[str] = ...,
23
+ workspace: Optional[Union["Workspace", UUID, str]] = ...,
24
+ is_user_role: bool = ...,
25
+ is_apikey_role: bool = ...,
26
+ fetch_all: bool = False,
27
+ ) -> List["Role"]:
28
+ """Fetch a collection of HydroServer roles."""
29
+
30
+ return super().list(
31
+ page=page,
32
+ page_size=page_size,
33
+ order_by=order_by,
34
+ fetch_all=fetch_all,
35
+ workspace_id=normalize_uuid(workspace),
36
+ is_user_role=is_user_role,
37
+ is_apikey_role=is_apikey_role,
38
+ )
@@ -0,0 +1,232 @@
1
+ import json
2
+ from typing import TYPE_CHECKING, Union, List, Tuple, Optional
3
+ from pydantic import EmailStr
4
+ from uuid import UUID
5
+ from datetime import datetime
6
+ from hydroserverpy.api.models import Workspace, Role, Collaborator, APIKey
7
+ from hydroserverpy.api.utils import normalize_uuid
8
+ from ..base import HydroServerBaseService
9
+
10
+
11
+ if TYPE_CHECKING:
12
+ from hydroserverpy import HydroServer
13
+
14
+
15
+ class WorkspaceService(HydroServerBaseService):
16
+ def __init__(self, client: "HydroServer"):
17
+ self.model = Workspace
18
+ super().__init__(client)
19
+
20
+ def list(
21
+ self,
22
+ page: int = ...,
23
+ page_size: int = ...,
24
+ order_by: List[str] = ...,
25
+ is_private: bool = ...,
26
+ is_associated: bool = ...,
27
+ fetch_all: bool = False,
28
+ ) -> List["Workspace"]:
29
+ """Fetch a collection of HydroServer workspaces."""
30
+
31
+ return super().list(
32
+ page=page,
33
+ page_size=page_size,
34
+ order_by=order_by,
35
+ fetch_all=fetch_all,
36
+ is_private=is_private,
37
+ is_associated=is_associated,
38
+ )
39
+
40
+ def create(self, name: str, is_private: bool, **_) -> "Workspace":
41
+ """Create a new workspace."""
42
+
43
+ return super().create(
44
+ name=name,
45
+ is_private=is_private,
46
+ )
47
+
48
+ def update(
49
+ self, uid: Union[UUID, str], name: str = ..., is_private: bool = ..., **_
50
+ ) -> "Workspace":
51
+ """Update a workspace."""
52
+
53
+ return super().update(
54
+ uid=uid,
55
+ name=name,
56
+ is_private=is_private,
57
+ )
58
+
59
+ def list_collaborators(self, uid: Union[UUID, str]) -> List["Collaborator"]:
60
+ """Get all collaborators associated with a workspace."""
61
+
62
+ path = f"/{self.client.base_route}/{self.model.get_route()}/{str(uid)}/collaborators"
63
+ response = self.client.request("get", path)
64
+
65
+ return [
66
+ Collaborator(client=self.client, uid=None, workspace_id=uid, **obj)
67
+ for obj in response.json()
68
+ ]
69
+
70
+ def add_collaborator(
71
+ self, uid: Union[UUID, str], email: EmailStr, role: Union["Role", UUID, str]
72
+ ) -> "Collaborator":
73
+ """Add a collaborator to a workspace."""
74
+
75
+ path = f"/{self.client.base_route}/{self.model.get_route()}/{str(uid)}/collaborators"
76
+ headers = {"Content-type": "application/json"}
77
+ body = {
78
+ "email": email,
79
+ "roleId": normalize_uuid(role)
80
+ }
81
+ response = self.client.request(
82
+ "post", path, headers=headers, data=json.dumps(body, default=self.default_serializer)
83
+ ).json()
84
+
85
+ return Collaborator(
86
+ client=self.client, uid=None, workspace_id=uid, **response
87
+ )
88
+
89
+ def edit_collaborator_role(
90
+ self, uid: Union[UUID, str], email: EmailStr, role: Union["Role", UUID, str]
91
+ ) -> "Collaborator":
92
+ """Edit the role of a collaborator in a workspace."""
93
+
94
+ path = f"/{self.client.base_route}/{self.model.get_route()}/{str(uid)}/collaborators"
95
+ headers = {"Content-type": "application/json"}
96
+ body = {
97
+ "email": email,
98
+ "roleId": normalize_uuid(role)
99
+ }
100
+
101
+ response = self.client.request(
102
+ "put", path, headers=headers, data=json.dumps(body, default=self.default_serializer)
103
+ )
104
+
105
+ return Collaborator(
106
+ client=self.client, uid=None, workspace_id=uid, **response.json()
107
+ )
108
+
109
+ def remove_collaborator(self, uid: Union[UUID, str], email: EmailStr) -> None:
110
+ """Remove a collaborator from a workspace."""
111
+
112
+ path = f"/{self.client.base_route}/{self.model.get_route()}/{str(uid)}/collaborators"
113
+ self.client.request("delete", path, json={"email": email})
114
+
115
+ def list_api_keys(self, uid: Union[UUID, str]) -> List["APIKey"]:
116
+ """Get all API keys associated with a workspace."""
117
+
118
+ path = f"/{self.client.base_route}/{self.model.get_route()}/{str(uid)}/api-keys"
119
+ response = self.client.request("get", path)
120
+
121
+ return [
122
+ APIKey(client=self.client, **obj)
123
+ for obj in response.json()
124
+ ]
125
+
126
+ def get_api_key(self, uid: Union[UUID, str], api_key_id: Union[UUID, str]) -> "APIKey":
127
+ """Get an API key associated with a workspace."""
128
+
129
+ path = f"/{self.client.base_route}/{self.model.get_route()}/{str(uid)}/api-keys/{api_key_id}"
130
+ response = self.client.request("get", path)
131
+
132
+ return APIKey(client=self.client, **response.json())
133
+
134
+ def create_api_key(
135
+ self,
136
+ uid: Union[UUID, str],
137
+ name: str,
138
+ role: Union["Role", UUID, str],
139
+ description: Optional[str] = None,
140
+ is_active: bool = True,
141
+ expires_at: Optional[datetime] = None
142
+ ) -> Tuple["APIKey", str]:
143
+ """Create an API key for a workspace."""
144
+
145
+ path = f"/{self.client.base_route}/{self.model.get_route()}/{str(uid)}/api-keys"
146
+ headers = {"Content-type": "application/json"}
147
+ body = {
148
+ "roleId": normalize_uuid(role),
149
+ "name": name,
150
+ "description": description,
151
+ "isActive": is_active,
152
+ "expiresAt": expires_at
153
+ }
154
+
155
+ response = self.client.request(
156
+ "post", path, headers=headers, data=json.dumps(body, default=self.default_serializer),
157
+ ).json()
158
+
159
+ return APIKey(
160
+ client=self.client, **response
161
+ ), response["key"]
162
+
163
+ def update_api_key(
164
+ self,
165
+ uid: Union[UUID, str],
166
+ api_key_id: Union[UUID, str],
167
+ role: Union["Role", UUID, str] = ...,
168
+ name: str = ...,
169
+ description: Optional[str] = ...,
170
+ is_active: bool = ...,
171
+ expires_at: Optional[datetime] = ...
172
+ ) -> "APIKey":
173
+ """Update an existing API key."""
174
+
175
+ path = f"/{self.client.base_route}/{self.model.get_route()}/{str(uid)}/api-keys/{api_key_id}"
176
+ headers = {"Content-type": "application/json"}
177
+ body = {
178
+ "roleId": normalize_uuid(role),
179
+ "name": name,
180
+ "description": description,
181
+ "isActive": is_active,
182
+ "expiresAt": expires_at
183
+ }
184
+ body = {k: v for k, v in body.items() if v is not ...}
185
+
186
+ response = self.client.request(
187
+ "patch", path, headers=headers, data=json.dumps(body, default=self.default_serializer),
188
+ ).json()
189
+
190
+ return APIKey(client=self.client, **response)
191
+
192
+ def delete_api_key(
193
+ self,
194
+ uid: Union[UUID, str],
195
+ api_key_id: Union[UUID, str]
196
+ ):
197
+ """Delete an existing API key."""
198
+
199
+ path = f"/{self.client.base_route}/{self.model.get_route()}/{str(uid)}/api-keys/{api_key_id}"
200
+ self.client.request("delete", path)
201
+
202
+ def regenerate_api_key(
203
+ self,
204
+ uid: Union[UUID, str],
205
+ api_key_id: Union[UUID, str]
206
+ ):
207
+ """Regenerate an existing API key."""
208
+
209
+ path = f"/{self.client.base_route}/{self.model.get_route()}/{str(uid)}/api-keys/{api_key_id}/regenerate"
210
+ response = self.client.request("put", path).json()
211
+
212
+ return APIKey(
213
+ client=self.client, **response
214
+ ), response["key"]
215
+
216
+ def transfer_ownership(self, uid: Union[UUID, str], email: str) -> None:
217
+ """Transfer ownership of a workspace to another HydroServer user."""
218
+
219
+ path = f"/{self.client.base_route}/{self.model.get_route()}/{str(uid)}/transfer"
220
+ self.client.request("post", path, json={"newOwner": email})
221
+
222
+ def accept_ownership_transfer(self, uid: Union[UUID, str]) -> None:
223
+ """Accept ownership transfer of a workspace."""
224
+
225
+ path = f"/{self.client.base_route}/{self.model.get_route()}/{str(uid)}/transfer"
226
+ self.client.request("put", path)
227
+
228
+ def cancel_ownership_transfer(self, uid: Union[UUID, str]) -> None:
229
+ """Cancel ownership transfer of a workspace."""
230
+
231
+ path = f"/{self.client.base_route}/{self.model.get_route()}/{str(uid)}/transfer"
232
+ self.client.request("delete", path)
File without changes