hydroserverpy 1.2.1__py3-none-any.whl → 1.3.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 (47) hide show
  1. hydroserverpy/__init__.py +1 -1
  2. hydroserverpy/api/{main.py → client.py} +52 -22
  3. hydroserverpy/api/models/__init__.py +1 -2
  4. hydroserverpy/api/models/base.py +180 -47
  5. hydroserverpy/api/models/etl/data_archive.py +31 -59
  6. hydroserverpy/api/models/etl/data_source.py +34 -76
  7. hydroserverpy/api/models/etl/orchestration_system.py +23 -38
  8. hydroserverpy/api/models/iam/apikey.py +57 -38
  9. hydroserverpy/api/models/iam/collaborator.py +55 -19
  10. hydroserverpy/api/models/iam/role.py +32 -4
  11. hydroserverpy/api/models/iam/workspace.py +58 -86
  12. hydroserverpy/api/models/sta/datastream.py +122 -214
  13. hydroserverpy/api/models/sta/observation.py +101 -0
  14. hydroserverpy/api/models/sta/observed_property.py +18 -53
  15. hydroserverpy/api/models/sta/processing_level.py +16 -31
  16. hydroserverpy/api/models/sta/result_qualifier.py +16 -31
  17. hydroserverpy/api/models/sta/sensor.py +27 -88
  18. hydroserverpy/api/models/sta/thing.py +48 -152
  19. hydroserverpy/api/models/sta/unit.py +16 -29
  20. hydroserverpy/api/services/__init__.py +1 -0
  21. hydroserverpy/api/services/base.py +92 -76
  22. hydroserverpy/api/services/etl/data_archive.py +42 -72
  23. hydroserverpy/api/services/etl/data_source.py +42 -72
  24. hydroserverpy/api/services/etl/orchestration_system.py +25 -33
  25. hydroserverpy/api/services/iam/role.py +38 -0
  26. hydroserverpy/api/services/iam/workspace.py +96 -99
  27. hydroserverpy/api/services/sta/datastream.py +151 -210
  28. hydroserverpy/api/services/sta/observed_property.py +31 -49
  29. hydroserverpy/api/services/sta/processing_level.py +30 -36
  30. hydroserverpy/api/services/sta/result_qualifier.py +24 -34
  31. hydroserverpy/api/services/sta/sensor.py +34 -48
  32. hydroserverpy/api/services/sta/thing.py +96 -89
  33. hydroserverpy/api/services/sta/unit.py +30 -34
  34. hydroserverpy/api/utils.py +22 -0
  35. hydroserverpy/etl/extractors/base.py +2 -4
  36. hydroserverpy/etl/loaders/hydroserver_loader.py +1 -0
  37. hydroserverpy/etl/timestamp_parser.py +82 -48
  38. hydroserverpy/etl/transformers/base.py +5 -9
  39. hydroserverpy/etl_csv/hydroserver_etl_csv.py +1 -1
  40. {hydroserverpy-1.2.1.dist-info → hydroserverpy-1.3.0b2.dist-info}/METADATA +1 -1
  41. hydroserverpy-1.3.0b2.dist-info/RECORD +70 -0
  42. hydroserverpy/api/http.py +0 -22
  43. hydroserverpy-1.2.1.dist-info/RECORD +0 -68
  44. {hydroserverpy-1.2.1.dist-info → hydroserverpy-1.3.0b2.dist-info}/WHEEL +0 -0
  45. {hydroserverpy-1.2.1.dist-info → hydroserverpy-1.3.0b2.dist-info}/licenses/LICENSE +0 -0
  46. {hydroserverpy-1.2.1.dist-info → hydroserverpy-1.3.0b2.dist-info}/top_level.txt +0 -0
  47. {hydroserverpy-1.2.1.dist-info → hydroserverpy-1.3.0b2.dist-info}/zip-safe +0 -0
