Skip to content

Commit 30b7331

Browse files
committed
enalbe client to create dataArrays
1 parent 51e1ef1 commit 30b7331

6 files changed

Lines changed: 311 additions & 1 deletion

File tree

README.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,15 @@ things_list = service.things().query().top(20).list()
6565
for thing in things_list.entities:
6666
print("my name is: {}".format(thing.name))
6767
```
68+
69+
### Queries to related entity lists
70+
71+
For example the Observations of a given Datastream can be queried via
72+
```
73+
datastream = service.datastreams().find(1)
74+
observations_list = datastream.get_observations().query().filter("result gt 10").list()
75+
```
76+
6877
### Callback function in `EntityList`
6978
The progress of the loading process can be tracked by supplying a callback function along with a step size. The callback
7079
function and the step size must both be provided to the `list` function (see example below).
@@ -86,6 +95,35 @@ for thing in things:
8695
print(thing.name)
8796
```
8897

98+
### DataArrays
99+
DataArrays can be used to make the creation of Observations easier, because with an DataArray only one HTTP Request
100+
has to be created.
101+
102+
An example usage looks as follows:
103+
```
104+
import frost_sta_client as fsc
105+
106+
service = fsc.SensorThingsService("exampleserver.com/FROST-Server/v1.1")
107+
dav = fsc.model.ext.data_array_value.DataArrayValue()
108+
datastream = service.datastreams().find(1)
109+
foi = service.feature_of_interest().find(1)
110+
components = {dav.Property.PHENOMENON_TIME, dav.Property.RESULT, dav.Property.FEATURE_OF_INTEREST}
111+
dav.components = components
112+
dav.datastream = datastream
113+
obs1 = fsc.Observation(result=3,
114+
phenomenon_time='2022-12-19T10:00:00Z',
115+
datastream=datastream,
116+
feature_of_interest=foi)
117+
obs2 = fsc.Observation(result=5,
118+
phenomenon_time='2022-12-19T17:00:00Z',
119+
datastream=datastream,
120+
feature_of_interest=foi)
121+
dav.add_observation(obs1)
122+
dav.add_observation(obs2)
123+
dad = fsc.model.ext.data_array_document.DataArrayDocument()
124+
dad.add_data_array_value(dav)
125+
result_list = service.observations().create(dad)
126+
```
89127

90128
### Json (De)Serialization
91129
Since not all possible backends that are configurable in jsonpickle handle long floats equally, the backend json

frost_sta_client/__version__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
__title__ = 'frost_sta_client'
2-
__version__ = '1.1.30'
2+
__version__ = '1.1.31'
33
__license__ = 'LGPL3'
44
__author__ = 'Katharina Emde'
55
__copyright__ = 'Fraunhofer IOSB'

frost_sta_client/dao/observation.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,37 @@
1616

1717
from frost_sta_client.dao import base
1818
from frost_sta_client.model.ext.entity_type import EntityTypes
19+
from frost_sta_client.utils import transform_entity_to_json_dict
20+
import frost_sta_client
21+
22+
import logging
23+
import requests
24+
import json
25+
1926

2027

