staplus-client 0.3.3__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 (53) hide show
  1. staplus_client/__init__.py +47 -0
  2. staplus_client/__version__.py +8 -0
  3. staplus_client/dao/__init__.py +18 -0
  4. staplus_client/dao/actuator.py +30 -0
  5. staplus_client/dao/base.py +70 -0
  6. staplus_client/dao/campaign.py +25 -0
  7. staplus_client/dao/datastream.py +35 -0
  8. staplus_client/dao/features_of_interest.py +36 -0
  9. staplus_client/dao/historical_location.py +35 -0
  10. staplus_client/dao/license.py +25 -0
  11. staplus_client/dao/location.py +35 -0
  12. staplus_client/dao/multi_datastream.py +36 -0
  13. staplus_client/dao/observation.py +55 -0
  14. staplus_client/dao/observation_group.py +25 -0
  15. staplus_client/dao/observedproperty.py +35 -0
  16. staplus_client/dao/party.py +25 -0
  17. staplus_client/dao/relation.py +57 -0
  18. staplus_client/dao/sensor.py +35 -0
  19. staplus_client/dao/task.py +30 -0
  20. staplus_client/dao/tasking_capability.py +30 -0
  21. staplus_client/dao/thing.py +36 -0
  22. staplus_client/model/__init__.py +29 -0
  23. staplus_client/model/campaign.py +417 -0
  24. staplus_client/model/datastream.py +166 -0
  25. staplus_client/model/entity.py +29 -0
  26. staplus_client/model/ext/__init__.py +0 -0
  27. staplus_client/model/ext/entity_list.py +75 -0
  28. staplus_client/model/ext/entity_type.py +142 -0
  29. staplus_client/model/ext/unitofmeasurement.py +5 -0
  30. staplus_client/model/feature_of_interest.py +17 -0
  31. staplus_client/model/historical_location.py +21 -0
  32. staplus_client/model/license.py +301 -0
  33. staplus_client/model/location.py +22 -0
  34. staplus_client/model/multi_datastream.py +160 -0
  35. staplus_client/model/observation.py +218 -0
  36. staplus_client/model/observation_group.py +399 -0
  37. staplus_client/model/observedproperty.py +20 -0
  38. staplus_client/model/party.py +315 -0
  39. staplus_client/model/relation.py +246 -0
  40. staplus_client/model/sensor.py +16 -0
  41. staplus_client/model/thing.py +110 -0
  42. staplus_client/query/__init__.py +0 -0
  43. staplus_client/query/query.py +109 -0
  44. staplus_client/service/BearerAuth.py +23 -0
  45. staplus_client/service/__init__.py +17 -0
  46. staplus_client/service/auth_handler.py +23 -0
  47. staplus_client/service/staplusservice.py +162 -0
  48. staplus_client/utils.py +64 -0
  49. staplus_client-0.3.3.dist-info/LICENSE +165 -0
  50. staplus_client-0.3.3.dist-info/METADATA +470 -0
  51. staplus_client-0.3.3.dist-info/RECORD +53 -0
  52. staplus_client-0.3.3.dist-info/WHEEL +5 -0
  53. staplus_client-0.3.3.dist-info/top_level.txt +1 -0