@@ -1,50 +1,35 @@
1
- from typing import Union, Optional, TYPE_CHECKING
2
- from uuid import UUID
3
- from pydantic import BaseModel, Field
4
- from ..base import HydroServerModel
1
+ import uuid
2
+ from typing import Optional, ClassVar, TYPE_CHECKING
3
+ from pydantic import Field
4
+ from ..base import HydroServerBaseModel
5
5
 
6
6
  if TYPE_CHECKING:
7
7
  from hydroserverpy import HydroServer
8
8
  from hydroserverpy.api.models import Workspace
9
9
 
10
10
 
11
- class ProcessingLevelFields(BaseModel):
11
+ class ProcessingLevel(HydroServerBaseModel):
12
12
  code: str = Field(..., max_length=255)
13
13
  definition: Optional[str] = None
14
14
  explanation: Optional[str] = None
15
+ workspace_id: Optional[uuid.UUID] = None
15
16
 
17
+ _editable_fields: ClassVar[set[str]] = {"code", "definition", "explanation"}
16
18
 
17
- class ProcessingLevel(HydroServerModel, ProcessingLevelFields):
18
- def __init__(self, _connection: "HydroServer", _uid: Union[UUID, str], **data):
19
- super().__init__(
20
- _connection=_connection, _model_ref="processinglevels", _uid=_uid, **data
21
- )
22
-
23
- self._workspace_id = str(data.get("workspace_id") or data["workspaceId"])
19
+ def __init__(self, client: "HydroServer", **data):
20
+ super().__init__(client=client, service=client.processinglevels, **data)
24
21
 
25
22
  self._workspace = None
26
23
 
24
+ @classmethod
25
+ def get_route(cls):
26
+ return "processing-levels"
27
+
27
28
  @property
28
- def workspace(self) -> "Workspace":
29
+ def workspace(self) -> Optional["Workspace"]:
29
30
  """The workspace this processing level belongs to."""
30
31
 
31
- if self._workspace is None and self._workspace_id:
32
- self._workspace = self._connection.workspaces.get(uid=self._workspace_id)
32
+ if self._workspace is None and self.workspace_id:
33
+ self._workspace = self.client.workspaces.get(uid=self.workspace_id)
33
34
 
34
35
  return self._workspace
35
-
36
- def refresh(self):
37
- """Refresh this processing level from HydroServer."""
38
-
39
- super()._refresh()
40
- self._workspace = None
41
-
42
- def save(self):
43
- """Save changes to this processing level to HydroServer."""
44
-
45
- super()._save()
46
-
47
- def delete(self):
48
- """Delete this processing level from HydroServer."""
49
-
50
- super()._delete()
@@ -1,49 +1,34 @@
1
- from typing import Union, Optional, TYPE_CHECKING
2
- from uuid import UUID
3
- from pydantic import BaseModel, Field
4
- from ..base import HydroServerModel
1
+ import uuid
2
+ from typing import Optional, ClassVar, TYPE_CHECKING
3
+ from pydantic import Field
4
+ from ..base import HydroServerBaseModel
5
5
 
6
6
  if TYPE_CHECKING:
7
7
  from hydroserverpy import HydroServer
8
8
  from hydroserverpy.api.models import Workspace
9
9
 
10
10
 
11
- class ResultQualifierFields(BaseModel):
11
+ class ResultQualifier(HydroServerBaseModel):
12
12
  code: str = Field(..., max_length=255)
13
13
  description: str
14
+ workspace_id: Optional[uuid.UUID] = None
14
15
 
16
+ _editable_fields: ClassVar[set[str]] = {"code", "description"}
15
17
 
16
- class ResultQualifier(HydroServerModel, ResultQualifierFields):
17
- def __init__(self, _connection: "HydroServer", _uid: Union[UUID, str], **data):
18
- super().__init__(
19
- _connection=_connection, _model_ref="resultqualifiers", _uid=_uid, **data
20
- )
21
-
22
- self._workspace_id = str(data.get("workspace_id") or data["workspaceId"])
18
+ def __init__(self, client: "HydroServer", **data):
19
+ super().__init__(client=client, service=client.resultqualifiers, **data)
23
20
 
