dbentity 1.0.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.
- dbentity/__init__.py +6 -0
- dbentity/_version.py +24 -0
- dbentity/attribute.py +417 -0
- dbentity/db_control.py +328 -0
- dbentity/db_entity.py +175 -0
- dbentity/db_query.py +142 -0
- dbentity/db_upgrade.py +49 -0
- dbentity/entity.py +125 -0
- dbentity-1.0.0.dist-info/METADATA +126 -0
- dbentity-1.0.0.dist-info/RECORD +13 -0
- dbentity-1.0.0.dist-info/WHEEL +5 -0
- dbentity-1.0.0.dist-info/licenses/LICENSE +21 -0
- dbentity-1.0.0.dist-info/top_level.txt +1 -0
dbentity/__init__.py
ADDED
dbentity/_version.py
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# file generated by vcs-versioning
|
|
2
|
+
# don't change, don't track in version control
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
__all__ = [
|
|
6
|
+
"__version__",
|
|
7
|
+
"__version_tuple__",
|
|
8
|
+
"version",
|
|
9
|
+
"version_tuple",
|
|
10
|
+
"__commit_id__",
|
|
11
|
+
"commit_id",
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
version: str
|
|
15
|
+
__version__: str
|
|
16
|
+
__version_tuple__: tuple[int | str, ...]
|
|
17
|
+
version_tuple: tuple[int | str, ...]
|
|
18
|
+
commit_id: str | None
|
|
19
|
+
__commit_id__: str | None
|
|
20
|
+
|
|
21
|
+
__version__ = version = '1.0.0'
|
|
22
|
+
__version_tuple__ = version_tuple = (1, 0, 0)
|
|
23
|
+
|
|
24
|
+
__commit_id__ = commit_id = None
|
dbentity/attribute.py
ADDED
|
@@ -0,0 +1,417 @@
|
|
|
1
|
+
import time as _time
|
|
2
|
+
import datetime as _datetime
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class AttributeException(Exception):
|
|
6
|
+
"""General Data object error"""
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class NumberOutOfRangeException(AttributeException):
|
|
10
|
+
"""If number is out of range"""
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class WrongNumberFormatException(AttributeException):
|
|
14
|
+
"""If number is out of range"""
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def last_time_to_string(secondsf):
|
|
18
|
+
seconds = int(secondsf)
|
|
19
|
+
minutes = int(seconds // 60)
|
|
20
|
+
hours = int(minutes // 60)
|
|
21
|
+
days = int(hours // 24)
|
|
22
|
+
seconds %= 60
|
|
23
|
+
minutes %= 60
|
|
24
|
+
hours %= 24
|
|
25
|
+
since_str = ''
|
|
26
|
+
if days:
|
|
27
|
+
since_str = f"{days}d {hours:02d}h {minutes:02d}m {seconds:02d}s"
|
|
28
|
+
elif hours:
|
|
29
|
+
since_str = f"{hours:02d}h {minutes:02d}m {seconds:02d}s"
|
|
30
|
+
elif minutes:
|
|
31
|
+
since_str = f"{minutes:02d}m {seconds:02d}s"
|
|
32
|
+
elif seconds > 10:
|
|
33
|
+
since_str = f"{secondsf:.1f}s"
|
|
34
|
+
elif seconds > 1:
|
|
35
|
+
since_str = f"{secondsf:.2f}s"
|
|
36
|
+
elif seconds > .1:
|
|
37
|
+
since_str = f"{secondsf:.3f}s"
|
|
38
|
+
else:
|
|
39
|
+
since_str = f"{secondsf / 1000:.1f}ms"
|
|
40
|
+
return since_str
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class Attribute():
|
|
44
|
+
CREATE = True
|
|
45
|
+
SAVE = True
|
|
46
|
+
INDEX = False
|
|
47
|
+
CONNECTION = False
|
|
48
|
+
CONNECTIONS = False
|
|
49
|
+
FUNCTION = None
|
|
50
|
+
|
|
51
|
+
def __init__(
|
|
52
|
+
self,
|
|
53
|
+
name,
|
|
54
|
+
db_key=None,
|
|
55
|
+
form_key=None,
|
|
56
|
+
default=None):
|
|
57
|
+
self._name = name
|
|
58
|
+
self._db_key = db_key
|
|
59
|
+
self._form_key = form_key
|
|
60
|
+
self._default = default
|
|
61
|
+
|
|
62
|
+
def __repr__(self):
|
|
63
|
+
return f'{self.__class__.__name__}:{self._name}'
|
|
64
|
+
|
|
65
|
+
@property
|
|
66
|
+
def name(self):
|
|
67
|
+
return self._name
|
|
68
|
+
|
|
69
|
+
@property
|
|
70
|
+
def db_key(self):
|
|
71
|
+
if self._db_key is None:
|
|
72
|
+
self._db_key = self._name
|
|
73
|
+
return self._db_key
|
|
74
|
+
|
|
75
|
+
@property
|
|
76
|
+
def form_key(self):
|
|
77
|
+
return self._form_key
|
|
78
|
+
|
|
79
|
+
@property
|
|
80
|
+
def default(self):
|
|
81
|
+
return self._default
|
|
82
|
+
|
|
83
|
+
def is_name(self, name):
|
|
84
|
+
return name == self._name
|
|
85
|
+
|
|
86
|
+
def is_form_key(self, form_key):
|
|
87
|
+
return form_key == self._form_key
|
|
88
|
+
|
|
89
|
+
def to_template(self, value):
|
|
90
|
+
if value is None:
|
|
91
|
+
value = ''
|
|
92
|
+
return value
|
|
93
|
+
|
|
94
|
+
def from_form(self, value):
|
|
95
|
+
if value is None and self._default is not None:
|
|
96
|
+
value = self._default
|
|
97
|
+
return value
|
|
98
|
+
|
|
99
|
+
def to_value(self, value):
|
|
100
|
+
return value
|
|
101
|
+
|
|
102
|
+
def to_json(self, value):
|
|
103
|
+
return value
|
|
104
|
+
|
|
105
|
+
def from_value(self, value):
|
|
106
|
+
return value
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
class IndexAttribute(Attribute):
|
|
110
|
+
CREATE = False
|
|
111
|
+
SAVE = False
|
|
112
|
+
INDEX = True
|
|
113
|
+
|
|
114
|
+
def __init__(
|
|
115
|
+
self,
|
|
116
|
+
name=None,
|
|
117
|
+
db_key=None):
|
|
118
|
+
if name is None:
|
|
119
|
+
name = 'uid'
|
|
120
|
+
if db_key is None:
|
|
121
|
+
db_key = 'id'
|
|
122
|
+
super().__init__(name, db_key=db_key)
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
class CreateIndexAttribute(IndexAttribute):
|
|
126
|
+
CREATE = True
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
class DatetimeAttribute(Attribute):
|
|
130
|
+
def to_json(self, value):
|
|
131
|
+
if isinstance(value, _datetime.datetime):
|
|
132
|
+
return {
|
|
133
|
+
'datetime': value.strftime('%Y-%m-%d %H:%M:%S'),
|
|
134
|
+
'datetime_short': value.strftime('%Y%m%d%H%M%S'),
|
|
135
|
+
'timestamp': value.timestamp(),
|
|
136
|
+
}
|
|
137
|
+
return value
|
|
138
|
+
|
|
139
|
+
def to_template(self, value):
|
|
140
|
+
if isinstance(value, _datetime.datetime):
|
|
141
|
+
return {
|
|
142
|
+
'datetime': value.strftime('%Y-%m-%d %H:%M:%S'),
|
|
143
|
+
'datetime_short': value.strftime('%Y%m%d%H%M%S'),
|
|
144
|
+
'timestamp': value.timestamp(),
|
|
145
|
+
}
|
|
146
|
+
return value
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
class LastTimeAttribute(Attribute):
|
|
150
|
+
def to_json(self, value):
|
|
151
|
+
if isinstance(value, (int, float)):
|
|
152
|
+
return _time.time() - value
|
|
153
|
+
|
|
154
|
+
def to_value(self, value):
|
|
155
|
+
if isinstance(value, (int, float)):
|
|
156
|
+
return _time.time() - value
|
|
157
|
+
|
|
158
|
+
def to_template(self, value):
|
|
159
|
+
if isinstance(value, (int, float)):
|
|
160
|
+
secondsf = _time.time() - value
|
|
161
|
+
since_str = last_time_to_string(secondsf)
|
|
162
|
+
return {
|
|
163
|
+
'timestamp': value,
|
|
164
|
+
'since_sec': secondsf,
|
|
165
|
+
'since_str': since_str}
|
|
166
|
+
return value
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
class MinLastTimeAttribute(Attribute):
|
|
170
|
+
FUNCTION = 'MIN'
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
class MaxLastTimeAttribute(Attribute):
|
|
174
|
+
FUNCTION = 'MAX'
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
class StringAttribute(Attribute):
|
|
178
|
+
pass
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
class BytesAttribute(Attribute):
|
|
182
|
+
def to_json(self, value):
|
|
183
|
+
if value is None:
|
|
184
|
+
return None
|
|
185
|
+
return repr(bytes(value))
|
|
186
|
+
|
|
187
|
+
def to_template(self, value):
|
|
188
|
+
if value is None:
|
|
189
|
+
return None
|
|
190
|
+
return repr(bytes(value))
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
class PasswordAttribute(Attribute):
|
|
194
|
+
def to_template(self, value):
|
|
195
|
+
return ""
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
class BooleanAttribute(Attribute):
|
|
199
|
+
def from_form(self, value):
|
|
200
|
+
if value is None and self._default is not None:
|
|
201
|
+
value = self._default
|
|
202
|
+
return bool(value)
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
class IntegerAttribute(Attribute):
|
|
206
|
+
def __init__(
|
|
207
|
+
self,
|
|
208
|
+
name,
|
|
209
|
+
db_key=None,
|
|
210
|
+
form_key=None,
|
|
211
|
+
default=None,
|
|
212
|
+
minimal=None,
|
|
213
|
+
maximal=None):
|
|
214
|
+
self._min = minimal
|
|
215
|
+
self._max = maximal
|
|
216
|
+
super().__init__(
|
|
217
|
+
name,
|
|
218
|
+
db_key=db_key,
|
|
219
|
+
form_key=form_key,
|
|
220
|
+
default=default)
|
|
221
|
+
|
|
222
|
+
def from_form(self, value):
|
|
223
|
+
if not value:
|
|
224
|
+
return None
|
|
225
|
+
if not value.lstrip('+-').isdigit():
|
|
226
|
+
raise WrongNumberFormatException()
|
|
227
|
+
value = int(value)
|
|
228
|
+
if self._min is not None and value < self._min:
|
|
229
|
+
raise NumberOutOfRangeException()
|
|
230
|
+
if self._max is not None and value > self._max:
|
|
231
|
+
raise NumberOutOfRangeException()
|
|
232
|
+
return value
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
class SumIntegerAttribute(Attribute):
|
|
236
|
+
FUNCTION = 'SUM'
|
|
237
|
+
|
|
238
|
+
def __init__(
|
|
239
|
+
self,
|
|
240
|
+
name,
|
|
241
|
+
db_key=None,
|
|
242
|
+
form_key=None,
|
|
243
|
+
default=None,
|
|
244
|
+
minimal=None,
|
|
245
|
+
maximal=None):
|
|
246
|
+
self._min = minimal
|
|
247
|
+
self._max = maximal
|
|
248
|
+
super().__init__(
|
|
249
|
+
name,
|
|
250
|
+
db_key=db_key,
|
|
251
|
+
form_key=form_key,
|
|
252
|
+
default=default)
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
class IntegerArrayAttribute(Attribute):
|
|
256
|
+
def __init__(
|
|
257
|
+
self,
|
|
258
|
+
name,
|
|
259
|
+
db_key=None,
|
|
260
|
+
form_key=None,
|
|
261
|
+
default=None,
|
|
262
|
+
minimal=None,
|
|
263
|
+
maximal=None):
|
|
264
|
+
self._min = minimal
|
|
265
|
+
self._max = maximal
|
|
266
|
+
super().__init__(
|
|
267
|
+
name,
|
|
268
|
+
db_key=db_key,
|
|
269
|
+
form_key=form_key,
|
|
270
|
+
default=default)
|
|
271
|
+
|
|
272
|
+
def from_form(self, values):
|
|
273
|
+
if not values:
|
|
274
|
+
return None
|
|
275
|
+
if not isinstance(values, (list, tuple)):
|
|
276
|
+
values = [values]
|
|
277
|
+
new_values = set()
|
|
278
|
+
if values:
|
|
279
|
+
for value in values:
|
|
280
|
+
if not value.lstrip('+-').isdigit():
|
|
281
|
+
raise WrongNumberFormatException()
|
|
282
|
+
new_value = int(value)
|
|
283
|
+
if self._min is not None and new_value < self._min:
|
|
284
|
+
raise NumberOutOfRangeException()
|
|
285
|
+
if self._max is not None and new_value > self._max:
|
|
286
|
+
raise NumberOutOfRangeException()
|
|
287
|
+
new_values.add(new_value)
|
|
288
|
+
return sorted(new_values)
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
class FixedPointAttribute(Attribute):
|
|
292
|
+
def __init__(
|
|
293
|
+
self,
|
|
294
|
+
name,
|
|
295
|
+
db_key=None,
|
|
296
|
+
form_key=None,
|
|
297
|
+
default=None,
|
|
298
|
+
fp=0,
|
|
299
|
+
minimal=None,
|
|
300
|
+
maximal=None):
|
|
301
|
+
self._fp = fp
|
|
302
|
+
self._min = minimal
|
|
303
|
+
self._max = maximal
|
|
304
|
+
super().__init__(
|
|
305
|
+
name,
|
|
306
|
+
db_key=db_key,
|
|
307
|
+
form_key=form_key,
|
|
308
|
+
default=default)
|
|
309
|
+
|
|
310
|
+
def from_form(self, value):
|
|
311
|
+
if value is None:
|
|
312
|
+
return None
|
|
313
|
+
value = value.replace(',', '.')
|
|
314
|
+
if not value.lstrip('+-').replace('.', '', 1).isdigit():
|
|
315
|
+
raise WrongNumberFormatException()
|
|
316
|
+
value = float(value)
|
|
317
|
+
if self._min is not None and value < self._min:
|
|
318
|
+
raise NumberOutOfRangeException()
|
|
319
|
+
if self._max is not None and value > self._max:
|
|
320
|
+
raise NumberOutOfRangeException()
|
|
321
|
+
return round(value * 10 ** self._fp)
|
|
322
|
+
|
|
323
|
+
def to_template(self, value):
|
|
324
|
+
if value is None:
|
|
325
|
+
return ''
|
|
326
|
+
return value / 10 ** self._fp
|
|
327
|
+
|
|
328
|
+
def to_value(self, value):
|
|
329
|
+
if value is None:
|
|
330
|
+
return None
|
|
331
|
+
return value / 10 ** self._fp
|
|
332
|
+
|
|
333
|
+
def from_value(self, value):
|
|
334
|
+
if value is None:
|
|
335
|
+
return None
|
|
336
|
+
return value * 10 ** self._fp
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
class SumFixedPointAttribute(Attribute):
|
|
340
|
+
FUNCTION = 'SUM'
|
|
341
|
+
|
|
342
|
+
def __init__(
|
|
343
|
+
self,
|
|
344
|
+
name,
|
|
345
|
+
db_key=None,
|
|
346
|
+
form_key=None,
|
|
347
|
+
default=None,
|
|
348
|
+
fp=0,
|
|
349
|
+
minimal=None,
|
|
350
|
+
maximal=None):
|
|
351
|
+
self._fp = fp
|
|
352
|
+
self._min = minimal
|
|
353
|
+
self._max = maximal
|
|
354
|
+
super().__init__(
|
|
355
|
+
name,
|
|
356
|
+
db_key=db_key,
|
|
357
|
+
form_key=form_key,
|
|
358
|
+
default=default)
|
|
359
|
+
|
|
360
|
+
def to_template(self, value):
|
|
361
|
+
if value is None:
|
|
362
|
+
return ''
|
|
363
|
+
return value / 10 ** self._fp
|
|
364
|
+
|
|
365
|
+
def to_value(self, value):
|
|
366
|
+
if value is None:
|
|
367
|
+
return None
|
|
368
|
+
return value / 10 ** self._fp
|
|
369
|
+
|
|
370
|
+
|
|
371
|
+
class ConnectionAttribute(Attribute):
|
|
372
|
+
SAVE = False
|
|
373
|
+
CONNECTION = True
|
|
374
|
+
|
|
375
|
+
def __init__(
|
|
376
|
+
self,
|
|
377
|
+
name,
|
|
378
|
+
sub_entity=None,
|
|
379
|
+
db_key=None,
|
|
380
|
+
conn_key=None):
|
|
381
|
+
self._sub_entity = sub_entity
|
|
382
|
+
self._conn_key = conn_key
|
|
383
|
+
super().__init__(name, db_key=db_key)
|
|
384
|
+
|
|
385
|
+
@property
|
|
386
|
+
def sub_entity(self):
|
|
387
|
+
return self._sub_entity
|
|
388
|
+
|
|
389
|
+
@property
|
|
390
|
+
def db_key(self):
|
|
391
|
+
if self._db_key is None:
|
|
392
|
+
self._db_key = f'{self._name}_id'
|
|
393
|
+
return self._db_key
|
|
394
|
+
|
|
395
|
+
@property
|
|
396
|
+
def conn_key(self):
|
|
397
|
+
if self._conn_key is None:
|
|
398
|
+
self._conn_key = 'id'
|
|
399
|
+
return self._conn_key
|
|
400
|
+
|
|
401
|
+
@property
|
|
402
|
+
def save(self):
|
|
403
|
+
return False
|
|
404
|
+
|
|
405
|
+
|
|
406
|
+
class SubElementsAttribute(Attribute):
|
|
407
|
+
SAVE = False
|
|
408
|
+
CONNECTIONS = True
|
|
409
|
+
|
|
410
|
+
def __init__(
|
|
411
|
+
self,
|
|
412
|
+
name):
|
|
413
|
+
super().__init__(name, db_key=False)
|
|
414
|
+
|
|
415
|
+
@property
|
|
416
|
+
def save(self):
|
|
417
|
+
return False
|