@@ -0,0 +1,30 @@
1
+ # Copyright (C) 2023-2024 Secure Dimensions GmbH, Munich, Germany.
2
+ #
3
+ # This program is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU Lesser General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # This program is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU Lesser General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU Lesser General Public License
14
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
15
+ from frost_sta_client.dao.task import TaskDao as STATaskDao
16
+ from staplus_client.dao.base import BaseDao as STAplusBaseDao
17
+ from staplus_client.query.query import Query
18
+
19
+ class TaskDao(STATaskDao):
20
+ def __init__(self, service):
21
+ """
22
+ A data access object for operations with the Task entity
23
+ """
24
+ super().__init__(service)
25
+
26
+ def query(self):
27
+ return Query(self.service, self.entitytype, self.entitytype_plural, self.entity_class, self.parent)
28
+
29
+ def create(self, entity):
30
+ return STAplusBaseDao.create(self, entity)
@@ -0,0 +1,30 @@
1
+ # Copyright (C) 2023-2024 Secure Dimensions GmbH, Munich, Germany.
2
+ #
3
+ # This program is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU Lesser General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # This program is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU Lesser General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU Lesser General Public License
14
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
15
+ from frost_sta_client.dao.tasking_capability import TaskingCapabilityDao as STATaskingCapabilityDao
16
+ from staplus_client.dao.base import BaseDao as STAplusBaseDao
17
+ from staplus_client.query.query import Query
18
+
19
+ class TaskingCapabilityDao(STATaskingCapabilityDao):
20
+ def __init__(self, service):
21
+ """
22
+ A data access object for operations with the TaskingCapability entity
23
+ """
24
+ super().__init__(service)
25
+
26
+ def query(self):
27
+ return Query(self.service, self.entitytype, self.entitytype_plural, self.entity_class, self.parent)
28
+
29
+ def create(self, entity):
30
+ return STAplusBaseDao.create(self, entity)
@@ -0,0 +1,36 @@
1
+ # Copyright (C) 2023-2024 Secure Dimensions GmbH, Munich, Germany.
2
+ #
3
+ # This program is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU Lesser General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # This program is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU Lesser General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU Lesser General Public License
14
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
15
+
16
+ from frost_sta_client.dao.thing import ThingDao as STAThingDao
17
+ from staplus_client.dao.base import BaseDao as STAplusBaseDao
18
+ from staplus_client.query.query import Query
19
+ from staplus_client.model.ext.entity_type import EntityTypes
20
+
21
+ class ThingDao(STAThingDao):
22
+ def __init__(self, service):
23
+ """
24
+ A data access object for operations with the Thing entity
25
+ """
26
+ super().__init__(service)
27
+ entitytype = EntityTypes["Thing"]
28
+ self.entitytype = entitytype["singular"]
29
+ self.entitytype_plural = entitytype["plural"]
30
+ self.entity_class = entitytype["class"]
31
+
32
+ def query(self):
33
+ return Query(self.service, self.entitytype, self.entitytype_plural, self.entity_class, self.parent)
34
+
35
+ def create(self, entity):
36
+ return STAplusBaseDao.create(self, entity)
@@ -0,0 +1,29 @@
1
+ # Copyright (C) 2023-2024 Secure Dimensions GmbH, Munich, Germany.
2
+ #
3
+ # This program is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU Lesser General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # This program is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU Lesser General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU Lesser General Public License
14
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
15
+
16
+ from staplus_client.model import party
17
+ from staplus_client.model import sensor
18
+ from staplus_client.model import observedproperty
19
+ from staplus_client.model import historical_location
20
+ from staplus_client.model import thing
21
+ from staplus_client.model import datastream
22
+ from staplus_client.model import multi_datastream
23
+ from staplus_client.model import license
24
+ from staplus_client.model import campaign
25
+ from staplus_client.model import relation
26
+ from staplus_client.model import observation_group
27
+ from staplus_client.model import observation
28
+ from staplus_client.model import feature_of_interest
29
+ from staplus_client.model import location
@@ -0,0 +1,417 @@
1
+ # Copyright (C) 2023-2024 Secure Dimensions GmbH, Munich, Germany.
2
+ #
3
+ # This program is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU Lesser General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # This program is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU Lesser General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU Lesser General Public License
14
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
15
+ from datetime import datetime
16
+
17
+ from frost_sta_client.utils import check_datetime
18
+ from staplus_client.utils import transform_json_to_entity_list
19
+ from staplus_client.model import entity, datastream, multi_datastream, license, observation_group, party
20
+ from staplus_client.model.ext import entity_list, entity_type
21
+ from staplus_client.dao.campaign import CampaignDao
22
+
23
+
24
+ class Campaign(entity.Entity):
25
+
26
+ def __init__(self,
27
+ name='',
28
+ description='',
29
+ classification=None,
30
+ terms_of_use=None,
31
+ privacy_policy=None,
32
+ creation_time=None,
33
+ start_time=None,
34
+ end_time=None,
35
+ url=None,
36
+ properties=None,
37
+ datastreams=None,
38
+ multi_datastreams=None,
39
+ observation_groups=None,
40
+ party=None,
41
+ license=None,
42
+ **kwargs):
43
+ super().__init__(**kwargs)
44
+ self.name = name
45
+ self.description = description
46
+ self.classification = classification
47
+ self.terms_of_use = terms_of_use
48
+ self.privacy_policy = privacy_policy
49
+ self.creation_time = creation_time
50
+ self.start_time = start_time
51
+ self.end_time = end_time
52
+ self.url = url
53
+ self.properties = properties
54
+ self.datastreams = datastreams
55
+ self.multi_datastreams = multi_datastreams
56
+ self.observation_groups = observation_groups
57
+ self.party = party
58
+ self.license = license
59
+
60
+ def __new__(cls, *args, **kwargs):
61
+ new_campaign = super().__new__(cls)
62
+ attributes = {'_id': None, '_name': '', '_description': '', '_classification': None, '_terms_of_use': '',
63
+ '_privacy_statement': None, '_creation_time': '', '_start_time': None, '_end_start': None,
64
+ '_url': None, '_properties': None,
65
+ '_datastreams': None, '_multi_datastreams': None, 'observation_groups': None,
66
+ '_party': None, '_license': None,
67
+ '_self_link': '', '_service': None}
68
+ for key, value in attributes.items():
69
+ new_campaign.__dict__[key] = value
70
+ return new_campaign
71
+
72
+ @property
73
+ def name(self):
74
+ return self._name
75
+
76
+ @name.setter
77
+ def name(self, value):
78
+ if value is None:
79
+ self._name = None
80
+ return
81
+ if not isinstance(value, str):
82
+ raise ValueError('name should be of type str!')
83
+ self._name = value
84
+
85
+ @property
86
+ def description(self):
87
+ return self._description
88
+
89
+ @description.setter
90
+ def description(self, value):
91
+ if value is None:
92
+ self._description = None
93
+ return
94
+ if not isinstance(value, str):
95
+ raise ValueError('description should be of type str!')
96
+ self._description = value
97
+
98
+ @property
99
+ def classification(self):
100
+ return self._classification
101
+
102
+ @classification.setter
103
+ def classification(self, value):
104
+ if value is None:
105
+ self._classification = None
106
+ return
107
+ if not isinstance(value, str):
108
+ raise ValueError('classification should be of type str!')
109
+ self._classification = value
110
+
111
+ @property
112
+ def terms_of_use(self):
113
+ return self._terms_of_use
114
+
115
+ @terms_of_use.setter
116
+ def terms_of_use(self, value):
117
+ if value is None:
118
+ self._terms_of_use = None
119
+ return
120
+ if not isinstance(value, str):
121
+ raise ValueError('termsOfUse should be of type str!')
122
+ self._terms_of_use = value
123
+
124
+ @property
125
+ def privacy_policy(self):
126
+ return self._privacy_policy
127
+
128
+ @privacy_policy.setter
129
+ def privacy_policy(self, value):
130
+ if value is None:
131
+ self._privacy_policy = None
132
+ return
133
+ if not isinstance(value, str):
134
+ raise ValueError('privacyPolicy should be of type str!')
135
+ self._privacy_policy = value
136
+
137
+ @property
138
+ def creation_time(self):
139
+ return self._creation_time
140
+
141
+ @creation_time.setter
142
+ def creation_time(self, value):
143
+ if value is None:
144
+ self._creation_time = None
145
+ return
146
+ self._creation_time = check_datetime(value, 'creation_time')
147
+
148
+ @property
149
+ def start_time(self):
150
+ return self._start_time
151
+
152
+ @start_time.setter
153
+ def start_time(self, value):
154
+ if value is None:
155
+ self._start_time = None
156
+ return
157
+ self._start_time = check_datetime(value, 'start_time')
158
+
159
+ @property
160
+ def end_time(self):
161
+ return self._end_time
162
+
163
+ @end_time.setter
164
+ def end_time(self, value):
165
+ if value is None:
166
+ self._end_time = None
167
+ return
168
+ self._end_time = check_datetime(value, 'end_time')
169
+
170
+ @property
171
+ def url(self):
172
+ return self._url
173
+
174
+ @url.setter
175
+ def url(self, value):
176
+ if value is None:
177
+ self._url = None
178
+ return
179
+ if not isinstance(value, str):
180
+ raise ValueError('url should be of type str!')
181
+ self._url = value
182
+
183
+ @property
184
+ def properties(self):
185
+ return self._properties
186
+
187
+ @properties.setter
188
+ def properties(self, value):
189
+ if value is None:
190
+ self._properties = None
191
+ return
192
+ if not isinstance(value, dict):
193
+ raise ValueError('properties should be of type dict')
194
+ self._properties = value
195
+
196
+ @property
197
+ def datastreams(self):
198
+ return self._datastreams
199
+
200
+ @datastreams.setter
201
+ def datastreams(self, values):
202
+ if values is None:
203
+ self._datastreams = None
204
+ return
205
+ if isinstance(values, list) and all(isinstance(ds, datastream.Datastream) for ds in values):
206
+ entity_class = entity_type.EntityTypes['Datastream']['class']
207
+ self._datastreams = entity_list.EntityList(entity_class=entity_class, entities=values)
208
+ return
209
+ if not isinstance(values, entity_list.EntityList) or \
210
+ any((not isinstance(ds, datastream.Datastream)) for ds in values.entities):
211
+ raise ValueError('datastreams should be a list of Datastream')
212
+ self._datastreams = values
213
+
214
+ def get_datastreams(self):
215
+ result = self.service.datastreams()
216
+ result.parent = self
217
+ return result
218
+
219
+ @property
220
+ def multi_datastreams(self):
221
+ return self._multi_datastreams
222
+
223
+ @multi_datastreams.setter
224
+ def multi_datastreams(self, values):
225
+ if values is None:
226
+ self._multi_datastreams = None
227
+ return
228
+ if isinstance(values, list) and all(isinstance(ds, multi_datastream.MultiDatastream) for ds in values):
229
+ entity_class = entity_type.EntityTypes['MultiDatastream']['class']
230
+ self._multi_datastreams = entity_list.EntityList(entity_class=entity_class, entities=values)
231
+ return
232
+ if not isinstance(values, entity_list.EntityList) or \
233
+ any((not isinstance(ds, multi_datastream.MultiDatastream)) for ds in values.entities):
234
+ raise ValueError('multi_datastreams should be a list of MultiDatastream')
235
+ self._multi_datastreams = values
236
+
237
+ def get_multi_datastreams(self):
238
+ result = self.service.multi_datastreams()
239
+ result.parent = self
240
+ return result
241
+
242
+ @property
243
+ def observation_groups(self):
244
+ return self._observation_groups
245
+
246
+ @observation_groups.setter
247
+ def observation_groups(self, values):
248
+ if values is None:
249
+ self._observation_groups = None
250
+ return
251
+ if isinstance(values, list) and all(isinstance(gs, observation_group.ObservationGroup) for gs in values):
252
+ entity_class = entity_type.EntityTypes['ObservationGroups']['class']
253
+ self._observation_groups = entity_list.EntityList(entity_class=entity_class, entities=values)
254
+ return
255
+ if not isinstance(values, entity_list.EntityList) or \
256
+ any((not isinstance(gs, observation_group.ObservationGroup)) for gs in values.entities):
257
+ raise ValueError('observation_groups should be a list of ObservationGroup')
258
+ self._observation_groups = values
259
+
260
+ def get_observation_groups(self):
261
+ result = self.service.observation_groups()
262
+ result.parent = self
263
+ return result
264
+
265
+ @property
266
+ def party(self):
267
+ return self._party
268
+
269
+ @party.setter
270
+ def party(self, value):
271
+ if value is None or isinstance(value, party.Party):
272
+ self._party = value
273
+ return
274
+ raise ValueError('party should be of type Party!')
275
+
276
+ def get_party(self):
277
+ result = self.service.party()
278
+ result.parent = self
279
+ return result
280
+
281
+ @property
282
+ def license(self):
283
+ return self._license
284
+
285
+ @license.setter
286
+ def license(self, value):
287
+ if value is None or isinstance(value, license.License):
288
+ self._license = value
289
+ return
290
+ raise ValueError('license should be of type License!')
291
+
292
+ def get_licence(self):
293
+ result = self.service.license()
294
+ result.parent = self
295
+ return result
296
+
297
+ def ensure_service_on_children(self, service):
298
+ if self.datastreams is not None:
299
+ self.datastreams.set_service(service)
300
+ if self.multi_datastreams is not None:
301
+ self.multi_datastreams.set_service(service)
302
+ if self.observation_groups is not None:
303
+ self.observation_groups.set_service(service)
304
+ if self.party is not None:
305
+ self.party.set_service(service)
306
+ if self.license is not None:
307
+ self.license.set_service(service)
308
+
309
+ def __eq__(self, other):
310
+ if other is None:
311
+ return False
312
+ if not isinstance(other, type(self)):
313
+ return False
314
+ if id(self) == id(other):
315
+ return True
316
+ if self.name != other.name:
317
+ return False
318
+ if self.description != other.description:
319
+ return False
320
+ if self.classification != other.classification:
321
+ return False
322
+ if self.terms_of_use != other.terms_of_use:
323
+ return False
324
+ if self.privacy_policy != other.privacy_policy:
325
+ return False
326
+ if self.creation_time is not None and other.creation_time is not None and datetime.fromisoformat(self.creation_time) != datetime.fromisoformat(other.creation_time):
327
+ return False
328
+ if self.start_time is not None and other.start_time is not None and datetime.fromisoformat(self.start_time) != datetime.fromisoformat(other.start_time):
329
+ return False
330
+ if self.end_time is not None and other.end_time is not None and datetime.fromisoformat(self.end_time) != datetime.fromisoformat(other.end_time):
331
+ return False
332
+ if self.url != other.url:
333
+ return False
334
+ if self.properties != other.properties:
335
+ return False
336
+ return True
337
+
338
+ def __ne__(self, other):
339
+ return not self == other
340
+
341
+ def __getstate__(self):
342
+ data = super().__getstate__()
343
+ if self.name is not None and self.name != '':
344
+ data['name'] = self.name
345
+ if self.description is not None and self.description != '':
346
+ data['description'] = self.description
347
+ if self.classification is not None and self.classification != '':
348
+ data['classification'] = self.classification
349
+ if self.terms_of_use is not None and self.terms_of_use != '':
350
+ data['termsOfUse'] = self.terms_of_use
351
+ if self.privacy_policy is not None and self.privacy_policy != '':
352
+ data['privacyPolicy'] = self.privacy_policy
353
+ if self.creation_time is not None:
354
+ data['creationTime'] = datetime.fromisoformat(self.creation_time)
355
+ if self.start_time is not None:
356
+ data['startTime'] = datetime.fromisoformat(self.start_time)
357
+ if self.end_time is not None:
358
+ data['endTime'] = datetime.fromisoformat(self.end_time)
359
+ if self.url is not None and self.url != '':
360
+ data['url'] = self.url
361
+ if self.properties is not None and self.terms_of_use != {}:
362
+ data['properties'] = self.properties
363
+ if self._datastreams is not None and len(self.datastreams.entities) > 0:
364
+ data['Datastreams'] = self.datastreams.__getstate__()
365
+ if self._multi_datastreams is not None and len(self.multi_datastreams.entities) > 0:
366
+ data['MultiDatastreams'] = self.multi_datastreams.__getstate__()
367
+ if self._observation_groups is not None and len(self.observation_groups.entities) > 0:
368
+ data['ObservationGroups'] = self.observation_groups.__getstate__()
369
+ if self._party is not None:
370
+ data['Party'] = self.party
371
+ if self._license is not None:
372
+ data['License'] = self.license
373
+ return data
374
+
375
+ def __setstate__(self, state):
376
+ super().__setstate__(state)
377
+
378
+ self.name = state.get("name", None)
379
+ self.description = state.get("description", None)
380
+ self.classification = state.get("classification", None)
381
+ self.terms_of_use = state.get("termsOfUse", None)
382
+ self.privacy_policy = state.get("privacyPolicy", None)
383
+ self.creation_time = state.get("creationTime", None)
384
+ self.start_time = state.get("startTime", None)
385
+ self.end_time = state.get("endTime", None)
386
+ self.properties = state.get("properties", None)
387
+ self.url = state.get("url", None)
388
+
389
+ if state.get("Datastreams", None) is not None and isinstance(state["Datastreams"], list):
390
+ entity_class = entity_type.EntityTypes['Datastream']['class']
391
+ self.datastreams = transform_json_to_entity_list(state['Datastreams'], entity_class)
392
+ self.datastreams.next_link = state.get("Datastreams@iot.nextLink", None)
393
+ self.datastreams.count = state.get("Datastreams@iot.count", None)
394
+ if state.get("MultiDatastreams", None) is not None and isinstance(state["MultiDatastreams"], list):
395
+ entity_class = entity_type.EntityTypes['MultiDatastream']['class']
396
+ self.multi_datastreams = transform_json_to_entity_list(state['MultiDatastreams'], entity_class)
397
+ self.multi_datastreams.next_link = state.get("MultiDatastreams@iot.nextLink", None)
398
+ self.multi_datastreams.count = state.get("MultiDatastreams@iot.count", None)
399
+ if state.get("Party", None) is not None:
400
+ self.party = party.Party()
401
+ self.party.__setstate__(state["Party"])
402
+ if state.get("License", None) is not None:
403
+ self.license = license.License()
404
+ self.license.__setstate__(state["License"])
405
+ if state.get("ObservationGroups", None) is not None and isinstance(state["ObservationGroups"], list):
406
+ entity_class = entity_type.EntityTypes['ObservationGroup']['class']
407
+ self.observation_groups = transform_json_to_entity_list(state['ObservationGroups'], entity_class)
408
+ self.observation_groups.next_link = state.get("ObservationGroups@iot.nextLink", None)
409
+ self.observation_groups.count = state.get("ObservationGroups@iot.count", None)
410
+
411
+ def get_dao(self, service):
412
+ return CampaignDao(service)
413
+
414
+ def clone(self):
415
+ entity = self.__class__()
416
+ entity.id = self.id
417
+ return entity