24
21
  self._workspace = None
25
22
 
23
+ @classmethod
24
+ def get_route(cls):
25
+ return "result-qualifiers"
26
+
26
27
  @property
27
- def workspace(self) -> "Workspace":
28
+ def workspace(self) -> Optional["Workspace"]:
28
29
  """The workspace this result qualifier belongs to."""
29
30
 
30
- if self._workspace is None and self._workspace_id:
31
- self._workspace = self._connection.workspaces.get(uid=self._workspace_id)
31
+ if self._workspace is None and self.workspace_id:
32
+ self._workspace = self.client.workspaces.get(uid=self.workspace_id)
32
33
 
33
34
  return self._workspace
34
-
35
- def refresh(self):
36
- """Refresh this result qualifier from HydroServer."""
37
-
38
- super()._refresh()
39
- self._workspace = None
40
-
41
- def save(self):
42
- """Save changes to this result qualifier to HydroServer."""
43
-
44
- super()._save()
45
-
46
- def delete(self):
47
- """Delete this result qualifier from HydroServer."""
48
-
49
- super()._delete()
@@ -1,105 +1,44 @@
1
- from typing import Union, Optional, TYPE_CHECKING
2
- from uuid import UUID
3
- from pydantic import BaseModel, Field, AliasChoices, AliasPath
4
- from ..base import HydroServerModel
1
+ import uuid
2
+ from typing import Optional, ClassVar, TYPE_CHECKING
3
+ from pydantic import Field
4
+ from ..base import HydroServerBaseModel
5
5
 
6
6
  if TYPE_CHECKING:
7
7
  from hydroserverpy import HydroServer
8
8
  from hydroserverpy.api.models import Workspace
9
9
 
10
10
 
11
- class SensorFields(BaseModel):
11
+ class Sensor(HydroServerBaseModel):
12
12
  name: str = Field(..., max_length=255)
13
13
  description: str
14
14
  encoding_type: str = Field(..., max_length=255)
15
- manufacturer: Optional[str] = Field(
16
- None,
17
- max_length=255,
18
- validation_alias=AliasChoices(
19
- "manufacturer", AliasPath("metadata", "sensorModel", "sensorManufacturer")
20
- ),
21
- )
22
- sensor_model: Optional[str] = Field(
23
- None,
24
- max_length=255,
25
- alias="model",
26
- validation_alias=AliasChoices(
27
- "model", AliasPath("metadata", "sensorModel", "sensorModelName")
28
- ),
29
- )
30
- sensor_model_link: Optional[str] = Field(
31
- None,
32
- max_length=500,
33
- alias="modelLink",
34
- validation_alias=AliasChoices(
35
- "modelLink", AliasPath("metadata", "sensorModel", "sensorModelUrl")
36
- ),
37
- )
38
- method_type: str = Field(
39
- ...,
40
- max_length=100,
41
- validation_alias=AliasChoices(
42
- "methodType", AliasPath("metadata", "methodType")
43
- ),
44
- )
45
- method_link: Optional[str] = Field(
46
- None,
47
- max_length=500,
48
- validation_alias=AliasChoices(
49
- "methodLink", AliasPath("metadata", "methodLink")
50
- ),
51
- )
52
- method_code: Optional[str] = Field(
53
- None,
54
- max_length=50,
55
- validation_alias=AliasChoices(
56
- "methodCode", AliasPath("metadata", "methodCode")
57
- ),
58
- )
59
-
60
-
61
- class Sensor(HydroServerModel, SensorFields):
62
- def __init__(self, _connection: "HydroServer", _uid: Union[UUID, str], **data):
63
- super().__init__(
64
- _connection=_connection, _model_ref="sensors", _uid=_uid, **data
65
- )
66
-
67
- self._workspace_id = (
68
- data.get("workspace_id")
69
- or data.get("workspaceId")
70
- or (
71
- None
72
- if data.get("properties", {}).get("workspace") is None
73
- else data.get("properties", {}).get("workspace", {}).get("id")
74
- )
75
- )
76
- self._workspace_id = (
77
- str(self._workspace_id) if self._workspace_id is not None else None
78
- )
15
+ manufacturer: Optional[str] = Field(None, max_length=255)
16
+ sensor_model: Optional[str] = Field(None, max_length=255, alias="model")
17
+ sensor_model_link: Optional[str] = Field(None, max_length=500, alias="modelLink")
18
+ method_type: str = Field(..., max_length=100)
19
+ method_link: Optional[str] = Field(None, max_length=500)
20
+ method_code: Optional[str] = Field(None, max_length=50)
21
+ workspace_id: Optional[uuid.UUID] = None
22
+
23
+ _editable_fields: ClassVar[set[str]] = {
24
+ "name", "description", "encoding_type", "manufacturer", "sensor_model", "sensor_model_link", "method_type",
25
+ "method_link", "method_code"
26
+ }
27
+
28
+ def __init__(self, client: "HydroServer", **data):
29
+ super().__init__(client=client, service=client.sensors, **data)
79
30
 
