hydroserverpy 1.2.0__py3-none-any.whl → 1.3.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of hydroserverpy might be problematic. Click here for more details.
- hydroserverpy/__init__.py +1 -1
- hydroserverpy/api/{main.py → client.py} +52 -22
- hydroserverpy/api/models/__init__.py +1 -2
- hydroserverpy/api/models/base.py +180 -47
- hydroserverpy/api/models/etl/data_archive.py +31 -59
- hydroserverpy/api/models/etl/data_source.py +34 -76
- hydroserverpy/api/models/etl/orchestration_system.py +23 -38
- hydroserverpy/api/models/iam/apikey.py +57 -38
- hydroserverpy/api/models/iam/collaborator.py +55 -19
- hydroserverpy/api/models/iam/role.py +32 -4
- hydroserverpy/api/models/iam/workspace.py +58 -86
- hydroserverpy/api/models/sta/datastream.py +122 -214
- hydroserverpy/api/models/sta/observation.py +101 -0
- hydroserverpy/api/models/sta/observed_property.py +18 -53
- hydroserverpy/api/models/sta/processing_level.py +16 -31
- hydroserverpy/api/models/sta/result_qualifier.py +16 -31
- hydroserverpy/api/models/sta/sensor.py +27 -88
- hydroserverpy/api/models/sta/thing.py +48 -152
- hydroserverpy/api/models/sta/unit.py +16 -29
- hydroserverpy/api/services/__init__.py +1 -0
- hydroserverpy/api/services/base.py +92 -76
- hydroserverpy/api/services/etl/data_archive.py +42 -72
- hydroserverpy/api/services/etl/data_source.py +42 -72
- hydroserverpy/api/services/etl/orchestration_system.py +25 -33
- hydroserverpy/api/services/iam/role.py +38 -0
- hydroserverpy/api/services/iam/workspace.py +96 -99
- hydroserverpy/api/services/sta/datastream.py +150 -211
- hydroserverpy/api/services/sta/observed_property.py +31 -49
- hydroserverpy/api/services/sta/processing_level.py +30 -36
- hydroserverpy/api/services/sta/result_qualifier.py +24 -34
- hydroserverpy/api/services/sta/sensor.py +34 -48
- hydroserverpy/api/services/sta/thing.py +96 -89
- hydroserverpy/api/services/sta/unit.py +30 -34
- hydroserverpy/api/utils.py +22 -0
- hydroserverpy/etl/extractors/base.py +3 -5
- hydroserverpy/etl/loaders/hydroserver_loader.py +1 -0
- hydroserverpy/etl/timestamp_parser.py +82 -48
- hydroserverpy/etl/transformers/base.py +6 -10
- hydroserverpy/etl_csv/hydroserver_etl_csv.py +18 -24
- {hydroserverpy-1.2.0.dist-info → hydroserverpy-1.3.0.dist-info}/METADATA +1 -1
- hydroserverpy-1.3.0.dist-info/RECORD +70 -0
- hydroserverpy/api/http.py +0 -22
- hydroserverpy-1.2.0.dist-info/RECORD +0 -68
- {hydroserverpy-1.2.0.dist-info → hydroserverpy-1.3.0.dist-info}/WHEEL +0 -0
- {hydroserverpy-1.2.0.dist-info → hydroserverpy-1.3.0.dist-info}/licenses/LICENSE +0 -0
- {hydroserverpy-1.2.0.dist-info → hydroserverpy-1.3.0.dist-info}/top_level.txt +0 -0
- {hydroserverpy-1.2.0.dist-info → hydroserverpy-1.3.0.dist-info}/zip-safe +0 -0
|
@@ -1,50 +1,35 @@
|
|
|
1
|
-
|
|
2
|
-
from
|
|
3
|
-
from pydantic import
|
|
4
|
-
from ..base import
|
|
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
|
|
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
|
-
|
|
18
|
-
|
|
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.
|
|
32
|
-
self._workspace = self.
|
|
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
|
-
|
|
2
|
-
from
|
|
3
|
-
from pydantic import
|
|
4
|
-
from ..base import
|
|
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
|
|
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
|
-
|
|
17
|
-
|
|
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.
|
|
31
|
-
self._workspace = self.
|
|
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
|
-
|
|
2
|
-
from
|
|
3
|
-
from pydantic import
|
|
4
|
-
from ..base import
|
|
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
|
|
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
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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.
|
|
87
|
-
self._workspace = self.
|
|
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
|
-
|
|
2
|
-
from
|
|
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
|
|
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
|
|
17
|
+
class Thing(HydroServerBaseModel):
|
|
19
18
|
name: str = Field(..., max_length=200)
|
|
20
19
|
description: str
|
|
21
|
-
sampling_feature_type: str = Field(
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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
|
-
|
|
138
|
-
|
|
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
|
-
|
|
152
|
-
|
|
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.
|
|
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.
|
|
69
|
+
self._datastreams = self.client.datastreams.list(thing=self.uid, fetch_all=True).items
|
|
169
70
|
|
|
170
71
|
return self._datastreams
|
|
171
72
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
112
|
+
self.client.things.delete_photo(uid=self.uid, name=name)
|
|
217
113
|
del self.photos[name]
|
|
@@ -1,49 +1,36 @@
|
|
|
1
|
-
|
|
2
|
-
from
|
|
3
|
-
from pydantic import
|
|
4
|
-
from ..base import
|
|
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
|
|
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
|
-
|
|
19
|
-
|
|
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.
|
|
31
|
-
self._workspace = self.
|
|
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()
|