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
hydroserverpy/__init__.py CHANGED
@@ -1,7 +1,6 @@
1
- from .core.service import HydroServer
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,22 @@
1
+ import json
2
+ from requests import HTTPError
3
+
4
+
5
+ def raise_for_hs_status(response):
6
+ try:
7
+ response.raise_for_status()
8
+ except HTTPError as e:
9
+ try:
10
+ http_error_msg = (
11
+ f"{response.status_code} Client Error: "
12
+ f"{str(json.loads(response.content).get('detail'))}"
13
+ )
14
+ except (
15
+ ValueError,
16
+ TypeError,
17
+ ):
18
+ http_error_msg = e
19
+ if 400 <= response.status_code < 500:
20
+ raise HTTPError(http_error_msg, response=response)
21
+ else:
22
+ raise HTTPError(str(e), response=response)
@@ -0,0 +1,173 @@
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
+ OrchestrationSystemService,
14
+ DataSourceService,
15
+ DataArchiveService,
16
+ )
17
+
18
+
19
+ class HydroServer:
20
+ def __init__(
21
+ self,
22
+ host: str,
23
+ email: Optional[str] = None,
24
+ password: Optional[str] = None,
25
+ apikey: Optional[str] = None,
26
+ ):
27
+ self.host = host.strip("/")
28
+ self.auth = (
29
+ (
30
+ email or "__key__",
31
+ password or apikey,
32
+ )
33
+ if (email and password) or apikey
34
+ else None
35
+ )
36
+
37
+ self._auth_url = f"{self.host}/api/auth/app/session"
38
+
39
+ self._session = None
40
+ self._timeout = 60
41
+ self._auth_header = None
42
+
43
+ self._init_session()
44
+
45
+ def login(self, email: str, password: str) -> None:
46
+ """Provide your HydroServer credentials to log in to your account."""
47
+
48
+ self._init_session(auth=(email, password))
49
+
50
+ def logout(self) -> None:
51
+ """End your HydroServer session."""
52
+
53
+ self._session.delete(self._auth_url, timeout=self._timeout)
54
+
55
+ def _init_session(self, auth: Optional[Tuple[str, str]] = None) -> None:
56
+ if self._session is not None:
57
+ self.logout()
58
+ self._session.close()
59
+
60
+ self._session = requests.Session()
61
+
62
+ auth = auth or self.auth
63
+
64
+ if auth and auth[0] == "__key__":
65
+ self._session.headers.update({"key": auth[1]})
66
+ elif auth:
67
+ self._session.headers.update(
68
+ {"Authorization": f"Bearer {self._authenticate(auth[0], auth[1])}"}
69
+ )
70
+
71
+ def _authenticate(self, email: str, password: str) -> None:
72
+ response = self._session.post(
73
+ self._auth_url,
74
+ json={"email": email, "password": password},
75
+ timeout=self._timeout,
76
+ )
77
+ response.raise_for_status()
78
+ session_token = response.json().get("meta", {}).get("session_token")
79
+
80
+ if not session_token:
81
+ raise ValueError("Authentication failed: No access token returned.")
82
+
83
+ return session_token
84
+
85
+ def request(self, method, path, *args, **kwargs) -> requests.Response:
86
+ """Sends a request to HydroServer's API."""
87
+
88
+ for attempt in range(2):
89
+ try:
90
+ response = getattr(self._session, method)(
91
+ f"{self.host}/{path.strip('/')}",
92
+ timeout=self._timeout,
93
+ *args,
94
+ **kwargs,
95
+ )
96
+ raise_for_hs_status(response)
97
+ except (
98
+ requests.exceptions.HTTPError,
99
+ requests.exceptions.ConnectionError,
100
+ ) as e:
101
+ if attempt == 0:
102
+ self._init_session()
103
+ continue
104
+ else:
105
+ raise e
106
+
107
+ return response
108
+
109
+ @property
110
+ def workspaces(self):
111
+ """Utilities for managing HydroServer workspaces."""
112
+
113
+ return WorkspaceService(self)
114
+
115
+ @property
116
+ def things(self):
117
+ """Utilities for managing HydroServer things."""
118
+
119
+ return ThingService(self)
120
+
121
+ @property
122
+ def observedproperties(self):
123
+ """Utilities for managing HydroServer observed properties."""
124
+
125
+ return ObservedPropertyService(self)
126
+
127
+ @property
128
+ def units(self):
129
+ """Utilities for managing HydroServer units."""
130
+
131
+ return UnitService(self)
132
+
133
+ @property
134
+ def processinglevels(self):
135
+ """Utilities for managing HydroServer processing levels."""
136
+
137
+ return ProcessingLevelService(self)
138
+
139
+ @property
140
+ def resultqualifiers(self):
141
+ """Utilities for managing HydroServer result qualifiers."""
142
+
143
+ return ResultQualifierService(self)
144
+
145
+ @property
146
+ def sensors(self):
147
+ """Utilities for managing HydroServer sensors."""
148
+
149
+ return SensorService(self)
150
+
151
+ @property
152
+ def datastreams(self):
153
+ """Utilities for managing HydroServer datastreams."""
154
+
155
+ return DatastreamService(self)
156
+
157
+ @property
158
+ def orchestrationsystems(self):
159
+ """Utilities for managing HydroServer orchestration systems."""
160
+
161
+ return OrchestrationSystemService(self)
162
+
163
+ @property
164
+ def datasources(self):
165
+ """Utilities for managing HydroServer data sources."""
166
+
167
+ return DataSourceService(self)
168
+
169
+ @property
170
+ def dataarchives(self):
171
+ """Utilities for managing HydroServer data archives."""
172
+
173
+ return DataArchiveService(self)
@@ -0,0 +1,21 @@
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
+ from .etl.orchestration_system import OrchestrationSystem
14
+ from .etl.data_source import DataSource
15
+ from .etl.data_archive import DataArchive
16
+
17
+ Workspace.model_rebuild()
18
+ Role.model_rebuild()
19
+ Collaborator.model_rebuild()
20
+
21
+ 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
File without changes
@@ -0,0 +1,105 @@
1
+ from typing import Union, Optional, TYPE_CHECKING, List
2
+ from uuid import UUID
3
+ from pydantic import BaseModel, Field
4
+ from .orchestration_system import OrchestrationSystem
5
+ from .orchestration_configuration import OrchestrationConfigurationFields
6
+ from ..sta.datastream import Datastream
7
+ from ..base import HydroServerModel
8
+
9
+ if TYPE_CHECKING:
10
+ from hydroserverpy import HydroServer
11
+ from hydroserverpy.api.models import Workspace
12
+
13
+
14
+ class DataArchiveFields(BaseModel):
15
+ name: str = Field(..., max_length=255)
16
+ settings: Optional[dict] = None
17
+
18
+
19
+ class DataArchive(
20
+ HydroServerModel, DataArchiveFields, OrchestrationConfigurationFields
21
+ ):
22
+ def __init__(self, _connection: "HydroServer", _uid: Union[UUID, str], **data):
23
+ super().__init__(
24
+ _connection=_connection, _model_ref="dataarchives", _uid=_uid, **data
25
+ )
26
+
27
+ self._workspace_id = str(data.get("workspace_id") or data["workspaceId"])
28
+ self._orchestration_system_id = str(
29
+ data.get("orchestration_system_id") or data["orchestrationSystem"]["id"]
30
+ )
31
+
32
+ self._workspace = None
33
+
34
+ if data.get("orchestrationSystem"):
35
+ self._orchestration_system = OrchestrationSystem(
36
+ _connection=_connection,
37
+ _uid=self._orchestration_system_id,
38
+ **data["orchestrationSystem"]
39
+ )
40
+ else:
41
+ self._orchestration_system = None
42
+
43
+ if data.get("datastreams"):
44
+ self._datastreams = [
45
+ Datastream(_connection=_connection, _uid=datastream["id"], **datastream)
46
+ for datastream in data["datastreams"]
47
+ ]
48
+ else:
49
+ self._datastreams = []
50
+
51
+ @property
52
+ def workspace(self) -> "Workspace":
53
+ """The workspace this data archive belongs to."""
54
+
55
+ if self._workspace is None and self._workspace_id:
56
+ self._workspace = self._connection.workspaces.get(uid=self._workspace_id)
57
+
58
+ return self._workspace
59
+
60
+ @property
61
+ def orchestration_system(self) -> "OrchestrationSystem":
62
+ """The orchestration system that manages this data archive."""
63
+
64
+ if self._orchestration_system is None and self._orchestration_system_id:
65
+ self._orchestration_system = self._connection.orchestration_systems.get(
66
+ uid=self._orchestration_system_id
67
+ )
68
+
69
+ return self._orchestration_system
70
+
71
+ @property
72
+ def datastreams(self) -> List["Datastream"]:
73
+ """The datastreams this data archive provides data for."""
74
+
75
+ return self._datastreams
76
+
77
+ def refresh(self):
78
+ """Refresh this data archive from HydroServer."""
79
+
80
+ super()._refresh()
81
+ self._workspace = None
82
+
83
+ def save(self):
84
+ """Save changes to this data archive to HydroServer."""
85
+
86
+ super()._save()
87
+
88
+ def delete(self):
89
+ """Delete this data archive from HydroServer."""
90
+
91
+ super()._delete()
92
+
93
+ def add_datastream(self, datastream: Union["Datastream", UUID, str]):
94
+ """Add a datastream to this data archive."""
95
+
96
+ self._connection.dataarchives.add_datastream(
97
+ uid=self.uid, datastream=datastream
98
+ )
99
+
100
+ def remove_datastream(self, datastream: Union["Datastream", UUID, str]):
101
+ """Remove a datastream from this data archive."""
102
+
103
+ self._connection.dataarchives.remove_datastream(
104
+ uid=self.uid, datastream=datastream
105
+ )
@@ -0,0 +1,150 @@
1
+ import tempfile
2
+ from typing import Union, List, Optional, TYPE_CHECKING
3
+ from uuid import UUID
4
+ from pydantic import BaseModel, Field
5
+ from urllib.request import urlopen
6
+ from hydroserverpy.etl_csv.hydroserver_etl_csv import HydroServerETLCSV
7
+ from .orchestration_system import OrchestrationSystem
8
+ from .orchestration_configuration import OrchestrationConfigurationFields
9
+ from ..sta.datastream import Datastream
10
+ from ..base import HydroServerModel
11
+
12
+ if TYPE_CHECKING:
13
+ from hydroserverpy import HydroServer
14
+ from hydroserverpy.api.models import Workspace
15
+
16
+
17
+ class DataSourceFields(BaseModel):
18
+ name: str = Field(..., max_length=255)
19
+ settings: Optional[dict] = None
20
+
21
+
22
+ class DataSource(HydroServerModel, DataSourceFields, OrchestrationConfigurationFields):
23
+ def __init__(self, _connection: "HydroServer", _uid: Union[UUID, str], **data):
24
+ super().__init__(
25
+ _connection=_connection, _model_ref="datasources", _uid=_uid, **data
26
+ )
27
+
28
+ self._workspace_id = str(data.get("workspace_id") or data["workspaceId"])
29
+ self._orchestration_system_id = str(
30
+ data.get("orchestration_system_id") or data["orchestrationSystem"]["id"]
31
+ )
32
+
33
+ self._workspace = None
34
+
35
+ if data.get("orchestrationSystem"):
36
+ self._orchestration_system = OrchestrationSystem(
37
+ _connection=_connection,
38
+ _uid=self._orchestration_system_id,
39
+ **data["orchestrationSystem"]
40
+ )
41
+ else:
42
+ self._orchestration_system = None
43
+
44
+ if data.get("datastreams"):
45
+ self._datastreams = [
46
+ Datastream(_connection=_connection, _uid=datastream["id"], **datastream)
47
+ for datastream in data["datastreams"]
48
+ ]
49
+ else:
50
+ self._datastreams = []
51
+
52
+ @property
53
+ def workspace(self) -> "Workspace":
54
+ """The workspace this data source belongs to."""
55
+
56
+ if self._workspace is None and self._workspace_id:
57
+ self._workspace = self._connection.workspaces.get(uid=self._workspace_id)
58
+
59
+ return self._workspace
60
+
61
+ @property
62
+ def orchestration_system(self) -> "OrchestrationSystem":
63
+ """The orchestration system that manages this data source."""
64
+
65
+ if self._orchestration_system is None and self._orchestration_system_id:
66
+ self._orchestration_system = self._connection.orchestration_systems.get(
67
+ uid=self._orchestration_system_id
68
+ )
69
+
70
+ return self._orchestration_system
71
+
72
+ @orchestration_system.setter
73
+ def orchestration_system(
74
+ self, orchestration_system: Union["OrchestrationSystem", UUID, str]
75
+ ):
76
+ if not orchestration_system:
77
+ raise ValueError("Orchestration system of data source cannot be None.")
78
+ if str(getattr(orchestration_system, "uid", orchestration_system)) != str(
79
+ self.orchestration_system.uid
80
+ ):
81
+ self._orchestration_system = self._connection.orchestrationsystems.get(
82
+ uid=str(getattr(orchestration_system, "uid", orchestration_system))
83
+ )
84
+
85
+ @property
86
+ def datastreams(self) -> List["Datastream"]:
87
+ """The datastreams this data source provides data for."""
88
+
89
+ if self._datastreams is None:
90
+ data_source = self._connection.datasources.get(uid=self.uid)
91
+ self._datastreams = data_source.datastreams
92
+
93
+ return self._datastreams
94
+
95
+ def refresh(self):
96
+ """Refresh this data source from HydroServer."""
97
+
98
+ super()._refresh()
99
+ self._workspace = None
100
+ self._datastreams = None
101
+
102
+ def save(self):
103
+ """Save changes to this data source to HydroServer."""
104
+
105
+ super()._save()
106
+
107
+ def delete(self):
108
+ """Delete this data source from HydroServer."""
109
+
110
+ super()._delete()
111
+
112
+ def add_datastream(self, datastream: Union["Datastream", UUID, str]):
113
+ """Add a datastream to this data source."""
114
+
115
+ self._connection.datasources.add_datastream(uid=self.uid, datastream=datastream)
116
+
117
+ def remove_datastream(self, datastream: Union["Datastream", UUID, str]):
118
+ """Remove a datastream from this data source."""
119
+
120
+ self._connection.datasources.remove_datastream(
121
+ uid=self.uid, datastream=datastream
122
+ )
123
+
124
+ # TODO: Replace with ETL module.
125
+ def load_data(self):
126
+ """Load data for this data source."""
127
+
128
+ if self.paused is True:
129
+ return
130
+
131
+ if self.settings["extractor"]["type"] == "local":
132
+ with open(self.settings["extractor"]["path"]) as data_file:
133
+ loader = HydroServerETLCSV(
134
+ self._connection, data_file=data_file, data_source=self
135
+ )
136
+ loader.run()
137
+ elif self.settings["extractor"]["type"] == "HTTP":
138
+ with tempfile.NamedTemporaryFile(mode="w") as temp_file:
139
+ with urlopen(self.settings["extractor"]["urlTemplate"]) as response:
140
+ chunk_size = 1024 * 1024 * 10 # Use a 10mb chunk size.
141
+ while True:
142
+ chunk = response.read(chunk_size)
143
+ if not chunk:
144
+ break
145
+ temp_file.write(chunk)
146
+ temp_file.seek(0)
147
+ loader = HydroServerETLCSV(
148
+ self._connection, data_file=temp_file, data_source=self
149
+ )
150
+ loader.run()
@@ -0,0 +1,35 @@
1
+ from pydantic import AliasPath
2
+ from typing import Optional, Literal
3
+ from datetime import datetime
4
+ from pydantic import BaseModel, Field
5
+
6
+
7
+ class OrchestrationConfigurationFields(BaseModel):
8
+ interval: Optional[int] = Field(
9
+ None, gt=0, validation_alias=AliasPath("schedule", "interval")
10
+ )
11
+ interval_units: Optional[Literal["minutes", "hours", "days"]] = Field(
12
+ None, validation_alias=AliasPath("schedule", "intervalUnits")
13
+ )
14
+ crontab: Optional[str] = Field(
15
+ None, max_length=255, validation_alias=AliasPath("schedule", "crontab")
16
+ )
17
+ start_time: Optional[datetime] = Field(
18
+ None, validation_alias=AliasPath("schedule", "startTime")
19
+ )
20
+ end_time: Optional[datetime] = Field(
21
+ None, validation_alias=AliasPath("schedule", "endTime")
22
+ )
23
+ last_run_successful: Optional[bool] = Field(
24
+ None, validation_alias=AliasPath("status", "lastRunSuccessful")
25
+ )
26
+ last_run_message: Optional[str] = Field(
27
+ None, max_length=255, validation_alias=AliasPath("status", "lastRunMessage")
28
+ )
29
+ last_run: Optional[datetime] = Field(
30
+ None, validation_alias=AliasPath("status", "lastRun")
31
+ )
32
+ next_run: Optional[datetime] = Field(
33
+ None, validation_alias=AliasPath("status", "nextRun")
34
+ )
35
+ paused: bool = Field(False, validation_alias=AliasPath("status", "paused"))
@@ -0,0 +1,78 @@
1
+ from typing import Union, List, TYPE_CHECKING
2
+ from uuid import UUID
3
+ from pydantic import BaseModel, Field
4
+ from ..base import HydroServerModel
5
+
6
+ if TYPE_CHECKING:
7
+ from hydroserverpy import HydroServer
8
+ from hydroserverpy.api.models import Workspace, DataSource, DataArchive
9
+
10
+
11
+ class OrchestrationSystemFields(BaseModel):
12
+ name: str = Field(..., max_length=255)
13
+ orchestration_system_type: str = Field(..., max_length=255, alias="type")
14
+
15
+
16
+ class OrchestrationSystem(HydroServerModel, OrchestrationSystemFields):
17
+ def __init__(self, _connection: "HydroServer", _uid: Union[UUID, str], **data):
18
+ super().__init__(
19
+ _connection=_connection,
20
+ _model_ref="orchestrationsystems",
21
+ _uid=_uid,
22
+ **data
23
+ )
24
+
25
+ self._workspace_id = str(data.get("workspace_id") or data["workspaceId"])
26
+
27
+ self._workspace = None
28
+ self._datasources = None
29
+ self._dataarchives = None
30
+
31
+ @property
32
+ def workspace(self) -> "Workspace":
33
+ """The workspace this orchestration system belongs to."""
34
+
35
+ if self._workspace is None and self._workspace_id:
36
+ self._workspace = self._connection.workspaces.get(uid=self._workspace_id)
37
+
38
+ return self._workspace
39
+
40
+ @property
41
+ def datasources(self) -> List["DataSource"]:
42
+ """The data sources associated with this workspace."""
43
+
44
+ if self._datasources is None:
45
+ self._datasources = self._connection.datasources.list(
46
+ orchestration_system=self.uid
47
+ )
48
+
49
+ return self._datasources
50
+
51
+ @property
52
+ def dataarchives(self) -> List["DataArchive"]:
53
+ """The data archives associated with this workspace."""
54
+
55
+ if self._dataarchives is None:
56
+ self._dataarchives = self._connection.dataarchives.list(
57
+ orchestration_system=self.uid
58
+ )
59
+
60
+ return self._dataarchives
61
+
62
+ def refresh(self):
63
+ """Refresh this orchestration system from HydroServer."""
64
+
65
+ super()._refresh()
66
+ self._workspace = None
67
+ self._datasources = None
68
+ self._dataarchives = None
69
+
70
+ def save(self):
71
+ """Save changes to this orchestration system to HydroServer."""
72
+
73
+ super()._save()
74
+
75
+ def delete(self):
76
+ """Delete this orchestration system from HydroServer."""
77
+
78
+ super()._delete()
File without changes
@@ -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")