80
31
  self._workspace = None
81
32
 
33
+ @classmethod
34
+ def get_route(cls):
35
+ return "sensors"
36
+
82
37
  @property
83
- def workspace(self) -> "Workspace":
38
+ def workspace(self) -> Optional["Workspace"]:
84
39
  """The workspace this sensor belongs to."""
85
40
 
86
- if self._workspace is None and self._workspace_id:
87
- self._workspace = self._connection.workspaces.get(uid=self._workspace_id)
41
+ if self._workspace is None and self.workspace_id:
42
+ self._workspace = self.client.workspaces.get(uid=self.workspace_id)
88
43
 
89
44
  return self._workspace
90
-
91
- def refresh(self):
92
- """Refresh this sensor from HydroServer."""
93
-
94
- super()._refresh()
95
- self._workspace = None
96
-
97
- def save(self):
98
- """Save changes to this sensor to HydroServer."""
99
-
100
- super()._save()
101
-
102
- def delete(self):
103
- """Delete this sensor from HydroServer."""
104
-
105
- super()._delete()
@@ -1,162 +1,63 @@
1
- from typing import Optional, Union, List, Dict, IO, TYPE_CHECKING
2
- from uuid import UUID
1
+ import uuid
2
+ from typing import Optional, ClassVar, List, Dict, IO, TYPE_CHECKING
3
3
  from pydantic import (
4
- BaseModel,
5
4
  Field,
6
- AliasChoices,
7
5
  AliasPath,
6
+ AliasChoices,
8
7
  AnyHttpUrl,
9
- field_validator,
8
+ field_validator
10
9
  )
11
- from ..base import HydroServerModel
10
+ from ..base import HydroServerBaseModel
12
11
 
13
12
  if TYPE_CHECKING:
14
13
  from hydroserverpy import HydroServer
15
14
  from hydroserverpy.api.models import Workspace, Datastream
16
15
 
17
16
 
18
- class ThingFields(BaseModel):
17
+ class Thing(HydroServerBaseModel):
19
18
  name: str = Field(..., max_length=200)
20
19
  description: str
