sysnet-pyutils 1.0.7__py3-none-any.whl → 1.1.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.
- sysnet_pyutils/data_utils.py +323 -0
- sysnet_pyutils/ident.py +4 -4
- sysnet_pyutils/models/__init__.py +10 -0
- sysnet_pyutils/models/general.py +262 -0
- sysnet_pyutils/utils.py +145 -71
- {sysnet_pyutils-1.0.7.dist-info → sysnet_pyutils-1.1.0.dist-info}/METADATA +30 -9
- sysnet_pyutils-1.1.0.dist-info/RECORD +17 -0
- {sysnet_pyutils-1.0.7.dist-info → sysnet_pyutils-1.1.0.dist-info}/WHEEL +1 -1
- sysnet_pyutils-1.0.7.dist-info/RECORD +0 -14
- {sysnet_pyutils-1.0.7.dist-info → sysnet_pyutils-1.1.0.dist-info}/LICENSE +0 -0
- {sysnet_pyutils-1.0.7.dist-info → sysnet_pyutils-1.1.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,323 @@
|
|
1
|
+
from datetime import date, datetime
|
2
|
+
from typing import Union, List, Any
|
3
|
+
|
4
|
+
import pytz
|
5
|
+
|
6
|
+
from utils import is_valid_email, TZ
|
7
|
+
|
8
|
+
|
9
|
+
def get_dict_value(data: dict, item_name: str) -> Union[Any, None]:
|
10
|
+
"""
|
11
|
+
Extrahuje hodnotu ze slovníku. Používá se pro load dat JSON do objektů
|
12
|
+
|
13
|
+
:param data: Slovník s daty JSON
|
14
|
+
:param item_name: Název položky
|
15
|
+
:return: Hodnota
|
16
|
+
"""
|
17
|
+
if data is None or item_name in [None, '']:
|
18
|
+
return None
|
19
|
+
if item_name in data:
|
20
|
+
return data[item_name]
|
21
|
+
return None
|
22
|
+
|
23
|
+
|
24
|
+
def get_dict_value_string(data: dict, item_name: str) -> str:
|
25
|
+
"""
|
26
|
+
Extrahuje textovou hodnotu ze slovníku. Pokud je vstupní hodnota None, vrací prázdný string.
|
27
|
+
|
28
|
+
:param data: Slovník s daty JSON
|
29
|
+
:param item_name: Název položky
|
30
|
+
:return: Hodnota
|
31
|
+
"""
|
32
|
+
out = ''
|
33
|
+
if data is None or item_name in [None, '']:
|
34
|
+
return out
|
35
|
+
v = get_dict_value(data, item_name)
|
36
|
+
if v not in [None, '']:
|
37
|
+
out = str(v)
|
38
|
+
return out
|
39
|
+
|
40
|
+
|
41
|
+
def get_dict_value_email(data: dict, item_name: str) -> Union[Any, None]:
|
42
|
+
"""
|
43
|
+
Extrahuje emailovou adresu ze slovníku.
|
44
|
+
|
45
|
+
:param data: Slovník s daty JSON
|
46
|
+
:param item_name: Název položky
|
47
|
+
:return: Hodnota
|
48
|
+
"""
|
49
|
+
if data is None or item_name in [None, '']:
|
50
|
+
return None
|
51
|
+
out = get_dict_value(data, item_name)
|
52
|
+
if not is_valid_email(out):
|
53
|
+
return None
|
54
|
+
return out
|
55
|
+
|
56
|
+
|
57
|
+
def get_dict_value_bool(data: dict, item_name: str) -> bool:
|
58
|
+
"""
|
59
|
+
Extrahuje logickou hodnotu ze slovníku
|
60
|
+
|
61
|
+
:param data: Slovník s daty JSON
|
62
|
+
:param item_name: Název položky
|
63
|
+
:return: Hodnota
|
64
|
+
"""
|
65
|
+
if data is None or item_name in [None, '']:
|
66
|
+
return False
|
67
|
+
v = get_dict_value(data, item_name)
|
68
|
+
if v in [None, '']:
|
69
|
+
return False
|
70
|
+
if v.lower() in ('true', '1', 't'):
|
71
|
+
return True
|
72
|
+
return False
|
73
|
+
|
74
|
+
|
75
|
+
def get_dict_value_int(data: dict, item_name: str, spare_item_name: Union[str, None] = None) -> int:
|
76
|
+
"""
|
77
|
+
Extrahuje celočíselnou hodnotu ze slovníku. Pokud nenajde hlavní položku, hledá záložní.
|
78
|
+
|
79
|
+
:param data: Slovník s daty JSON
|
80
|
+
:param item_name: Název položky
|
81
|
+
:param spare_item_name: Název záložní položky
|
82
|
+
:return: Hodnota
|
83
|
+
"""
|
84
|
+
if data is None or item_name in [None, '']:
|
85
|
+
return 0
|
86
|
+
v = get_dict_value(data, item_name)
|
87
|
+
out = 0
|
88
|
+
if v is not None:
|
89
|
+
try:
|
90
|
+
out = int(v)
|
91
|
+
except (ValueError, TypeError):
|
92
|
+
out = 0
|
93
|
+
if (out == 0) and (spare_item_name is not None):
|
94
|
+
v1 = get_dict_value(data, spare_item_name)
|
95
|
+
if v1 is not None:
|
96
|
+
try:
|
97
|
+
out = int(v1)
|
98
|
+
except (ValueError, TypeError):
|
99
|
+
out = 0
|
100
|
+
return out
|
101
|
+
|
102
|
+
|
103
|
+
def get_dict_value_list(data: dict, item_name: str, spare_item_name: str = None) -> Union[List[str], None]:
|
104
|
+
v = get_dict_value(data, item_name)
|
105
|
+
if v in [None, '']:
|
106
|
+
v = get_dict_value(data, spare_item_name)
|
107
|
+
if v in [None, '']:
|
108
|
+
return None
|
109
|
+
out = [i.strip() for i in v[1:-1].replace('"',"").split(';')]
|
110
|
+
if len(out) == 1 and out[0] == '':
|
111
|
+
out = []
|
112
|
+
return out
|
113
|
+
|
114
|
+
|
115
|
+
def get_dict_value_float(data: dict, item_name: str, spare_item_name: str = None) -> Union[float, None]:
|
116
|
+
v = get_dict_value(data, item_name)
|
117
|
+
out: float = float(0)
|
118
|
+
if v is not None:
|
119
|
+
try:
|
120
|
+
if isinstance(v, str):
|
121
|
+
v = v.replace(',', '.')
|
122
|
+
out = float(v)
|
123
|
+
except (ValueError, TypeError):
|
124
|
+
out = float(0)
|
125
|
+
if (out == float(0)) and (spare_item_name is not None):
|
126
|
+
v1 = get_dict_value(data, spare_item_name)
|
127
|
+
if v1 is not None:
|
128
|
+
try:
|
129
|
+
out = float(v1)
|
130
|
+
except (ValueError, TypeError):
|
131
|
+
out = float(0)
|
132
|
+
return out
|
133
|
+
|
134
|
+
|
135
|
+
def get_dict_value_date(data: dict, item_name: str) -> Union[date, None]:
|
136
|
+
"""
|
137
|
+
Konvertuje ISO nebo český formát do data
|
138
|
+
|
139
|
+
:param data:
|
140
|
+
:param item_name:
|
141
|
+
:return:
|
142
|
+
"""
|
143
|
+
v = get_dict_value(data, item_name)
|
144
|
+
if v is None:
|
145
|
+
return None
|
146
|
+
out = convert_iso_to_date(v)
|
147
|
+
if out is None:
|
148
|
+
out = convert_cz_to_date(v)
|
149
|
+
return out
|
150
|
+
|
151
|
+
|
152
|
+
def convert_iso_to_date(text: str) -> Union[date, None]:
|
153
|
+
try:
|
154
|
+
out = date.fromisoformat(text)
|
155
|
+
return out
|
156
|
+
except (ValueError, TypeError):
|
157
|
+
return None
|
158
|
+
|
159
|
+
|
160
|
+
def convert_iso_to_datetime(text: str) -> Union[datetime, None]:
|
161
|
+
try:
|
162
|
+
out = datetime.fromisoformat(text)
|
163
|
+
local_tz = pytz.timezone(TZ)
|
164
|
+
out = out.astimezone(local_tz)
|
165
|
+
return out
|
166
|
+
except (ValueError, TypeError):
|
167
|
+
return None
|
168
|
+
|
169
|
+
|
170
|
+
def convert_cz_to_date(text: str) -> Union[date, None]:
|
171
|
+
try:
|
172
|
+
out = datetime.strptime(text, '%d.%m.%Y').date()
|
173
|
+
return out
|
174
|
+
except (ValueError, TypeError):
|
175
|
+
return None
|
176
|
+
|
177
|
+
|
178
|
+
def convert_cz_to_datetime(text: str) -> Union[datetime, None]:
|
179
|
+
try:
|
180
|
+
out = datetime.strptime(text, '%d.%m.%Y %H:%M:%S')
|
181
|
+
local_tz = pytz.timezone(TZ)
|
182
|
+
out = out.astimezone(local_tz)
|
183
|
+
return out
|
184
|
+
except (ValueError, TypeError):
|
185
|
+
return None
|
186
|
+
|
187
|
+
|
188
|
+
def get_dict_value_datetime(data: dict, item_name: str) -> Union[datetime, None]:
|
189
|
+
"""
|
190
|
+
Konvertuje ISO nebo český formát do datatime
|
191
|
+
|
192
|
+
:param data:
|
193
|
+
:param item_name:
|
194
|
+
:return:
|
195
|
+
"""
|
196
|
+
v = get_dict_value(data, item_name)
|
197
|
+
out = None
|
198
|
+
if v is None:
|
199
|
+
return None
|
200
|
+
try:
|
201
|
+
if isinstance(v, datetime):
|
202
|
+
return v
|
203
|
+
elif isinstance(v, date):
|
204
|
+
gmt_tz = pytz.timezone('GMT')
|
205
|
+
return datetime(year=v.year, month=v.month, day=v.day, tzinfo=gmt_tz)
|
206
|
+
elif isinstance(v, str):
|
207
|
+
out = convert_iso_to_datetime(v)
|
208
|
+
if out is None:
|
209
|
+
out = convert_cz_to_datetime(v)
|
210
|
+
return out
|
211
|
+
except (ValueError, TypeError):
|
212
|
+
return None
|
213
|
+
|
214
|
+
|
215
|
+
def get_object_attr(obj: Any, attr: str) -> Union[Any, None]:
|
216
|
+
"""
|
217
|
+
Získa hodnotu atributu z datového objektu
|
218
|
+
|
219
|
+
:param obj:
|
220
|
+
:param attr:
|
221
|
+
:return:
|
222
|
+
"""
|
223
|
+
if obj is None:
|
224
|
+
return None
|
225
|
+
if hasattr(obj, attr):
|
226
|
+
return getattr(obj, attr)
|
227
|
+
return None
|
228
|
+
|
229
|
+
|
230
|
+
def get_dict_item_value_list(data: dict, item_name: str, alt_item_names: List[str] = None) -> Union[list, None]:
|
231
|
+
out = []
|
232
|
+
v = get_dict_item_value(data=data, item_name=item_name, alt_item_names=alt_item_names, blank=False)
|
233
|
+
if v is None:
|
234
|
+
return out
|
235
|
+
if isinstance(v, list):
|
236
|
+
out = v
|
237
|
+
else:
|
238
|
+
out.append(v)
|
239
|
+
return out
|
240
|
+
|
241
|
+
|
242
|
+
def get_dict_item_value(data: dict, item_name: str, alt_item_names: List[str] = None, blank=True) -> Union[Any, None]:
|
243
|
+
out = None
|
244
|
+
if blank:
|
245
|
+
out = ''
|
246
|
+
if (data is None) or (item_name is None):
|
247
|
+
return out
|
248
|
+
if item_name in data:
|
249
|
+
out = data[item_name]
|
250
|
+
elif alt_item_names is not None:
|
251
|
+
if alt_item_names:
|
252
|
+
for name in alt_item_names:
|
253
|
+
if name in data:
|
254
|
+
out = data[name]
|
255
|
+
break
|
256
|
+
return out
|
257
|
+
|
258
|
+
|
259
|
+
def get_dict_code_value_list(data: dict, item_name_code: str, item_name_value: str) -> Union[list[tuple[Any, Any]], None]:
|
260
|
+
"""
|
261
|
+
Vrátí z dvojice položek slovníku tuple code/value
|
262
|
+
|
263
|
+
:param data:
|
264
|
+
:param item_name_code:
|
265
|
+
:param item_name_value:
|
266
|
+
:return:
|
267
|
+
"""
|
268
|
+
codes = get_dict_value_list(data, item_name_code)
|
269
|
+
values = get_dict_value_list(data, item_name_value)
|
270
|
+
if not codes or not values:
|
271
|
+
return None
|
272
|
+
if len(codes) != len(values):
|
273
|
+
return None
|
274
|
+
out = []
|
275
|
+
i = 0
|
276
|
+
for c in codes:
|
277
|
+
v = values[i]
|
278
|
+
i += 1
|
279
|
+
out.append((c,v))
|
280
|
+
return out
|
281
|
+
|
282
|
+
def adjust_datetime_to_utc(date_value: Union[date, datetime], for_store: bool = False) -> Union[datetime, date]:
|
283
|
+
"""
|
284
|
+
Nastavení času pro uložení. Použije se časová zóna UTF
|
285
|
+
|
286
|
+
:param date_value: Datová hodnota
|
287
|
+
:param for_store: Pro uložení (převede se vždy na datetime)
|
288
|
+
:return: nastavená hodnota
|
289
|
+
"""
|
290
|
+
if date_value in [None, '']:
|
291
|
+
return date_value
|
292
|
+
if for_store:
|
293
|
+
if isinstance(date_value, date):
|
294
|
+
return datetime(year=date_value.year, month=date_value.month, day=date_value.day, tzinfo=pytz.UTC)
|
295
|
+
if isinstance(date_value, datetime):
|
296
|
+
if date_value.tzinfo is None:
|
297
|
+
date_value.replace(tzinfo=pytz.UTC)
|
298
|
+
return date(year=date_value.year, month=date_value.month, day=date_value.day)
|
299
|
+
return date_value
|
300
|
+
|
301
|
+
|
302
|
+
def dict_convert_date_to_str(data: dict):
|
303
|
+
"""
|
304
|
+
Rekurzivně převede všechny časové položky ve slovníku na ISO text
|
305
|
+
|
306
|
+
:param data: dictionary
|
307
|
+
:return:
|
308
|
+
"""
|
309
|
+
if isinstance(data, dict):
|
310
|
+
for k, v in data.items():
|
311
|
+
if isinstance(v, datetime):
|
312
|
+
if v.tzinfo is None:
|
313
|
+
v_gmt = v.replace(tzinfo=pytz.utc)
|
314
|
+
else:
|
315
|
+
v_gmt = v.astimezone(pytz.utc)
|
316
|
+
data[k] = v_gmt.strftime('%Y-%m-%dT%H:%M:%SZ')
|
317
|
+
elif isinstance(v, dict):
|
318
|
+
dict_convert_date_to_str(v)
|
319
|
+
elif isinstance(v, list):
|
320
|
+
for item in v:
|
321
|
+
dict_convert_date_to_str(item)
|
322
|
+
else:
|
323
|
+
pass
|
sysnet_pyutils/ident.py
CHANGED
@@ -53,7 +53,7 @@ def generate_tiny_uuid():
|
|
53
53
|
return out[-ID_LENGTH:]
|
54
54
|
|
55
55
|
|
56
|
-
def generate_id12(three_char_prefix):
|
56
|
+
def generate_id12(three_char_prefix: str) -> str:
|
57
57
|
"""
|
58
58
|
Vygeneruje 12místný alfanumerický identifikátor s pevným prefixem
|
59
59
|
|
@@ -70,7 +70,7 @@ def generate_id12(three_char_prefix):
|
|
70
70
|
return correct_pid(out)
|
71
71
|
|
72
72
|
|
73
|
-
def check_pid(pid):
|
73
|
+
def check_pid(pid: str) -> bool:
|
74
74
|
"""
|
75
75
|
Kontroluje správnost PID pomocí kontrolního součtu
|
76
76
|
|
@@ -88,7 +88,7 @@ def check_pid(pid):
|
|
88
88
|
return out
|
89
89
|
|
90
90
|
|
91
|
-
def correct_pid(pid):
|
91
|
+
def correct_pid(pid: str) -> str:
|
92
92
|
"""
|
93
93
|
Opraví PID do korektní podoby. Doplní kontrolní součet.
|
94
94
|
|
@@ -103,7 +103,7 @@ def correct_pid(pid):
|
|
103
103
|
return out
|
104
104
|
|
105
105
|
|
106
|
-
def generate_pid():
|
106
|
+
def generate_pid() -> str:
|
107
107
|
"""
|
108
108
|
Vybeneruje nový PID
|
109
109
|
|
@@ -0,0 +1,262 @@
|
|
1
|
+
from datetime import datetime
|
2
|
+
from enum import Enum
|
3
|
+
from typing import Optional, List, Tuple, Any
|
4
|
+
from uuid import UUID
|
5
|
+
|
6
|
+
from pydantic import BaseModel, Field, EmailStr
|
7
|
+
|
8
|
+
from sysnet_pyutils.utils import local_now
|
9
|
+
|
10
|
+
|
11
|
+
class ApiError(Exception):
|
12
|
+
def __init__(self, code: int = 500, message: str = None):
|
13
|
+
self.code = code
|
14
|
+
self.message = message
|
15
|
+
|
16
|
+
|
17
|
+
class ErrorModel(BaseModel):
|
18
|
+
code: int
|
19
|
+
message: str
|
20
|
+
|
21
|
+
|
22
|
+
class UserType(BaseModel):
|
23
|
+
identifier: Optional[str] = Field(
|
24
|
+
default=None,
|
25
|
+
description='identifikátor uživatele (PID nebo UUID)',
|
26
|
+
examples=['ABC123456789'],
|
27
|
+
)
|
28
|
+
name: Optional[str] = Field(
|
29
|
+
default=None, description='Jméno uživatele', examples=['Jiří Novák']
|
30
|
+
)
|
31
|
+
dn: Optional[str] = Field(
|
32
|
+
default=None,
|
33
|
+
description='Příznačné jméno (distinguished name) LDAP',
|
34
|
+
examples=['cn=Jiří Novák, o=AOPK, c=CZ'],
|
35
|
+
)
|
36
|
+
email: Optional[EmailStr] = Field(default=None, description='Adresa elektronické pošty', examples=['jiri.novak@aopk.cz'])
|
37
|
+
phone: Optional[str] = Field(default=None, description='Telefonní číslo uživatele')
|
38
|
+
name_first: Optional[str] = Field(default=None, description='Křestní jméno uživatele', examples=['Jiří'])
|
39
|
+
name_last: Optional[str] = Field(default=None, description='Příjmení uživatele', examples=['Novák'])
|
40
|
+
name_full: Optional[str] = Field(default=None, description='Úplné jméno uživatele', examples=['MUDr. Jiří Novák, PhD.'])
|
41
|
+
|
42
|
+
|
43
|
+
class AclLevelEnum(Enum):
|
44
|
+
READ = 'R'
|
45
|
+
WRITE = 'W'
|
46
|
+
MANAGE = 'M'
|
47
|
+
|
48
|
+
@classmethod
|
49
|
+
def has_value(cls, value):
|
50
|
+
return value in cls._value2member_map_
|
51
|
+
|
52
|
+
|
53
|
+
class CodeValueType(BaseModel):
|
54
|
+
# kód/hodnota
|
55
|
+
code: Optional[str] = Field(default=None, description='Kód položky', examples=['CZ'])
|
56
|
+
value: Optional[str] = Field(default=None, description='Hodnota položky', examples=['Česká republika / Czech republic'])
|
57
|
+
|
58
|
+
def __eq__(self, other):
|
59
|
+
if not isinstance(other, CodeValueType):
|
60
|
+
return NotImplemented
|
61
|
+
return self.code == other.code and self.value == other.value
|
62
|
+
|
63
|
+
def load_data(self, data: Tuple[Any, Any]):
|
64
|
+
self.code = data[0]
|
65
|
+
self.value = data[1]
|
66
|
+
return self
|
67
|
+
|
68
|
+
class RegionalValueType(CodeValueType):
|
69
|
+
# kód/hodnota/rok/kraj
|
70
|
+
year: Optional[int] = Field(default=None, description='Rok platnotsi položky', examples=[2022])
|
71
|
+
region: Optional[str] = Field(default=None, description='Kraj platnosti položky', examples=['Středočeský kraj'])
|
72
|
+
|
73
|
+
|
74
|
+
class TimeLimitedType(CodeValueType):
|
75
|
+
# kód/hodnota
|
76
|
+
date_from: Optional[datetime] = Field(default=None, description='Platí od data')
|
77
|
+
date_to: Optional[datetime] = Field(default=None, description='Platí do data')
|
78
|
+
|
79
|
+
|
80
|
+
class LogItemType(BaseModel):
|
81
|
+
# položka logu
|
82
|
+
timestamp: datetime = Field(default=local_now(), description='Časová značka logu')
|
83
|
+
originator: Optional[str] = Field(default='SYSTEM', description='Zdroj logu', examples=['<NAME>'])
|
84
|
+
message: Optional[str] = Field(default=None, description='Zpráva logu', examples=['Autorizováno'])
|
85
|
+
|
86
|
+
|
87
|
+
class ContainerHistoryItemType(BaseModel):
|
88
|
+
# položka historie zařazení do kontejnerů
|
89
|
+
timestamp: datetime = Field(default=local_now(), description='Časová značka logu')
|
90
|
+
originator: Optional[str] = Field(default='SYSTEM', description='Zdroj logu', examples=['<NAME>'])
|
91
|
+
container: Optional[UUID] = Field(default=None, description='Identifikátor kontejneru')
|
92
|
+
message: Optional[str] = Field(default=None, description='Zpráva logu', examples=['Autorizováno'])
|
93
|
+
|
94
|
+
|
95
|
+
class AclType(BaseModel):
|
96
|
+
name: Optional[str] = Field(
|
97
|
+
default=None,
|
98
|
+
description='Uživatelské jméno nebo název role',
|
99
|
+
examples=['jan.novak@email.com'],
|
100
|
+
)
|
101
|
+
level: Optional[AclLevelEnum] = Field(
|
102
|
+
default=None, description='Úroveň přístupu (reader, writer, manager)'
|
103
|
+
)
|
104
|
+
can_delete: Optional[bool] = Field(
|
105
|
+
default=False, description='Oprávnění odstraňovat dokumenty'
|
106
|
+
)
|
107
|
+
|
108
|
+
|
109
|
+
class MetadataTypeEntry(BaseModel):
|
110
|
+
title: Optional[str] = Field(default=None, description='Název dokumentu')
|
111
|
+
id_no: Optional[str] = Field(default=None, description='Číslo dokumentu', examples=['23CZ123456'])
|
112
|
+
unid: Optional[str] = Field(default=None, description='Domino universal ID', examples=['3005277CB984B7FFC12587890060E2BF'])
|
113
|
+
pid: Optional[str] = Field(default=None, description='Unique identifier', examples=['MBOA7HNBDJTR'])
|
114
|
+
uuid: Optional[UUID] = Field(default=None, description='Unique identifier')
|
115
|
+
form: Optional[str] = Field(default=None, description='Formulář', examples=['certificate'])
|
116
|
+
authorized: Optional[bool] = Field(default=False, description='Dokument byl autorizován')
|
117
|
+
archived: Optional[bool] = Field(default=False, description='Dokument byl archivován')
|
118
|
+
deleted: Optional[bool] = Field(default=False, description='Dokument byl odstraněn')
|
119
|
+
|
120
|
+
|
121
|
+
class MetadataTypeBase(BaseModel):
|
122
|
+
title: Optional[str] = Field(default=None, description='Název dokumentu')
|
123
|
+
id_no: Optional[str] = Field(default=None, description='Číslo dokumentu', examples=['23CZ123456'])
|
124
|
+
id_no_local: Optional[str] = Field(default=None, description='Lokální číslo dokumentu', examples=['23CZ123456'])
|
125
|
+
id_no_list: Optional[List[str]] = Field(default=None, description='Seznam všech čísel dokumentu', examples=['23CZ123456'])
|
126
|
+
creator: Optional[str] = Field(
|
127
|
+
default=None,
|
128
|
+
description='Tvůrce dokumentu - Subjekt primárně odpovědný za vytvoření dokument.',
|
129
|
+
examples=['CN=Jan Novák/O=CITES/C=CZ'],
|
130
|
+
)
|
131
|
+
created_by_official: Optional[bool] = Field(default=False, description='Vytvořeno úředníkem')
|
132
|
+
contributor: Optional[UserType] = Field(default=None, description='Přispěvatel (podílí se na obsahu dokumentu)')
|
133
|
+
acl: Optional[List[AclType]] = Field(default=None, description='seznam přístupových práv')
|
134
|
+
|
135
|
+
|
136
|
+
class MetadataType(MetadataTypeBase):
|
137
|
+
date_created: Optional[datetime] = Field(
|
138
|
+
default=local_now(),
|
139
|
+
description='Datum a čas vytvoření dokumentu',
|
140
|
+
examples=['2023-04-20T05:12:03Z'],
|
141
|
+
)
|
142
|
+
date_modified: Optional[datetime] = Field(
|
143
|
+
default=local_now(),
|
144
|
+
description='Datum a čas poslední úpravy dokumentu',
|
145
|
+
examples=['2023-04-20T05:12:03Z'],
|
146
|
+
)
|
147
|
+
date_authorized: Optional[datetime] = Field(
|
148
|
+
default=None,
|
149
|
+
description='Datum a čas autorizace',
|
150
|
+
examples=['2023-04-20T05:12:03Z'],
|
151
|
+
)
|
152
|
+
date_archived: Optional[datetime] = Field(
|
153
|
+
default=None,
|
154
|
+
description='Datum a čas archivace',
|
155
|
+
examples=['2023-04-20T05:12:03Z'],
|
156
|
+
)
|
157
|
+
date_deleted: Optional[datetime] = Field(
|
158
|
+
default=None,
|
159
|
+
description='Datum a čas odstranění',
|
160
|
+
examples=['2023-04-20T05:12:03Z'],
|
161
|
+
)
|
162
|
+
unid: Optional[str] = Field(default=None, description='Domino universal ID', examples=['3005277CB984B7FFC12587890060E2BF'])
|
163
|
+
pid: Optional[str] = Field(default=None, description='Unique identifier', examples=['MBOA7HNBDJTR'])
|
164
|
+
uuid: Optional[UUID] = Field(default=None, description='Unique identifier')
|
165
|
+
form: Optional[str] = Field(default=None, description='Formulář', examples=['certificate'])
|
166
|
+
authorized: Optional[bool] = Field(default=False, description='Dokument byl autorizován')
|
167
|
+
archived: Optional[bool] = Field(default=False, description='Dokument byl archivován')
|
168
|
+
deleted: Optional[bool] = Field(default=False, description='Dokument byl odstraněn')
|
169
|
+
has_attachments: Optional[bool] = Field(default=False, description='Dokument má/nemá přílohy')
|
170
|
+
container: Optional[UUID] = Field(default=None, description='Identifikátor kontejneru, do kterého je dokument zařazen.')
|
171
|
+
container_history: Optional[List[ContainerHistoryItemType]] = Field(default=None, description='Historie kontejnerů')
|
172
|
+
|
173
|
+
|
174
|
+
class ListTypeBase(BaseModel):
|
175
|
+
start: Optional[int] = Field(default=None, description='Počáteční dokument na stránce', examples=[0])
|
176
|
+
page_size: Optional[int] = Field(default=None, description='Velikost stránky', examples=[10])
|
177
|
+
page: Optional[int] = Field(default=None, description='Požadovaná stránka', examples=[0])
|
178
|
+
count: Optional[int] = Field(default=None, description='celkový počet vrácených položek', examples=[25])
|
179
|
+
|
180
|
+
|
181
|
+
class LinkedType(BaseModel):
|
182
|
+
# Identifikace pevně provázaného dokumemtu (nadřízený nebo jinak provázaný)
|
183
|
+
title: Optional[str] = Field(default=None, description='Název provázaného dokumentu', examples=['Žádost o vydání permitu'])
|
184
|
+
code: Optional[str] = Field(default=None, description='Kód vazby', examples=['CRŽP', 'CITS', 'IPPC'])
|
185
|
+
id: Optional[str] = Field(default=None, description='Hlavní identifikátor', examples=['0c282c62-1918-4fbe-ad2c-e49a021f4801'])
|
186
|
+
unid: Optional[str] = Field(default=None, description='Domino universal ID', examples=['3005277CB984B7FFC12587890060E2BF'])
|
187
|
+
pid: Optional[str] = Field(default=None, description='PID (dvanáctimístný identifikátor', examples=['MBOA7HNBDJTR'])
|
188
|
+
uuid: Optional[UUID] = Field(default=None, description='Jednoznačný identifikátor', examples=['0c282c62-1918-4fbe-ad2c-e49a021f4801'])
|
189
|
+
|
190
|
+
|
191
|
+
class GeoPointJtskType(BaseModel):
|
192
|
+
x: Optional[float] = Field(default=None, description='JTSK - X', examples=[-1182833.13])
|
193
|
+
y: Optional[float] = Field(default=None, description='JTSK - Y', examples=[-784886.70])
|
194
|
+
|
195
|
+
|
196
|
+
class GeoPointType(BaseModel):
|
197
|
+
lat: Optional[float] = Field(default=None, description='WGS84 - latitude', examples=[-4.3202115])
|
198
|
+
lon: Optional[float] = Field(default=None, description='WGS84 - longitude', examples=[55.7520211])
|
199
|
+
|
200
|
+
|
201
|
+
class MapSheet50Type(BaseModel):
|
202
|
+
# objekt mapový list 1:50000
|
203
|
+
id: Optional[str] = Field(default=None, description='Identifikátor mapového listu', examples=['03-44'])
|
204
|
+
name: Optional[str] = Field(default=None, description='Název mapového listu', examples=['Dvůr Králové'])
|
205
|
+
|
206
|
+
|
207
|
+
class BasinType(BaseModel):
|
208
|
+
# objekt povodí
|
209
|
+
id: Optional[int] = Field(default=None, description='Identifikátor', examples=[4236])
|
210
|
+
id_1: Optional[int] = Field(default=None, description='Identifikátor', examples=[1])
|
211
|
+
id_2: Optional[int] = Field(default=None, description='Identifikátor', examples=[11])
|
212
|
+
id_3: Optional[int] = Field(default=None, description='Identifikátor', examples=[49])
|
213
|
+
chp: Optional[str] = Field(default=None, description='', examples=['1-11-04-0300-0-00'])
|
214
|
+
chp_d: Optional[str] = Field(default=None, description='', examples=['1-11-04-0300-0-00'])
|
215
|
+
chp_u: Optional[str] = Field(default=None, description='', examples=['1-11-04-0300-0-00'])
|
216
|
+
basin_name_1: Optional[str] = Field(default=None, description='naz_pov_1', examples=['povodí Labe'])
|
217
|
+
basin_name_2: Optional[str] = Field(default=None, description='naz_pov_2', examples=['Berounka od Úslavy po ústí'])
|
218
|
+
basin_name_3: Optional[str] = Field(default=None, description='naz_pov_3', examples=['Litavka a Berounka od Litavky po Loděnici'])
|
219
|
+
stream_name: Optional[str] = Field(default=None, description='naz_tok', examples=['Červený potok'])
|
220
|
+
stream_name_2: Optional[str] = Field(default=None, description='naz_tok_2', examples=[''])
|
221
|
+
|
222
|
+
|
223
|
+
class LocationType(BaseModel):
|
224
|
+
# Lokalita
|
225
|
+
ruian_adm: Optional[int] = Field(default=None, description='Kód adresního místa RUIAN', examples=[21844895])
|
226
|
+
ruian_adm_name: Optional[str] = Field(default=None, description='Název adresního místa RUIAN', examples=['Kaplanova 1931/1, 14800 Praha 11'])
|
227
|
+
basin: Optional[BasinType] = Field(default=None, description='Povodí')
|
228
|
+
sheet50: Optional[MapSheet50Type] = Field(default=None, description='Mapový list')
|
229
|
+
street: Optional[str] = Field(default=None, description='Ulice nebo část obce a číslo popisné, evidenční, orientační', examples=['Kolmá 53/1230'])
|
230
|
+
city: Optional[str] = Field(default=None, description='Název obce', examples=['Slepičí Lhota'])
|
231
|
+
zip: Optional[str] = Field(default=None, description='PSČ', examples=['987 22'])
|
232
|
+
region: Optional[str] = Field(default=None, description='Kraj', examples=['Středočeský kraj'])
|
233
|
+
country: Optional[CodeValueType] = Field(default=None, description='Stát', examples=[{'code': 'SK', 'value': 'Slovensko'}])
|
234
|
+
address_list: Optional[list[str]] = Field(default=None, description='Seznam adresních položek', examples=['Kolmá 53/1230', 'Slepičí Lhota', '987 22', 'CZ'])
|
235
|
+
note: Optional[str] = Field(default=None, description='Poznámka', examples=['Parcela číslo 113/7'])
|
236
|
+
wgs: Optional[GeoPointType] = Field(default=None, description='WGS84')
|
237
|
+
jtsk: Optional[GeoPointJtskType] = Field(default=None, description='JTSK')
|
238
|
+
|
239
|
+
|
240
|
+
class PersonBaseType(BaseModel):
|
241
|
+
# Osoba z Registru osob
|
242
|
+
unid: Optional[str] = Field(default=None, description='Domino universal ID', examples=['3005277CB984B7FFC12587890060E2BF'])
|
243
|
+
pid: Optional[str] = Field(default=None, description='Unique identifier', examples=['MBOA7HNBDJTR'])
|
244
|
+
uuid: Optional[UUID] = Field(default=None, description='Unique identifier')
|
245
|
+
ico: Optional[str] = Field(default=None, description='IČO osoby')
|
246
|
+
name: Optional[str] = Field(default=None, description='Název osoby', examples=['B.A.R. Reptofilia'])
|
247
|
+
email: Optional[EmailStr] = Field(default=None, description='Email osoby')
|
248
|
+
address: Optional[str] = Field(default=None, description='Adresa osoby', examples=['Ulice Závodu Míru 2\n87423 Otrokovice'])
|
249
|
+
country: Optional[CodeValueType] = Field(default=None, description='Země adresy osoby')
|
250
|
+
main_person: Optional[LinkedType] = Field(default=None, description='Odkaz na hlavní osobu do registru')
|
251
|
+
linked_persons: Optional[list[LinkedType]] = Field(default=None, description='Odkaz na další osoby do registru')
|
252
|
+
|
253
|
+
|
254
|
+
class WorkflowType(BaseModel):
|
255
|
+
# Položka životního cyklu
|
256
|
+
node_code: Optional[str] = Field(default=None, description='Kód schvalovacího uzlu (APPROVE, AUTHORIZE, REJECT, ...)', examples=['APPROVE', 'AUTHORIZE', 'REJECT'])
|
257
|
+
node_name: Optional[str] = Field(default=None, description='Název schvalovacího uzlu (schválení, autorizace, ...)', examples=['Schválení'])
|
258
|
+
responsible: Optional[PersonBaseType] = Field(default=None, description='Odpovědná osoba')
|
259
|
+
date_execution: Optional[datetime] = Field(default=local_now(), description='Datum provedení')
|
260
|
+
executor: Optional[UserType] = Field(default='SYSTEM', description='Uživatel, který provedl událost')
|
261
|
+
status_from: Optional[str] = Field(default=None, description='Předchozí stav')
|
262
|
+
status_to: Optional[str] = Field(default=None, description='Následný stav')
|
sysnet_pyutils/utils.py
CHANGED
@@ -6,9 +6,10 @@ import re
|
|
6
6
|
import secrets
|
7
7
|
import sys
|
8
8
|
import traceback
|
9
|
-
import uuid
|
10
9
|
from datetime import datetime, timedelta, date
|
10
|
+
from typing import Tuple, Union, List, Any
|
11
11
|
from urllib.parse import quote
|
12
|
+
from uuid import UUID, uuid4, uuid1
|
12
13
|
|
13
14
|
import dateutil.parser
|
14
15
|
import pytz
|
@@ -125,7 +126,7 @@ class Config(object, metaclass=Singleton):
|
|
125
126
|
self.store()
|
126
127
|
|
127
128
|
|
128
|
-
def url_safe(url):
|
129
|
+
def url_safe(url: str) -> str:
|
129
130
|
"""
|
130
131
|
Upraví URL, aby neobsahovalo nepovolené znaky
|
131
132
|
|
@@ -135,7 +136,7 @@ def url_safe(url):
|
|
135
136
|
return quote(url, safe='/:?=&')
|
136
137
|
|
137
138
|
|
138
|
-
def who_am_i():
|
139
|
+
def who_am_i() -> str:
|
139
140
|
"""
|
140
141
|
Vrátí název funkce
|
141
142
|
|
@@ -143,10 +144,10 @@ def who_am_i():
|
|
143
144
|
"""
|
144
145
|
stack = traceback.extract_stack()
|
145
146
|
file_name, code_line, func_name, text = stack[-2]
|
146
|
-
return func_name
|
147
|
+
return str(func_name)
|
147
148
|
|
148
149
|
|
149
|
-
def unique_list(input_list):
|
150
|
+
def unique_list(input_list: list) -> list:
|
150
151
|
"""
|
151
152
|
Vyřadí opakující se položky ze seznamu
|
152
153
|
|
@@ -162,7 +163,7 @@ def unique_list(input_list):
|
|
162
163
|
return out
|
163
164
|
|
164
165
|
|
165
|
-
def api_keys_init(agenda='main', amount=4):
|
166
|
+
def api_keys_init(agenda: str='main', amount: int =4) -> List[dict]:
|
166
167
|
"""
|
167
168
|
Vygeneruje klíče pro API
|
168
169
|
|
@@ -176,7 +177,7 @@ def api_keys_init(agenda='main', amount=4):
|
|
176
177
|
return out
|
177
178
|
|
178
179
|
|
179
|
-
def uuid_next(uuid_type=4):
|
180
|
+
def uuid_next(uuid_type: int = 4) -> UUID:
|
180
181
|
"""
|
181
182
|
Vygeneruje UUID
|
182
183
|
|
@@ -184,13 +185,13 @@ def uuid_next(uuid_type=4):
|
|
184
185
|
:return: uuid
|
185
186
|
"""
|
186
187
|
if uuid_type == 4:
|
187
|
-
out =
|
188
|
+
out = uuid4()
|
188
189
|
else:
|
189
|
-
out =
|
190
|
+
out = uuid1()
|
190
191
|
return out
|
191
192
|
|
192
193
|
|
193
|
-
def pid_next():
|
194
|
+
def pid_next() -> str:
|
194
195
|
"""
|
195
196
|
Vygeneruje korektní PID
|
196
197
|
|
@@ -199,7 +200,7 @@ def pid_next():
|
|
199
200
|
return generate_pid()
|
200
201
|
|
201
202
|
|
202
|
-
def pid_check(pid):
|
203
|
+
def pid_check(pid: str) -> bool:
|
203
204
|
"""
|
204
205
|
Zkontroluje korektnost PID
|
205
206
|
|
@@ -209,7 +210,7 @@ def pid_check(pid):
|
|
209
210
|
return check_pid(pid)
|
210
211
|
|
211
212
|
|
212
|
-
def pid_correct(pid):
|
213
|
+
def pid_correct(pid: str) -> str:
|
213
214
|
"""
|
214
215
|
Opraví PID
|
215
216
|
|
@@ -219,7 +220,7 @@ def pid_correct(pid):
|
|
219
220
|
return correct_pid(pid)
|
220
221
|
|
221
222
|
|
222
|
-
def id12_next(three_char_prefix=None):
|
223
|
+
def id12_next(three_char_prefix: Union[str, None]=None) -> str:
|
223
224
|
"""
|
224
225
|
Vygeneruje korektní 12místný alfanumerický identifikátor s pevným prefixem
|
225
226
|
|
@@ -229,7 +230,7 @@ def id12_next(three_char_prefix=None):
|
|
229
230
|
return generate_id12(three_char_prefix=three_char_prefix)
|
230
231
|
|
231
232
|
|
232
|
-
def api_key_next(name, length=16):
|
233
|
+
def api_key_next(name: Union[str, None], length: int = 16) -> dict:
|
233
234
|
"""
|
234
235
|
Vygeneruje slovník API key {<API Key>: <name>}
|
235
236
|
|
@@ -237,11 +238,13 @@ def api_key_next(name, length=16):
|
|
237
238
|
:param length: Délka API klíče
|
238
239
|
:return: Slovník {<API Key>: <name>}
|
239
240
|
"""
|
241
|
+
if name in [None, '']:
|
242
|
+
name = 'main'
|
240
243
|
out = {api_key_generate(length=length): name}
|
241
244
|
return out
|
242
245
|
|
243
246
|
|
244
|
-
def api_key_generate(length: int):
|
247
|
+
def api_key_generate(length: int = 16) -> Union[str, None]:
|
245
248
|
"""
|
246
249
|
vygeneruje API klíč
|
247
250
|
|
@@ -251,7 +254,7 @@ def api_key_generate(length: int):
|
|
251
254
|
return secrets.token_urlsafe(length)
|
252
255
|
|
253
256
|
|
254
|
-
def hash_md5(text):
|
257
|
+
def hash_md5(text: str) -> Union[str, None]:
|
255
258
|
"""
|
256
259
|
Vytvoří md5 checksum ze zdrojového textu
|
257
260
|
:param text:
|
@@ -259,28 +262,46 @@ def hash_md5(text):
|
|
259
262
|
"""
|
260
263
|
if text is None:
|
261
264
|
return None
|
262
|
-
return hashlib.md5(text.encode("utf-8")).hexdigest()
|
265
|
+
return str(hashlib.md5(text.encode("utf-8")).hexdigest())
|
263
266
|
|
264
267
|
|
265
|
-
def hash_sha1(text):
|
268
|
+
def hash_sha1(text: str) -> Union[str, None]:
|
269
|
+
"""
|
270
|
+
Vytvoří sha1 checksum ze zdrojového textu
|
271
|
+
|
272
|
+
:param text:
|
273
|
+
:return:
|
274
|
+
"""
|
266
275
|
if text is None:
|
267
276
|
return None
|
268
|
-
return hashlib.sha1(text.encode("utf-8")).hexdigest()
|
277
|
+
return str(hashlib.sha1(text.encode("utf-8")).hexdigest())
|
269
278
|
|
270
279
|
|
271
|
-
def hash_sha256(text):
|
280
|
+
def hash_sha256(text: str) -> Union[str, None]:
|
281
|
+
"""
|
282
|
+
Vytvoří sha265 checksum ze zdrojového textu
|
283
|
+
|
284
|
+
:param text:
|
285
|
+
:return:
|
286
|
+
"""
|
272
287
|
if text is None:
|
273
288
|
return None
|
274
289
|
return hashlib.sha256(text.encode("utf-8")).hexdigest()
|
275
290
|
|
276
291
|
|
277
|
-
def hash_sha384(text):
|
292
|
+
def hash_sha384(text: str) -> Union[str, None]:
|
293
|
+
"""
|
294
|
+
Vytvoří sha265 checksum ze zdrojového textu
|
295
|
+
|
296
|
+
:param text:
|
297
|
+
:return:
|
298
|
+
"""
|
278
299
|
if text is None:
|
279
300
|
return None
|
280
301
|
return hashlib.sha384(text.encode("utf-8")).hexdigest()
|
281
302
|
|
282
303
|
|
283
|
-
def is_valid_unid(unid):
|
304
|
+
def is_valid_unid(unid: str) -> bool:
|
284
305
|
"""
|
285
306
|
Kontrola validity HCL Notes UNIID
|
286
307
|
|
@@ -300,7 +321,7 @@ def is_valid_unid(unid):
|
|
300
321
|
return False
|
301
322
|
|
302
323
|
|
303
|
-
def is_valid_uuid(value):
|
324
|
+
def is_valid_uuid(value: Union[UUID, str]) -> bool:
|
304
325
|
"""
|
305
326
|
Kontrola validity uuid
|
306
327
|
|
@@ -308,13 +329,13 @@ def is_valid_uuid(value):
|
|
308
329
|
:return:
|
309
330
|
"""
|
310
331
|
try:
|
311
|
-
|
332
|
+
UUID(str(value))
|
312
333
|
return True
|
313
334
|
except ValueError:
|
314
335
|
return False
|
315
336
|
|
316
337
|
|
317
|
-
def is_valid_pid(value):
|
338
|
+
def is_valid_pid(value: str) -> bool:
|
318
339
|
"""
|
319
340
|
Kontrola validity PID
|
320
341
|
:param value:
|
@@ -324,7 +345,7 @@ def is_valid_pid(value):
|
|
324
345
|
return bool(re.search(pattern, value))
|
325
346
|
|
326
347
|
|
327
|
-
def is_valid_ico(ico):
|
348
|
+
def is_valid_ico(ico: str) -> bool:
|
328
349
|
"""
|
329
350
|
Kontrola validity IČO
|
330
351
|
|
@@ -350,7 +371,20 @@ def is_valid_ico(ico):
|
|
350
371
|
return True
|
351
372
|
|
352
373
|
|
353
|
-
def
|
374
|
+
def is_valid_email(email: str) -> bool:
|
375
|
+
"""
|
376
|
+
Kontrola validity emailové adresy
|
377
|
+
|
378
|
+
:param email:
|
379
|
+
:return:
|
380
|
+
"""
|
381
|
+
if email is None:
|
382
|
+
return False
|
383
|
+
out = bool(re.search(r"^[\w.+\-]+@\w+\.[a-z]{2,3}$", email))
|
384
|
+
return out
|
385
|
+
|
386
|
+
|
387
|
+
def repair_ico(ico: str) -> Union[str, None]:
|
354
388
|
"""
|
355
389
|
Opraví IČO
|
356
390
|
|
@@ -376,7 +410,7 @@ def repair_ico(ico):
|
|
376
410
|
return ico
|
377
411
|
|
378
412
|
|
379
|
-
def iso_to_local_datetime(isodate):
|
413
|
+
def iso_to_local_datetime(isodate: str) -> Union[datetime, None]:
|
380
414
|
"""
|
381
415
|
ISO string datum do lokálního datetime
|
382
416
|
|
@@ -391,18 +425,21 @@ def iso_to_local_datetime(isodate):
|
|
391
425
|
return out
|
392
426
|
|
393
427
|
|
394
|
-
def convert_hex_to_int(id_hex):
|
428
|
+
def convert_hex_to_int(id_hex: str) -> Union[int, None]:
|
395
429
|
"""
|
396
430
|
Konvertuje hex string na int
|
397
431
|
|
398
432
|
:param id_hex: Hexadecimální string
|
399
433
|
:return: int
|
400
434
|
"""
|
401
|
-
|
402
|
-
|
435
|
+
try:
|
436
|
+
id_int = int(id_hex, base=16)
|
437
|
+
return id_int
|
438
|
+
except ValueError:
|
439
|
+
return None
|
403
440
|
|
404
441
|
|
405
|
-
def increment_date(date_str=None, days=1):
|
442
|
+
def increment_date(date_str: str =None, days: int = 1) -> Union[str, None]:
|
406
443
|
"""
|
407
444
|
Inkrementuje datum v textovém formátu ISO o daný počet dní
|
408
445
|
|
@@ -413,13 +450,16 @@ def increment_date(date_str=None, days=1):
|
|
413
450
|
if date_str is None:
|
414
451
|
return None
|
415
452
|
if days is None:
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
453
|
+
days = 1
|
454
|
+
try:
|
455
|
+
d = datetime.strptime(date_str, '%Y-%m-%d')
|
456
|
+
out = d + timedelta(days=days)
|
457
|
+
return out.date().isoformat()
|
458
|
+
except ValueError:
|
459
|
+
return None
|
420
460
|
|
421
461
|
|
422
|
-
def today():
|
462
|
+
def today() -> str:
|
423
463
|
"""
|
424
464
|
Vrací ISO 8601 datum dnešního dne
|
425
465
|
|
@@ -429,7 +469,7 @@ def today():
|
|
429
469
|
return out.date().isoformat()
|
430
470
|
|
431
471
|
|
432
|
-
def tomorrow():
|
472
|
+
def tomorrow() -> str:
|
433
473
|
"""
|
434
474
|
Vrací ISO 8601 datum zítřejšího dne
|
435
475
|
|
@@ -438,7 +478,7 @@ def tomorrow():
|
|
438
478
|
return increment_date(date_str=today(), days=1)
|
439
479
|
|
440
480
|
|
441
|
-
def cs_bool(value=None):
|
481
|
+
def cs_bool(value=None) -> str:
|
442
482
|
"""
|
443
483
|
Vrátí českou textovou hodnotu 'ano'/'ne' pokud je bool(value) True/False
|
444
484
|
|
@@ -451,7 +491,7 @@ def cs_bool(value=None):
|
|
451
491
|
return out
|
452
492
|
|
453
493
|
|
454
|
-
def cron_to_dict(cron):
|
494
|
+
def cron_to_dict(cron: str) -> Union[dict, None]:
|
455
495
|
"""
|
456
496
|
Konvertuje cron text do do slovníku
|
457
497
|
|
@@ -471,10 +511,11 @@ def cron_to_dict(cron):
|
|
471
511
|
return out
|
472
512
|
|
473
513
|
|
474
|
-
def date_to_datetime(date_value):
|
514
|
+
def date_to_datetime(date_value: Union[date, datetime]) -> Union[datetime, None]:
|
475
515
|
"""
|
476
516
|
Konvertuje date na datetime v lokální časové zóně
|
477
|
-
|
517
|
+
|
518
|
+
:param date_value: datum/datum a čas
|
478
519
|
:return: hodnota date v lokální časové zóně
|
479
520
|
"""
|
480
521
|
if date_value is None:
|
@@ -488,12 +529,12 @@ def date_to_datetime(date_value):
|
|
488
529
|
return out
|
489
530
|
|
490
531
|
|
491
|
-
def date_to_datetime_utc(date_value):
|
532
|
+
def date_to_datetime_utc(date_value: Union[date, datetime]) -> Union[datetime, None]:
|
492
533
|
"""
|
493
534
|
Konvertuje datum na tatum a čas v UTC.
|
494
535
|
Vhodné pro MongoDB pro ukládání položek typu datum.
|
495
536
|
|
496
|
-
:param date_value:
|
537
|
+
:param date_value: datum/datum a čas
|
497
538
|
:return: hodnota date v UTC
|
498
539
|
"""
|
499
540
|
if date_value is None:
|
@@ -508,7 +549,7 @@ def date_to_datetime_utc(date_value):
|
|
508
549
|
return None
|
509
550
|
|
510
551
|
|
511
|
-
def date_to_iso(date_value):
|
552
|
+
def date_to_iso(date_value: Union[date, datetime]) -> Union[str, None]:
|
512
553
|
"""
|
513
554
|
Konvertuje hodnotu typu date nebo datetime na ISO string
|
514
555
|
|
@@ -522,7 +563,7 @@ def date_to_iso(date_value):
|
|
522
563
|
return None
|
523
564
|
|
524
565
|
|
525
|
-
def remove_empty(source_list):
|
566
|
+
def remove_empty(source_list: List[Any]):
|
526
567
|
"""
|
527
568
|
Odstraní prázdné položky ze seznamu
|
528
569
|
|
@@ -533,13 +574,12 @@ def remove_empty(source_list):
|
|
533
574
|
return None
|
534
575
|
target_list = []
|
535
576
|
for item in source_list:
|
536
|
-
if item
|
537
|
-
|
538
|
-
target_list.append(item)
|
577
|
+
if bool(item):
|
578
|
+
target_list.append(item)
|
539
579
|
return target_list
|
540
580
|
|
541
581
|
|
542
|
-
def is_base64(body):
|
582
|
+
def is_base64(body: Union[str, bytes]) -> bool:
|
543
583
|
"""
|
544
584
|
Kontrola, zda jsou data kódována base64
|
545
585
|
|
@@ -548,7 +588,7 @@ def is_base64(body):
|
|
548
588
|
"""
|
549
589
|
try:
|
550
590
|
if isinstance(body, str):
|
551
|
-
# If there's any
|
591
|
+
# If there's any Unicode here, an exception will be thrown and the function will return false
|
552
592
|
sb_bytes = bytes(body, 'ascii')
|
553
593
|
elif isinstance(body, bytes):
|
554
594
|
sb_bytes = body
|
@@ -560,7 +600,7 @@ def is_base64(body):
|
|
560
600
|
return False
|
561
601
|
|
562
602
|
|
563
|
-
def to_base64(body):
|
603
|
+
def to_base64(body: Union[str, bytes]) -> Union[bytes, None]:
|
564
604
|
"""
|
565
605
|
Zajistí, aby data byla v base64
|
566
606
|
|
@@ -571,10 +611,10 @@ def to_base64(body):
|
|
571
611
|
return body
|
572
612
|
try:
|
573
613
|
if isinstance(body, str):
|
574
|
-
# If there's any
|
575
|
-
sb_bytes = bytes(body, 'ascii')
|
614
|
+
# If there's any Unicode here, an exception will be thrown and the function will return false
|
615
|
+
sb_bytes: bytes = bytes(body, 'ascii')
|
576
616
|
elif isinstance(body, bytes):
|
577
|
-
sb_bytes = body
|
617
|
+
sb_bytes: bytes = body
|
578
618
|
else:
|
579
619
|
raise ValueError("Argument must be string or bytes")
|
580
620
|
return base64.b64encode(sb_bytes)
|
@@ -583,7 +623,7 @@ def to_base64(body):
|
|
583
623
|
return None
|
584
624
|
|
585
625
|
|
586
|
-
def encode_string_b64(data: str, encoding='utf-8'):
|
626
|
+
def encode_string_b64(data: str, encoding='utf-8') -> Union[str, None]:
|
587
627
|
"""
|
588
628
|
Zakóduje string do base64
|
589
629
|
|
@@ -597,7 +637,7 @@ def encode_string_b64(data: str, encoding='utf-8'):
|
|
597
637
|
return out
|
598
638
|
|
599
639
|
|
600
|
-
def decode_b64_string(b64_data: str, encoding='utf-8'):
|
640
|
+
def decode_b64_string(b64_data: str, encoding='utf-8') -> Union[str, None]:
|
601
641
|
"""
|
602
642
|
Dekóduje base64 data na string
|
603
643
|
|
@@ -625,7 +665,7 @@ def encode_file_b64(filepath, encoding='utf-8'):
|
|
625
665
|
return out
|
626
666
|
|
627
667
|
|
628
|
-
def decode_b64_to_file(b64_data: str, filepath, encoding='utf-8'):
|
668
|
+
def decode_b64_to_file(b64_data: str, filepath: str, encoding='utf-8') -> Union[str, None]:
|
629
669
|
"""
|
630
670
|
Uloží base64 data do souboru
|
631
671
|
|
@@ -682,13 +722,13 @@ class Log(object, metaclass=Singleton):
|
|
682
722
|
self.logger = ext_logger
|
683
723
|
|
684
724
|
|
685
|
-
def to_camel(s):
|
725
|
+
def to_camel(s: str) -> str:
|
686
726
|
temp = re.split('_+', s)
|
687
727
|
out = temp[0] + ''.join(map(lambda x: x.title(), temp[1:]))
|
688
728
|
return out
|
689
729
|
|
690
730
|
|
691
|
-
def to_snake(s):
|
731
|
+
def to_snake(s: str) -> str:
|
692
732
|
# return re.sub('([A-Z]\w+$)', '_\\1', s).lower()
|
693
733
|
return re.sub(r'([A-Z]\w+$)', '_\\1', s).lower()
|
694
734
|
|
@@ -705,7 +745,7 @@ def to_camel_dict(d):
|
|
705
745
|
return {to_camel(a): to_camel_dict(b) if isinstance(b, (dict, list)) else b for a, b in d.items()}
|
706
746
|
|
707
747
|
|
708
|
-
def xml_to_dict(xml_text):
|
748
|
+
def xml_to_dict(xml_text: str) -> Union[dict, None]:
|
709
749
|
"""
|
710
750
|
Parsuje XML string do XML dictionary
|
711
751
|
:param xml_text: XML text
|
@@ -714,7 +754,7 @@ def xml_to_dict(xml_text):
|
|
714
754
|
return xmltodict.parse(xml_text)
|
715
755
|
|
716
756
|
|
717
|
-
def dict_to_xml(xml_dict):
|
757
|
+
def dict_to_xml(xml_dict: dict) -> Union[str, None]:
|
718
758
|
"""
|
719
759
|
Parsuje XML dict do XML textu
|
720
760
|
|
@@ -730,11 +770,11 @@ ORDER_BASE = 26
|
|
730
770
|
CHAR_BASE = 65
|
731
771
|
|
732
772
|
|
733
|
-
def order_to_cites(order: int):
|
773
|
+
def order_to_cites(order: int) -> str:
|
734
774
|
"""
|
735
775
|
Konvertuje celočíselnou hodnotu na písmennou
|
736
776
|
|
737
|
-
:param order:
|
777
|
+
:param order: Celočíselná hodnota např. 1458
|
738
778
|
:return: znaková hodnota např. 'BDB'
|
739
779
|
"""
|
740
780
|
if order is None:
|
@@ -760,11 +800,11 @@ def order_to_cites(order: int):
|
|
760
800
|
return out
|
761
801
|
|
762
802
|
|
763
|
-
def cites_to_order(cites: str):
|
803
|
+
def cites_to_order(cites: str) -> int:
|
764
804
|
"""
|
765
805
|
Konvertuje písmennou hodnotu na celočíselnou
|
766
806
|
|
767
|
-
:param cites:
|
807
|
+
:param cites: Znaková hodnota např. 'BDB'
|
768
808
|
:return: celočíselná hodnota např. 1458
|
769
809
|
"""
|
770
810
|
if cites is None:
|
@@ -784,7 +824,7 @@ def cites_to_order(cites: str):
|
|
784
824
|
return out
|
785
825
|
|
786
826
|
|
787
|
-
def local_now():
|
827
|
+
def local_now() -> datetime:
|
788
828
|
"""
|
789
829
|
Vrací aktuální časovou značku v lokální časové zóně.
|
790
830
|
|
@@ -794,7 +834,7 @@ def local_now():
|
|
794
834
|
return datetime.now(tz=pytz.timezone(TZ))
|
795
835
|
|
796
836
|
|
797
|
-
def gmt_now():
|
837
|
+
def gmt_now() -> datetime:
|
798
838
|
"""
|
799
839
|
Vrací aktuální časovou značku ve světovém čase.
|
800
840
|
|
@@ -804,7 +844,7 @@ def gmt_now():
|
|
804
844
|
return datetime.now(tz=pytz.utc)
|
805
845
|
|
806
846
|
|
807
|
-
def datetime_now(tz='Europe/Prague'):
|
847
|
+
def datetime_now(tz='Europe/Prague') -> datetime:
|
808
848
|
"""
|
809
849
|
Vrací aktuální časovou značku ve vybrané časové zóně.
|
810
850
|
|
@@ -813,15 +853,19 @@ def datetime_now(tz='Europe/Prague'):
|
|
813
853
|
return datetime.now(tz=pytz.timezone(tz))
|
814
854
|
|
815
855
|
|
816
|
-
def timestamp():
|
856
|
+
def timestamp(local=False) -> str:
|
817
857
|
"""
|
818
858
|
Vrací textovou podobu časové značky. Vhodné pro názvy souborů.
|
859
|
+
|
860
|
+
:param local: Lokální časová zóna
|
819
861
|
"""
|
820
862
|
out = gmt_now()
|
863
|
+
if local:
|
864
|
+
out = local_now()
|
821
865
|
return out.strftime('%Y%m%d%H%M%S')
|
822
866
|
|
823
867
|
|
824
|
-
def local_datetime(value=None):
|
868
|
+
def local_datetime(value=None) -> datetime:
|
825
869
|
"""
|
826
870
|
Vrací datum a čas v lokální
|
827
871
|
|
@@ -833,3 +877,33 @@ def local_datetime(value=None):
|
|
833
877
|
return value
|
834
878
|
local_tz = pytz.timezone(TZ)
|
835
879
|
return value.astimezone(local_tz)
|
880
|
+
|
881
|
+
|
882
|
+
def parse_ldap_name(ldap_name: str) -> Tuple[Union[str, None], Union[str, None]]:
|
883
|
+
"""
|
884
|
+
Parsuje LDAP jméno do CN.
|
885
|
+
|
886
|
+
:param ldap_name:
|
887
|
+
:return: Tuple CN a původní jméno.
|
888
|
+
"""
|
889
|
+
if ldap_name in [None, '']:
|
890
|
+
return None, None
|
891
|
+
if 'cn=' not in ldap_name.lower():
|
892
|
+
return ldap_name, None
|
893
|
+
if ',' in ldap_name:
|
894
|
+
ldap = ldap_name.split(',')
|
895
|
+
elif ', ' in ldap_name:
|
896
|
+
ldap = ldap_name.split(', ')
|
897
|
+
elif '/' in ldap_name:
|
898
|
+
ldap = ldap_name.split('/')
|
899
|
+
else:
|
900
|
+
ldap = [ldap_name]
|
901
|
+
cn = None
|
902
|
+
for item in ldap:
|
903
|
+
if 'cn=' in item:
|
904
|
+
cn = item.split('=')[-1]
|
905
|
+
break
|
906
|
+
if 'CN=' in item:
|
907
|
+
cn = item.split('=')[-1]
|
908
|
+
break
|
909
|
+
return cn, ldap_name
|
@@ -1,6 +1,6 @@
|
|
1
|
-
Metadata-Version: 2.
|
1
|
+
Metadata-Version: 2.2
|
2
2
|
Name: sysnet-pyutils
|
3
|
-
Version: 1.0
|
3
|
+
Version: 1.1.0
|
4
4
|
Summary: Python Utilities
|
5
5
|
Author-email: Data Developer <info@sysnet.cz>
|
6
6
|
Project-URL: Homepage, https://github.com/SYSNET-CZ/pyutils
|
@@ -14,13 +14,14 @@ Classifier: Topic :: Software Development :: Libraries
|
|
14
14
|
Requires-Python: >=3.9
|
15
15
|
Description-Content-Type: text/markdown
|
16
16
|
License-File: LICENSE
|
17
|
-
Requires-Dist: PyYAML
|
18
|
-
Requires-Dist: pytz
|
19
|
-
Requires-Dist: python-dateutil
|
20
|
-
Requires-Dist: xmltodict
|
21
|
-
Requires-Dist: boto3
|
22
|
-
Requires-Dist: botocore
|
23
|
-
Requires-Dist: pymongo
|
17
|
+
Requires-Dist: PyYAML>=6.0.1
|
18
|
+
Requires-Dist: pytz>=2023.3
|
19
|
+
Requires-Dist: python-dateutil>=2.8.2
|
20
|
+
Requires-Dist: xmltodict>=0.13.0
|
21
|
+
Requires-Dist: boto3>=1.34.34
|
22
|
+
Requires-Dist: botocore>=1.34.34
|
23
|
+
Requires-Dist: pymongo>=4.7.2
|
24
|
+
Requires-Dist: pydantic>=2.10.6
|
24
25
|
|
25
26
|
# sysnet-pyutils
|
26
27
|
|
@@ -311,6 +312,26 @@ Vhodné pro MongoDB pro ukládání položek typu datum.
|
|
311
312
|
* #### S konfiguračním souborem YAML se nyní pracuje v _UTF-8_
|
312
313
|
|
313
314
|
* #### Nová třída _LoggedObject_
|
315
|
+
--------------------------------------------------------------------------------------------------------------------------------
|
316
|
+
|
317
|
+
### verze 1.0.8
|
318
|
+
|
319
|
+
* #### Nové utility pro práci s daty JSON _data_utils_
|
320
|
+
--------------------------------------------------------------------------------------------------------------------------------
|
321
|
+
|
322
|
+
#### is_valid_email(email: str) -> bool:
|
323
|
+
Kontrola validity emailové adresy
|
324
|
+
* param email: hodnota
|
325
|
+
* return: True/False
|
326
|
+
--------------------------------------------------------------------------------------------------------------------------------
|
327
|
+
|
328
|
+
### verze 1.1.0
|
329
|
+
|
330
|
+
* #### Přidán obecný datový slovník SYSNET do balíku models.general
|
331
|
+
|
332
|
+
Datový slovník je vytvořen pomocí pydantic a je plně kompatibilní s FastAPI.
|
333
|
+
Obsahuje základní datové typy, které se používají ve všech aplikacích SYSNET.
|
334
|
+
|
314
335
|
|
315
336
|
|
316
337
|
## Systémové proměnné
|
@@ -0,0 +1,17 @@
|
|
1
|
+
sysnet_pyutils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
2
|
+
sysnet_pyutils/barcode.py,sha256=5dM1ISvVVP966JQxGiZBUYJoPx_jgzWEpEQ2uIOxTd4,2622
|
3
|
+
sysnet_pyutils/data_utils.py,sha256=isIlaEKIGK2_7my06oTAcvxCwTMCXy0g83HPbAl-V_Y,9660
|
4
|
+
sysnet_pyutils/domino.py,sha256=dGmg9d1PMPOyA_ysXBHVu_zabKGCJEuno3Yw3DexExQ,2671
|
5
|
+
sysnet_pyutils/geo.py,sha256=smAkRfobvKqjr3_UIjU5huDbnkb3_edD_xmv4R0r5Q0,2938
|
6
|
+
sysnet_pyutils/ident.py,sha256=GyNPF_LNqssMtMzuegpL1GK-0B-ysDiH18kYAdiE2hg,2807
|
7
|
+
sysnet_pyutils/log.py,sha256=SFuCPvFTg31LFvnyG24hCTe73FonfS0_FaAHRBhEvlw,2165
|
8
|
+
sysnet_pyutils/ses.py,sha256=WJzs0y2QnF4XGDGjls_W0p0v6DJ4fZ4_97m3XbzpaFw,2713
|
9
|
+
sysnet_pyutils/tools.py,sha256=ZioHvUIufrhuJyZLdPnuMUD8Li5LTljSMSCxqRnKIi4,377
|
10
|
+
sysnet_pyutils/utils.py,sha256=Y95MyEuZlSlTVsQ2rOArdkTN7xqcGsCA7RA6VoBHSSk,23257
|
11
|
+
sysnet_pyutils/models/__init__.py,sha256=oozOr_DKhenkM9BDaPOmtbLXhP5vtMUCjBPEjZDW4GQ,167
|
12
|
+
sysnet_pyutils/models/general.py,sha256=jqvPoKa6jKXfPLubNSSJZpSpIUG5Sw8A_zwDRlEp8VQ,14706
|
13
|
+
sysnet_pyutils-1.1.0.dist-info/LICENSE,sha256=bx5iLIKjgAdYQ7sISn7DsfHRKkoCUm1154sJJKhgqnU,35184
|
14
|
+
sysnet_pyutils-1.1.0.dist-info/METADATA,sha256=0JaJMhUht0BhXw9nadGcc8-GNwTT7wO3aXzLFhLDqFg,14744
|
15
|
+
sysnet_pyutils-1.1.0.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
16
|
+
sysnet_pyutils-1.1.0.dist-info/top_level.txt,sha256=ZKTltQWbLlWBXw4oovo1w7ui-JQ1WoyECqMSWdBj6XE,15
|
17
|
+
sysnet_pyutils-1.1.0.dist-info/RECORD,,
|
@@ -1,14 +0,0 @@
|
|
1
|
-
sysnet_pyutils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
2
|
-
sysnet_pyutils/barcode.py,sha256=5dM1ISvVVP966JQxGiZBUYJoPx_jgzWEpEQ2uIOxTd4,2622
|
3
|
-
sysnet_pyutils/domino.py,sha256=dGmg9d1PMPOyA_ysXBHVu_zabKGCJEuno3Yw3DexExQ,2671
|
4
|
-
sysnet_pyutils/geo.py,sha256=smAkRfobvKqjr3_UIjU5huDbnkb3_edD_xmv4R0r5Q0,2938
|
5
|
-
sysnet_pyutils/ident.py,sha256=HDEN1WZy0jE4nP3TugOsexeoNSO75o4Cc6TjUpLml0I,2763
|
6
|
-
sysnet_pyutils/log.py,sha256=SFuCPvFTg31LFvnyG24hCTe73FonfS0_FaAHRBhEvlw,2165
|
7
|
-
sysnet_pyutils/ses.py,sha256=WJzs0y2QnF4XGDGjls_W0p0v6DJ4fZ4_97m3XbzpaFw,2713
|
8
|
-
sysnet_pyutils/tools.py,sha256=ZioHvUIufrhuJyZLdPnuMUD8Li5LTljSMSCxqRnKIi4,377
|
9
|
-
sysnet_pyutils/utils.py,sha256=DsCvc3nvv2M-tbO1KgWPNh5E_kzch8H2dxo1HSY42G0,20668
|
10
|
-
sysnet_pyutils-1.0.7.dist-info/LICENSE,sha256=bx5iLIKjgAdYQ7sISn7DsfHRKkoCUm1154sJJKhgqnU,35184
|
11
|
-
sysnet_pyutils-1.0.7.dist-info/METADATA,sha256=13mQkJNZQbktrSyPQfcLIdnKBUsoAFsA7yw3BGB2p-I,13847
|
12
|
-
sysnet_pyutils-1.0.7.dist-info/WHEEL,sha256=OVMc5UfuAQiSplgO0_WdW7vXVGAt9Hdd6qtN4HotdyA,91
|
13
|
-
sysnet_pyutils-1.0.7.dist-info/top_level.txt,sha256=ZKTltQWbLlWBXw4oovo1w7ui-JQ1WoyECqMSWdBj6XE,15
|
14
|
-
sysnet_pyutils-1.0.7.dist-info/RECORD,,
|
File without changes
|
File without changes
|