2128
class ObservationDao(base.BaseDao):
29+
CREATE_OBSERVATIONS = "CreateObservations"
30+
2231
def __init__(self, service):
2332
"""
2433
A data access object for operations with the Observation entity
2534
"""
2635
base.BaseDao.__init__(self, service, EntityTypes["Observation"])
36+
37+
def create(self, data_array):
38+
url = self.service.url
39+
url.path.add(self.CREATE_OBSERVATIONS)
40+
logging.debug('Posting to ' + str(url.url))
41+
json_dict = transform_entity_to_json_dict(data_array.value)
42+
try:
43+
response = self.service.execute('post', url, json=json_dict)
44+
except requests.exceptions.HTTPError as e:
45+
error_json = e.response.json()
46+
error_message = error_json['message']
47+
logging.error("Creating {} failed with status-code {}, {}".format("Data Array",
48+
e.response.status_code,
49+
error_message))
50+
response_text_as_list = json.loads(response.text)
51+
result = [frost_sta_client.model.observation.Observation(self_link=link) for link in response_text_as_list]
52+
return result
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
# Copyright (C) 2021 Fraunhofer Institut IOSB, Fraunhoferstr. 1, D 76131
2+
# Karlsruhe, Germany.
3+
#
4+
# This program is free software: you can redistribute it and/or modify
5+
# it under the terms of the GNU Lesser General Public License as published by
6+
# the Free Software Foundation, either version 3 of the License, or
7+
# (at your option) any later version.
8+
#
9+
# This program is distributed in the hope that it will be useful,
10+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
# GNU Lesser General Public License for more details.
13+
#
14+
# You should have received a copy of the GNU Lesser General Public License
15+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
16+
from .data_array_value import DataArrayValue
17+
18+
19+
class DataArrayDocument:
20+
def __init__(self, count=-1, next_link = None, value = []):
21+
self._count = count
22+
self._next_link = next_link
23+
self._value = value
24+
25+
@property
26+
def count(self):
27+
return self._count
28+
29+
@count.setter
30+
def count(self, value):
31+
if type(value) == int or value is None:
32+
self._count = value
33+
else:
34+
raise TypeError('count should be of type int')
35+
36+
@property
37+
def next_link(self):
38+
return self._next_link
39+
40+
@next_link.setter
41+
def next_link(self, value):
42+
if type(value) == str or value is None:
43+
self._next_link = value
44+
else:
45+
raise TypeError('nextLink should be of type str')
46+
47+
@property
48+
def value(self):
49+
return self._value
50+
51+
@value.setter
52+
def value(self, value):
53+
if type(value) == list and all(isinstance(x, DataArrayValue) for x in value):
54+
self._value = value
55+
else:
56+
raise TypeError('value should be a list of type DataArrayValue')
57+
58+
def get_observations(self):
59+
obs_list = []
60+
for dav in self.value:
61+
obs_list.concat(dav.get_observations())
62+
return obs_list
63+
64+
def add_data_array_value(self, dav):
65+
self.value.append(dav)
66+
Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
# Copyright (C) 2021 Fraunhofer Institut IOSB, Fraunhoferstr. 1, D 76131
2+
# Karlsruhe, Germany.
3+
#
4+
# This program is free software: you can redistribute it and/or modify
5+
# it under the terms of the GNU Lesser General Public License as published by
6+
# the Free Software Foundation, either version 3 of the License, or
7+
# (at your option) any later version.
8+
#
9+
# This program is distributed in the hope that it will be useful,
10+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
# GNU Lesser General Public License for more details.
13+
#
14+
# You should have received a copy of the GNU Lesser General Public License
15+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
16+
from enum import Enum
17+
18+
import frost_sta_client
19+
import datetime
20+
21+
22+
class DataArrayValue:
23+
24+
class Property(Enum):
25+
ID = 1
26+
PHENOMENON_TIME = 2
27+
RESULT = 3
28+
RESULT_TIME = 4
29+
RESULT_QUALITY = 5
30+
VALID_TIME = 6
31+
PARAMETERS = 7
32+
FEATURE_OF_INTEREST = 8
33+
34+
def to_string(self):
35+
if self is DataArrayValue.Property.ID:
36+
return "id"
37+
if self is DataArrayValue.Property.PHENOMENON_TIME:
38+
return "phenomenonTime"
39+
if self is DataArrayValue.Property.RESULT:
40+
return "result"
41+
if self is DataArrayValue.Property.RESULT_TIME:
42+
return "resultTime"
43+
if self is DataArrayValue.Property.RESULT_QUALITY:
44+
return "resultQuality"
45+
if self is DataArrayValue.Property.VALID_TIME:
46+
return "validTime"
47+
if self is DataArrayValue.Property.PARAMETERS:
48+
return "parameters"
49+
if self is DataArrayValue.Property.FEATURE_OF_INTEREST:
50+
return "FeatureOfInterest/id"
51+
52+
53+
class VisibleProperties:
54+
def __init__(self, all_values: bool):
55+
self.id = all_values
56+
self.phenomenon_time = all_values
57+
self.result = all_values
58+
self.result_time = all_values
59+
self.result_quality = all_values
60+
self.valid_time = all_values
61+
self.parameters = all_values
62+
self.feature_of_interest = all_values
63+
64+
def __init__(self):
65+
self = DataArrayValue.VisibleProperties(False)
66+
67+
def __init__(self, select):
68+
self.id = DataArrayValue.Property.ID in select
69+
self.phenomenon_time = DataArrayValue.Property.PHENOMENON_TIME in select
70+
self.result = DataArrayValue.Property.RESULT in select
71+
self.result_time = DataArrayValue.Property.RESULT_TIME in select
72+
self.result_quality = DataArrayValue.Property.RESULT_QUALITY in select
73+
self.valid_time = DataArrayValue.Property.VALID_TIME in select
74+
self.parameters = DataArrayValue.Property.PARAMETERS in select
75+
self.feature_of_interest = DataArrayValue.Property.FEATURE_OF_INTEREST in select
76+
77+
def get_components(self):
78+
components = []
79+
if self.id:
80+
components.append(DataArrayValue.Property.ID.to_string())
81+
if self.phenomenon_time:
82+
components.append(DataArrayValue.Property.PHENOMENON_TIME.to_string())
83+
if self.result:
84+
components.append(DataArrayValue.Property.RESULT.to_string())
85+
if self.result_time:
86+
components.append(DataArrayValue.Property.RESULT_TIME.to_string())
87+
if self.result_quality:
88+
components.append(DataArrayValue.Property.RESULT_QUALITY.to_string())
89+
if self.valid_time:
90+
components.append(DataArrayValue.Property.VALID_TIME.to_string())
91+
if self.parameters:
92+
components.append(DataArrayValue.Property.PARAMETERS.to_string())
93+
if self.feature_of_interest:
94+
components.append(DataArrayValue.Property.FEATURE_OF_INTEREST.to_string())
95+
return components
96+
97+
98+
def from_observation(self, o: frost_sta_client.Observation):
99+
value = []
100+
if self.id:
101+
value.append(o.id)
102+
if self.phenomenon_time:
103+
if type(o.phenomenon_time) == str:
104+
value.append(o.phenomenon_time)
105+
if type(o.phenomenon_time) == datetime.datetime:
106+
value.append(o.phenomenon_time.isoformat())
107+
if self.result:
108+
value.append(o.result)
109+
if self.result_time:
110+
value.append(o.result_time)
111+
if self.result_quality:
112+
value.append(o.result_quality)
113+
if self.valid_time:
114+
value.append(o.valid_time)
115+
if self.parameters:
116+
value.append(o.parameters)
117+
if self.feature_of_interest:
118+
value.append(o.feature_of_interest.id)
119+
return value
120+
121+
122+
def __init__(self):
123+
self.datastream = None
124+
self.multi_datastream = None
125+
self.visible_properties = None
126+
self.data_array = []
127+
self.components = None
128+
self.observations = []
129+
130+
131+
@property
132+
def datastream(self):
133+
return self._datastream
134+
135+
@datastream.setter
136+
def datastream(self, value):
137+
self._datastream = value
138+
139+
@property
140+
def components(self):
141+
return self._components
142+
143+
@components.setter
144+
def components(self, properties):
145+
if properties is None:
146+
self._components = None
147+
return
148+
if len(self.data_array) >= 1:
149+
raise ValueError("Can not change components after adding Observations")
150+
self.visible_properties = self.VisibleProperties(properties)
151+
self._components = self.visible_properties.get_components()
152+
153+
@property
154+
def data_array(self):
155+
return self._data_array
156+
157+
@data_array.setter
158+
def data_array(self, value):
159+
self._data_array = value
160+
161+
def add_observation(self, o):
162+
self.data_array.append(self.visible_properties.from_observation(o))
163+
self.observations.append(o)
164+
165+
def __getstate__(self):
166+
data = {"Datastream": {
167+
"@iot.id": self.datastream.id
168+
},
169+
"components": self.components,
170+
"dataArray": self.data_array}
171+
return data
172+

frost_sta_client/model/observation.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
# along with this program. If not, see <http://www.gnu.org/licenses/>.
1616

1717
import json
18+
import datetime
1819

1920
import frost_sta_client.model
2021
from . import entity
@@ -70,6 +71,13 @@ def phenomenon_time(self):
7071
def phenomenon_time(self, value):
7172
self._phenomenon_time = utils.check_datetime(value, 'phenomenon_time')
7273

74+
def phenomenon_time_as_str(self):
75+
if type(self._phenomenon_time) == str or self._phenomenon_time is None:
76+
return self._phenomenon_time
77+
if type(self._phenomenon_time) == datetime.datetime:
78+
return self._phenomenon_time.isoformat()
79+
return None
80+
7381
@property
7482
def result(self):
7583
return self._result

0 commit comments

Comments
 (0)