21
- sampling_feature_type: str = Field(
22
- ...,
23
- max_length=200,
24
- validation_alias=AliasChoices(
25
- "samplingFeatureType", AliasPath("properties", "samplingFeatureType")
26
- ),
27
- )
28
- sampling_feature_code: str = Field(
29
- ...,
30
- max_length=200,
31
- validation_alias=AliasChoices(
32
- "samplingFeatureCode", AliasPath("properties", "samplingFeatureCode")
33
- ),
34
- )
35
- site_type: str = Field(
36
- ...,
37
- max_length=200,
38
- validation_alias=AliasChoices("siteType", AliasPath("properties", "siteType")),
39
- )
40
- tags: Dict[str, str] = Field(
41
- ...,
42
- json_schema_extra={"editable": False, "read_only": True},
43
- validation_alias=AliasChoices("tags", AliasPath("properties", "tags")),
44
- )
45
- photos: Dict[str, AnyHttpUrl] = Field(
46
- ...,
47
- json_schema_extra={"editable": False, "read_only": True},
48
- validation_alias=AliasChoices("photos", AliasPath("properties", "photos")),
49
- )
50
- data_disclaimer: Optional[str] = Field(
51
- None,
52
- validation_alias=AliasChoices(
53
- "dataDisclaimer", AliasPath("properties", "dataDisclaimer")
54
- ),
55
- )
56
- is_private: bool = Field(
57
- ...,
58
- validation_alias=AliasChoices(
59
- "isPrivate", AliasPath("properties", "isPrivate")
60
- ),
61
- )
62
-
63
- @field_validator("tags", mode="before")
64
- def convert_tags(
65
- cls, value: Union[List[Dict[str, str]], Dict[str, str]]
66
- ) -> Dict[str, str]:
67
- if isinstance(value, list):
68
- return {item["key"]: item["value"] for item in value}
69
- return value
70
-
71
- @field_validator("photos", mode="before")
72
- def convert_photos(
73
- cls, value: Union[List[Dict[str, str]], Dict[str, str]]
74
- ) -> Dict[str, str]:
75
- if isinstance(value, list):
76
- return {item["name"]: item["link"] for item in value}
77
- return value
78
-
79
-
80
- class LocationFields(BaseModel):
81
- latitude: float = Field(
82
- ...,
83
- ge=-90,
84
- le=90,
85
- validation_alias=AliasChoices(
86
- "latitude",
87
- AliasPath("Locations", 0, "location", "geometry", "coordinates", 0),
88
- ),
89
- )
90
- longitude: float = Field(
91
- ...,
92
- ge=-180,
93
- le=180,
94
- validation_alias=AliasChoices(
95
- "longitude",
96
- AliasPath("Locations", 0, "location", "geometry", "coordinates", 1),
97
- ),
98
- )
20
+ sampling_feature_type: str = Field(..., max_length=200)
21
+ sampling_feature_code: str = Field(..., max_length=200)
22
+ site_type: str = Field(..., max_length=200)
23
+ data_disclaimer: Optional[str] = None
24
+ is_private: bool
25
+ latitude: float = Field(..., ge=-90, le=90, validation_alias=AliasPath("location", "latitude"))
26
+ longitude: float = Field(..., ge=-180, le=180, validation_alias=AliasPath("location", "longitude"))
99
27
  elevation_m: Optional[float] = Field(
100
- None,
101
- ge=-99999,
102
- le=99999,
103
- validation_alias=AliasChoices(
104
- "elevation_m", AliasPath("Locations", 0, "properties", "elevation_m")
105
- ),
28
+ None, ge=-99999, le=99999, alias="elevation_m", validation_alias=AliasPath("location", "elevation_m")
106
29
  )
107
30
  elevation_datum: Optional[str] = Field(
108
- None,
109
- max_length=255,
110
- validation_alias=AliasChoices(
111
- "elevationDatum", AliasPath("Locations", 0, "properties", "elevationDatum")
112
- ),
113
- )
114
- state: Optional[str] = Field(
115
- None,
116
- max_length=200,
117
- validation_alias=AliasChoices(
118
- "state", AliasPath("Locations", 0, "properties", "state")
119
- ),
120
- )
121
- county: Optional[str] = Field(
122
- None,
123
- max_length=200,
124
- validation_alias=AliasChoices(
125
- "county", AliasPath("Locations", 0, "properties", "county")
126
- ),
127
- )
128
- country: Optional[str] = Field(
129
- None,
130
- max_length=2,
131
- validation_alias=AliasChoices(
132
- "country", AliasPath("Locations", 0, "properties", "country")
133
- ),
31
+ None, max_length=255, validation_alias=AliasChoices("elevationDatum", AliasPath("location", "elevationDatum"))
134
32
  )
33
+ state: Optional[str] = Field(None, max_length=200, validation_alias=AliasPath("location", "state"))
34
+ county: Optional[str] = Field(None, max_length=200, validation_alias=AliasPath("location", "county"))
35
+ country: Optional[str] = Field(None, max_length=2, validation_alias=AliasPath("location", "country"))
36
+ tags: Dict[str, str]
37
+ photos: Dict[str, AnyHttpUrl]
38
+ workspace_id: uuid.UUID
135
39
 
40
+ _editable_fields: ClassVar[set[str]] = {
41
+ "name", "description", "sampling_feature_type", "sampling_feature_code", "site_type", "data_disclaimer",
42
+ "is_private", "latitude", "longitude", "elevation_m", "elevation_datum", "state", "county", "country"
43
+ }
136
44
 
137
- class Thing(HydroServerModel, ThingFields, LocationFields):
138
- def __init__(self, _connection: "HydroServer", _uid: Union[UUID, str], **data):
139
- super().__init__(
140
- _connection=_connection, _model_ref="things", _uid=_uid, **data
141
- )
142
-
143
- self._workspace_id = str(
144
- data.get("workspace_id")
145
- or data.get("workspaceId")
146
- or data["properties"]["workspace"]["id"]
147
- )
45
+ def __init__(self, client: "HydroServer", **data):
46
+ super().__init__(client=client, service=client.things, **data)
148
47
 
149
48
  self._workspace = None
150
49
  self._datastreams = None
151
- self._photos = None
152
- self._tags = None
50
+
51
+ @classmethod
52
+ def get_route(cls):
53
+ return "things"
153
54
 
154
55
  @property
155
56
  def workspace(self) -> "Workspace":
156
57
  """The workspace this thing belongs to."""
157
58
 
158
59
  if self._workspace is None:
159
- self._workspace = self._connection.workspaces.get(uid=self._workspace_id)
60
+ self._workspace = self.client.workspaces.get(uid=self.workspace_id)
160
61
 
161
62
  return self._workspace
162
63
 
@@ -165,53 +66,48 @@ class Thing(HydroServerModel, ThingFields, LocationFields):
165
66
  """The datastreams collected at this thing."""
166
67
 
167
68
  if self._datastreams is None:
168
- self._datastreams = self._connection.datastreams.list(thing=self.uid)
69
+ self._datastreams = self.client.datastreams.list(thing=self.uid, fetch_all=True).items
169
70
 
170
71
  return self._datastreams
171
72
 
172
- def refresh(self):
173
- """Refresh this thing from HydroServer."""
174
-
175
- super()._refresh()
176
- self._workspace = None
177
- self._datastreams = None
178
-
179
- def save(self):
180
- """Save changes to this thing to HydroServer."""
181
-
182
- super()._save()
183
-
184
- def delete(self):
185
- """Delete this thing from HydroServer."""
73
+ @field_validator("tags", mode="before")
74
+ def transform_tags(cls, v):
75
+ if isinstance(v, list):
76
+ return {item["key"]: item["value"] for item in v if "key" in item and "value" in item}
77
+ return v
186
78
 
187
- super()._delete()
79
+ @field_validator("photos", mode="before")
80
+ def transform_photos(cls, v):
81
+ if isinstance(v, list):
82
+ return {item["name"]: item["link"] for item in v if "name" in item and "link" in item}
83
+ return v
188
84
 
189
85
  def add_tag(self, key: str, value: str):
190
86
  """Add a tag to this thing."""
191
87
 
192
- self._connection.things.add_tag(uid=self.uid, key=key, value=value)
88
+ self.client.things.add_tag(uid=self.uid, key=key, value=value)
193
89
  self.tags[key] = value
194
90
 
195
91
  def update_tag(self, key: str, value: str):
196
92
  """Edit a tag of this thing."""
197
93
 
198
- self._connection.things.update_tag(uid=self.uid, key=key, value=value)
94
+ self.client.things.update_tag(uid=self.uid, key=key, value=value)
199
95
  self.tags[key] = value
200
96
 
201
97
  def delete_tag(self, key: str):
202
98
  """Delete a tag of this thing."""
203
99
 
204
- self._connection.things.delete_tag(uid=self.uid, key=key)
100
+ self.client.things.delete_tag(uid=self.uid, key=key, value=self.tags[key])
205
101
  del self.tags[key]
206
102
 
207
103
  def add_photo(self, file: IO[bytes]):
208
104
  """Add a photo of this thing."""
209
105
 
210
- photo = self._connection.things.add_photo(uid=self.uid, file=file)
106
+ photo = self.client.things.add_photo(uid=self.uid, file=file)
211
107
  self.photos[photo["name"]] = photo["link"]
212
108
 
213
109
  def delete_photo(self, name: str):
214
110
  """Delete a photo of this thing."""
215
111
 
216
- self._connection.things.delete_photo(uid=self.uid, name=name)
112
+ self.client.things.delete_photo(uid=self.uid, name=name)
217
113
  del self.photos[name]
@@ -1,49 +1,36 @@
1
- from typing import Union, TYPE_CHECKING
2
- from uuid import UUID
3
- from pydantic import BaseModel, Field
4
- from ..base import HydroServerModel
1
+ import uuid
2
+ from typing import Optional, ClassVar, TYPE_CHECKING
3
+ from pydantic import Field
4
+ from ..base import HydroServerBaseModel
5
5
 
6
6
  if TYPE_CHECKING:
7
7
  from hydroserverpy import HydroServer
8
8
  from hydroserverpy.api.models import Workspace
9
9
 
10
10
 
11
- class UnitFields(BaseModel):
11
+ class Unit(HydroServerBaseModel):
12
12
  name: str = Field(..., max_length=255)
13
13
  symbol: str = Field(..., max_length=255)
14
14
  definition: str
15
15
  unit_type: str = Field(..., max_length=255, alias="type")
16
+ workspace_id: Optional[uuid.UUID] = None
16
17
 
18
+ _editable_fields: ClassVar[set[str]] = {"name", "symbol", "definition", "unit_type"}
17
19
 
18
- class Unit(HydroServerModel, UnitFields):
19
- def __init__(self, _connection: "HydroServer", _uid: Union[UUID, str], **data):
20
- super().__init__(_connection=_connection, _model_ref="units", _uid=_uid, **data)
21
-
22
- self._workspace_id = str(data.get("workspace_id") or data["workspaceId"])
20
+ def __init__(self, client: "HydroServer", **data):
21
+ super().__init__(client=client, service=client.units, **data)
23
22
 
24
23
  self._workspace = None
25
24
 
25
+ @classmethod
26
+ def get_route(cls):
27
+ return "units"
28
+
26
29
  @property
27
- def workspace(self) -> "Workspace":
30
+ def workspace(self) -> Optional["Workspace"]:
28
31
  """The workspace this unit belongs to."""
29
32
 
30
- if self._workspace is None and self._workspace_id:
31
- self._workspace = self._connection.workspaces.get(uid=self._workspace_id)
33
+ if self._workspace is None and self.workspace_id:
34
+ self._workspace = self.client.workspaces.get(uid=self.workspace_id)
32
35
 
33
36
  return self._workspace
34
-
35
- def refresh(self):
36
- """Refresh this unit from HydroServer."""
37
-
38
- super()._refresh()
39
- self._workspace = None
40
-
41
- def save(self):
42
- """Save changes to this unit to HydroServer."""
43
-
44
- super()._save()
45
-
46
- def delete(self):
47
- """Delete this unit from HydroServer."""
48
-
49
- super()._delete()
@@ -1,4 +1,5 @@
1
1
  from .iam.workspace import WorkspaceService
2
+ from .iam.role import RoleService
2
3
  from .sta.thing import ThingService
3
4
  from .sta.observed_property import ObservedPropertyService
4
5
  from .sta.unit import UnitService