sista-tacrpy 1.0.12__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.
@@ -0,0 +1,378 @@
1
+ """Soubor dílčích a agregujících funkcí sloužících k vytváření nových pojmů v DataHubu skrze API post."""
2
+
3
+ import uuid
4
+ from unidecode import unidecode
5
+ import numpy as np
6
+ import datetime
7
+ import pandas as pd
8
+ from tacrpy.datahub.openapi import post_data
9
+ from tacrpy.datahub.import_checks import (is_column_filled, has_specific_values, contains_boolean_values,
10
+ no_duplicates_in_column, is_column_list_of_dicts)
11
+
12
+
13
+ def _preprocess_regs(df: pd.DataFrame) -> pd.DataFrame:
14
+ """
15
+ Specifická pomocná dílčí funkce k preprocessingu tabulky se souhrnem vnitřních předpisů.
16
+
17
+ :param df: pd.DataFrame se souhrnem vnitřních předpisů
18
+ :return: pd.DataFrame k využití v dalších krocích modulu
19
+ """
20
+
21
+ df.columns = df.iloc[0]
22
+ df = df.iloc[1:]
23
+ df['Verze'] = df['Verze'].astype(str)
24
+ df1 = df.dropna(subset=['Odkaz'])
25
+ df1['verze_s'] = ('v' + df1['Verze'])
26
+ df1['předpis_full'] = df1['ID předpisu'] + ' ' + df1['Název vnitřního předpisu'] + ' ' + df1['verze_s']
27
+ links = df1[['ID předpisu', 'předpis_full', 'Odkaz']]
28
+ return links
29
+
30
+
31
+ def records_into_rows(df: pd.DataFrame, key_column: object, parse_column: object, separator: any) -> pd.DataFrame:
32
+ """
33
+ Dílčí funkce, která rozparsuje buňky v cílovém sloupci s více záznamy do jednotlivých řádků (na základě separátoru),
34
+ přičemž je zachována příslušnost k hlavnímu klíči.
35
+
36
+ :param df: název pd.DataFrame, v rámci kterého chci danou funkci použít
37
+ :param key_column: název sloupce s klíčem/ID, ke kterému chci rozparsované záznamy vztáhnout
38
+ :param parse_column: název sloupce, v rámci kterého chci záznamy rozparsovat do řádků
39
+ :param separator: volba separátoru, kterým jsou jednotlivé záznamy v buňce odděleny
40
+ :return: pd.DataFrame obsahující sloupec s rozparsovanými záznamy a klíč, ke kterému se dané záznamy vztahují
41
+ """
42
+
43
+ df[parse_column] = df[parse_column].astype(str)
44
+ new_rows = []
45
+ for index, row in df.iterrows():
46
+ records = row[parse_column].split(separator)
47
+ for record in records:
48
+ new_rows.append({key_column: row[key_column], parse_column: record})
49
+
50
+ split_df = pd.DataFrame(new_rows)
51
+ split_df[parse_column] = split_df[parse_column].str.lstrip()
52
+ split_df = split_df[split_df[parse_column] != '']
53
+ return split_df
54
+
55
+
56
+ def _add_owners_urn(df: pd.DataFrame, dh_users: pd. DataFrame, name_column: object,
57
+ separator: any, owner_type: str) -> pd.DataFrame:
58
+ """
59
+ Pomocná dílčí funkce zpracující formát, ve kterém jsou vlastníci pojmů vedeni ve zdrojovém souboru,
60
+ a vytvoří klíč na základě kterého jsou vlastníkům následně přiřazeny jejich urns v souladu s účty v datahubu.
61
+
62
+ :param df: pd.DataFrame obsahující sloupec s rozparsovanými vlastníky pojmů
63
+ :param dh_users: pd.DataFrame obsahující seznam urn existujících uživatelů v datahubu
64
+ :param name_column: název sloupce obsahující jméno a příjmení vlastníka pojmu ve formátu "příjmení, jméno"
65
+ :param separator: nastavení separátoru, kterým jsou části jména oddělené (v případě zdroj. souboru se jedná o ","
66
+ :param owner_type: nastavení druhu vlastnictví (defaultně se pro pojmy používá "BUSINESS OWNER")
67
+ :return: pd.DataFrame, který primárně obsahuje sloupce z df doplněné o urn jednotlivých vlastníků
68
+ """
69
+
70
+ parsed_df = records_into_rows(df, 'Název pojmu', 'Garanti pojmů', ';')
71
+ owner_preprocess = parsed_df[name_column].str.split(separator, expand=True)
72
+ for col in owner_preprocess:
73
+ owner_preprocess[col + 10] = (owner_preprocess[col].apply(lambda text: unidecode(text).lower()
74
+ if text else '').str.lstrip())
75
+
76
+ owner_preprocess_v2 = pd.merge(parsed_df, owner_preprocess[[10, 11]],
77
+ left_index=True,
78
+ right_index=True)
79
+ owner_preprocess_v3 = owner_preprocess_v2[10].str.split(' ', expand=True)
80
+ owner_preprocess_v4 = pd.merge(owner_preprocess_v2, owner_preprocess_v3,
81
+ left_index=True,
82
+ right_index=True)
83
+
84
+ desired_column = 1
85
+ if desired_column in owner_preprocess_v4.columns:
86
+ owner_preprocess_v4['base_urn_long'] = (owner_preprocess_v4.apply
87
+ (lambda row: row[11] + '.' + row[0] + ('.' + row[1] if not pd.isna(row[1]) else ''), axis=1))
88
+ else:
89
+ owner_preprocess_v4['base_urn_long'] = (owner_preprocess_v4.apply(lambda row: row[11] + '.' + row[0], axis=1))
90
+
91
+ owner_preprocess_v4['urn_long'] = (owner_preprocess_v4.apply
92
+ (lambda row: 'urn:li:corpuser:' + row['base_urn_long'], axis=1))
93
+ owner_preprocess_v4['base_urn_short'] = owner_preprocess_v4.apply(lambda row: row[0], axis=1)
94
+ owner_preprocess_v4['urn_short'] = (owner_preprocess_v4.apply
95
+ (lambda row: 'urn:li:corpuser:' + row['base_urn_short'], axis=1))
96
+
97
+ users_datahub = dh_users[["urn"]]
98
+ users_datahub['dh'] = True
99
+ intersect = pd.merge(owner_preprocess_v4, users_datahub,
100
+ left_on='urn_long',
101
+ right_on='urn',
102
+ how='left')
103
+ intersect['urn'] = intersect['urn'].fillna(intersect['urn_short'])
104
+ final_owners = intersect[['Název pojmu', 'urn']].rename(columns={'urn': 'owner'})
105
+ final_owners['type'] = owner_type
106
+ return final_owners
107
+
108
+
109
+ def row_transform(df: pd.DataFrame, new_column: object, key_column: object,
110
+ base_column_1: object, base_column_2: object) -> pd.DataFrame:
111
+ """
112
+ Dílčí funkce agregující řádky vstupního df na základě klíče a ze záznámů z vybraných sloupců vytvoří list slovníků.
113
+
114
+ :param df: název pd.DataFrame, v rámci kterého chci danou funkci použít
115
+ :param new_column: název nového sloupce, jehož obsahem bude agregace záznamů do struktury listu slovníků
116
+ :param key_column: název sloupce, který představuje klíč pro agregaci řádků
117
+ :param base_column_1: název prvního sloupce, jehož hodnoty jsou agregovány do struktury listu slovníků
118
+ :param base_column_2: název druhého sloupce, jehož hodnoty jsou agregovány do struktury listu slovníků
119
+ :return: pd.DataFrame obsahující dva sloupce - klíč a hodnoty ve struktuře listu slovníků
120
+ """
121
+
122
+ df[new_column] = df.apply(lambda row: {base_column_1: row[base_column_1], base_column_2: row[base_column_2]}
123
+ if not (pd.isna(row[base_column_1]) or pd.isna(row[base_column_2])) else None, axis=1)
124
+ df = df[df[new_column].notna()]
125
+ dictionary = df.groupby(key_column)[new_column].apply(list).to_dict()
126
+ df_list_of_dicts = pd.DataFrame(dictionary.items(), columns=[key_column, new_column])
127
+ return df_list_of_dicts
128
+
129
+
130
+ def _create_lovs(row):
131
+ """
132
+ Pomocná dílčí funkce odkazující se na hodnotu stejného řádku v jiném sloupci.
133
+ Slouží jako interní funkce pro vstup do funkce _expand_data.
134
+
135
+ :param row: vstupní hodnota, na základě které funkce vrací výsledek
136
+ :return: podle podnímky bool hodnota (False) nebo np.nan
137
+ """
138
+
139
+ if row:
140
+ return False
141
+ else:
142
+ return np.nan
143
+
144
+
145
+ def _expand_data(df: pd.DataFrame, base_column: object, new_column: object,
146
+ unidecode_column: object = None, parentnode: str = None) -> pd.DataFrame:
147
+ """
148
+ Pomocná dílčí funkce připravující strukturu dat do podoby, která je nutná pro import dat skrze API do datahubu
149
+ (předtím než se dataframe překlopí do JSON struktury).
150
+
151
+ :param df: pd.DataFrame, který obsahuje již všechna klíčová data potřebná pro import skrze API do datahubu,
152
+ která jsou ovšem ještě potřeba doupravit a doplnit o určité položky
153
+ :param base_column: název sloupce s bool typem, na základě kterého bude vytvořen sloupec nový (kompletní)
154
+ :param new_column: název nového sloupce s bool hodnotami, na základě kterého budou skrze interní funkci
155
+ vytvořeny další nové sloupce dat nutné pro vstup do importu
156
+ :param unidecode_column (optional): název sloupce, který je potřeba upravti skrze unidecode script
157
+ :param parentnode (optional): název sloupce pro případné upřesnění parentnodu
158
+ :return: pd.DataFrame s kompletními daty pro import do datahubu skrze API
159
+ """
160
+
161
+ df[new_column] = df[base_column]
162
+ for i in range(1, 4): # tvorba tří nových lov sloupců
163
+ new_column_name_a = f'lov_column_{i}'
164
+ df[new_column_name_a] = df[new_column].apply(_create_lovs)
165
+ df.fillna('', inplace=True)
166
+ if unidecode_column:
167
+ df[unidecode_column] = df[unidecode_column].apply(lambda text: unidecode(text).upper() if text else '')
168
+ if parentnode:
169
+ df['parentNode'] = parentnode
170
+ return df
171
+
172
+
173
+ def _df_to_dict(df: pd.DataFrame, expanded_column: str, urn_actor: str) -> dict:
174
+ """
175
+ Pomocná dílčí funkce konvertující vstupní data ve formátu pd.DataFrame do JSON struktury (dict)
176
+ a rozšiřuje položku u daného klíče o další vnořený dict.
177
+
178
+ :param df: pd.DataFrame, který obsahuje kompletní data potřebná pro import
179
+ :param expanded_column: název klíče, u kterého je nutné rozšířit strukturu o další vnořený dict
180
+ :param urn_actor: urn uživatele, který data nahrává (ve struktuře 'urn:li:corpuser:jmeno.prijmeni'
181
+ nebo 'urn:li:corpuser:prijmeni' - dle struktury gmailu)
182
+ :return: data v JSON struktuře připravená pro import skrze API post do datahubu
183
+ """
184
+
185
+ data_dict = df.to_dict('index')
186
+ new_keys = ['createStamp']
187
+ counter = 0
188
+ for key, inner_dict in data_dict.items():
189
+ list_of_dict = inner_dict[expanded_column]
190
+ for d in list_of_dict:
191
+ dt = datetime.datetime.now()
192
+ ts = int(datetime.datetime.timestamp(dt) * 1000) + counter
193
+ counter += 1
194
+ d.update({new_key: {"time": ts, "actor": urn_actor} for new_key in new_keys})
195
+ inner_dict[expanded_column] = list_of_dict
196
+ return data_dict
197
+
198
+
199
+ def preprocess_data(df_entities: pd.DataFrame, df_regs: pd.DataFrame, df_owners: pd.DataFrame,
200
+ parentnode: str = None) -> pd.DataFrame:
201
+ """
202
+ Finální agregující funkce (1/2), která vstupní data (z dataframů) transformuje
203
+ a doplní do potřebné struktury tak, aby mohla posloužit jako vstup do importovací funkce.
204
+
205
+ :param df_entities: pd.DataFrame obsahující vstupní data s informacemi o nahrávaných položkách
206
+ (musí obsahovat následující povinné sloupce:'Název pojmu', 'Definice pojmu', 'Kód výskyt pojmu', 'Garanti pojmů',
207
+ 'Zkratka pojmu', 'Název pojmu (EN)', 'Zkratka (EN)', 'Kategorie', 'Datová entita')
208
+ :param df_regs: pd.DataFrame obsahující přehled přespisů (dostupný na intranetu)
209
+ :param df_owners: pd.DataFrame obsahující seznam existujících uživatelských účtú v datahubu
210
+ :param parentnode: v případě hromadného zařazení všech nahrávaných položek do jedné nadřazené složky
211
+ :return: pd.DataFrame obsahující všechny položky nutné k importu do datahubu
212
+ """
213
+
214
+ df_regs_ = _preprocess_regs(df_regs)
215
+ df_parse_regs = records_into_rows(df_entities, 'Název pojmu', 'Kód výskyt pojmu', ';')
216
+ df_parse_owners = _add_owners_urn(df_entities, df_owners, 'Garanti pojmů', ',', 'BUSINESS_OWNER')
217
+ df_parse_regs_url = (pd.merge(df_parse_regs, df_regs_,
218
+ left_on='Kód výskyt pojmu',
219
+ right_on='ID předpisu',
220
+ how='left').rename(columns={'předpis_full': 'description', 'Odkaz': 'url'}))
221
+ df_regs_url = row_transform(df_parse_regs_url, 'elements', 'Název pojmu', 'url', 'description')
222
+ df_ownership = row_transform(df_parse_owners, 'owners', 'Název pojmu', 'owner', 'type')
223
+ df_merged2 = pd.merge(df_entities, df_regs_url,
224
+ left_on='Název pojmu',
225
+ right_on='Název pojmu',
226
+ how='left')
227
+ df_merged = (pd.merge(df_merged2, df_ownership,
228
+ left_on='Název pojmu',
229
+ right_on='Název pojmu',
230
+ how='left').rename(columns={'Název pojmu': 'name', 'Definice pojmu': 'definition',
231
+ 'Zkratka pojmu': 'abbrev', 'Název pojmu (EN)': 'nameEn',
232
+ 'Zkratka (EN)': 'abbrevEn', 'Kategorie': 'termCategory'}))
233
+ df_prepared = _expand_data(df_merged, 'Datová entita', 'isEntity', 'termCategory', parentnode)
234
+ return df_prepared
235
+
236
+
237
+ def import_data(df: pd.DataFrame, urn_actor: str, test: bool = True):
238
+ """
239
+ Finální agregující funkce (2/2), která otestuje úplnost a správnost vstupních dat
240
+ a skrze kterou se přes API importují jednotlivé prvky glossary terms do datahubu.
241
+
242
+ :param df: předpřipravený a otestovaný df, v kterém jsou obsažena veškerá data
243
+ tvořící jednotlivé glossary terms potřebná pro import do datahubu
244
+ :param urn_actor: urn osoby, která glossary terms nahrává (urn:li:corpuser:jmeno.prijmeni
245
+ nebo urn:li:corpuser:prijmeni - dle struktury gmailu)
246
+ :param test: určení, jestli se jedná o test (True) nebo produkci (False)
247
+ :return: nahrání glossary terms na zvolený server datahubu (s informací o průběhu uploadu
248
+ na úrovni jednotlivých terms a nově vytvořeném urn pro daný term)
249
+ """
250
+
251
+ columns_for_is_column_filled = ['name', 'termCategory', 'isEntity']
252
+ for column_filled in columns_for_is_column_filled:
253
+ if not is_column_filled(df, column_filled):
254
+ raise ValueError(f"Not all values are filled in column '{column_filled}' .")
255
+
256
+ predefined_values = ['ROLE', 'ICT', 'DOKUMENT', 'OSTATNI', 'INSTITUCE']
257
+ if not has_specific_values(df, 'termCategory', predefined_values):
258
+ raise ValueError("The 'termCategory' column contains an invalid value.")
259
+
260
+ if not contains_boolean_values(df, 'isEntity'):
261
+ raise ValueError("The 'isEntity' column contains an invalid value (only bool accepted).")
262
+
263
+ if not no_duplicates_in_column(df, 'name'):
264
+ raise ValueError("The 'name' column contains duplicate values.")
265
+
266
+ columns_for_is_list_of_dicts = ['elements', 'owners']
267
+ for column_with_list in columns_for_is_list_of_dicts:
268
+ if not is_column_list_of_dicts(df, column_with_list):
269
+ raise ValueError(f"Not all values are proper list_of_dicts in column '{column_with_list}' .")
270
+
271
+ json_data = _df_to_dict(df, 'elements', urn_actor)
272
+ for key, value in json_data.items():
273
+ term_id = uuid.uuid1().hex
274
+ if value['parentNode'] == '':
275
+ glossary_term_info = [
276
+ {
277
+ 'entityType': 'glossaryTerm',
278
+ 'entityKeyAspect': {
279
+ '__type': 'GlossaryTermKey',
280
+ 'name': term_id
281
+ },
282
+ 'aspect': {
283
+ '__type': 'GlossaryTermInfo',
284
+ 'name': value['name'],
285
+ 'definition': value['definition'],
286
+ 'termSource': 'INTERNAL'
287
+ }
288
+ }
289
+ ]
290
+ else:
291
+ glossary_term_info = [
292
+ {
293
+ 'entityType': 'glossaryTerm',
294
+ 'entityKeyAspect': {
295
+ '__type': 'GlossaryTermKey',
296
+ 'name': term_id
297
+ },
298
+ 'aspect': {
299
+ '__type': 'GlossaryTermInfo',
300
+ 'name': value['name'],
301
+ 'definition': value['definition'],
302
+ 'parentNode': value['parentNode'],
303
+ 'termSource': 'INTERNAL'
304
+ }
305
+ }
306
+ ]
307
+ r = post_data(glossary_term_info, test)
308
+
309
+ urn = r.json()[0]
310
+
311
+ glossary_term_tacr_gen = {
312
+ 'entityType': 'glossaryTerm',
313
+ 'entityUrn': urn,
314
+ 'aspect': {
315
+ '__type': 'GlossaryTermTacrGen',
316
+ 'abbrev': value['abbrev'],
317
+ 'nameEn': value['nameEn'],
318
+ 'abbrevEn': value['abbrevEn'],
319
+ 'termCategory': value['termCategory']
320
+ }
321
+ }
322
+
323
+ glossary_term_tacr = {
324
+ 'entityType': 'glossaryTerm',
325
+ 'entityUrn': urn,
326
+ 'aspect': {
327
+ '__type': 'GlossaryTermTacr',
328
+ 'isEntity': value['isEntity']
329
+ }
330
+ }
331
+
332
+ glossary_term_tacr_entity = {
333
+ 'entityType': 'glossaryTerm',
334
+ 'entityUrn': urn,
335
+ 'aspect': {
336
+ '__type': 'GlossaryTermTacrEntity',
337
+ 'lov': value['lov_column_1'],
338
+ 'externalLov': value['lov_column_2'],
339
+ 'lovSource': value['lov_column_3']
340
+ }
341
+ }
342
+
343
+ if value['elements'] != '':
344
+ institutional_memory = {
345
+ 'entityType': 'glossaryTerm',
346
+ 'entityUrn': urn,
347
+ 'aspect': {
348
+ '__type': 'InstitutionalMemory',
349
+ 'elements': value['elements']
350
+
351
+ }
352
+ }
353
+ else:
354
+ pass
355
+
356
+ if value['owners'] != '':
357
+ ownership = {
358
+ 'entityType': 'glossaryTerm',
359
+ 'entityUrn': urn,
360
+ 'aspect': {
361
+ '__type': 'Ownership',
362
+ 'owners': value['owners']
363
+
364
+ }
365
+ }
366
+ else:
367
+ pass
368
+
369
+ api_structure = [glossary_term_tacr_gen, glossary_term_tacr,
370
+ glossary_term_tacr_entity, institutional_memory, ownership]
371
+
372
+ r = post_data(api_structure, test)
373
+
374
+ if 200 <= r.status_code <= 250:
375
+ print("Post successful:", value['name'], urn)
376
+ else:
377
+ print(f"Post failed. Status code: {r.status_code}")
378
+ print(value['name'], urn)
@@ -0,0 +1,123 @@
1
+ """ Modul obsahuje předdefinované funkce, které slouží jako kontrola
2
+ úplnosti dat importovaných skrze API do datahubu.
3
+ Primárním využitím je testování skrze tyto předdefinované funkce nad cílovým souborem.
4
+ """
5
+
6
+ import pandas as pd
7
+ import ast
8
+
9
+ # kontrola vyplnění všech hodnot ve sloupci
10
+ def is_column_filled(df: pd.DataFrame, column_name: object) -> bool:
11
+ """ Ověřovací funkce určená primárně jako vstup do testu, indikuje, zda je sloupec kompletně vyplněný,
12
+ tj. zda v daném sloupci nejsou obsaženy prázdné hodnoty.
13
+
14
+ :param df: dataframe, v rámci kterého chci danou podmínku ověřovat
15
+ :param column_name: sloupec v rámci dataframu, na který chci danou podmínku aplikovat
16
+ :return: bool hodnota podle toho, jestli je nebo není podmínka pro vybraný sloupec splněna
17
+ """
18
+
19
+ return df[column_name].notnull().all()
20
+
21
+
22
+ # kontrola, jestli jsou ve sloupci předepsané hodnoty
23
+ def has_specific_values(df: pd.DataFrame, column_name: object, predefined_values: list[any]) -> bool:
24
+ """ Ověřovací funkce určená primárně jako vstup do testu, indikuje, zda se ve sloupci vyskytují pouze předdefinované hodnoty.
25
+
26
+ :param df: dataframe, v rámci kterého chci danou podmínku ověřovat
27
+ :param column_name: sloupec v rámci dataframu, na který chci danou podmínku aplikovat
28
+ :param predefined_values: list předdefinovaných hodnot
29
+ :return: bool hodnota podle toho, jestli je nebo není podmínka pro vybraný sloupec splněna
30
+ """
31
+
32
+ return df[column_name].isin(predefined_values).all()
33
+
34
+
35
+ # kontrola, zda sloupec obsahuje pouze bool hodnoty (True nebo False)
36
+ def contains_boolean_values(df: pd.DataFrame, column_name: object) -> bool:
37
+ """ Ověřovací funkce určená primárně jako vstup do testu, indikuje, zda se ve sloupci vyskytují pouze bool hodnoty.
38
+
39
+ :param df: dataframe, v rámci kterého chci danou podmínku ověřovat
40
+ :param column_name: sloupec v rámci dataframu, na který chci danou podmínku aplikovat
41
+ :return: bool hodnota podle toho, jestli je nebo není podmínka pro vybraný sloupec splněna
42
+ """
43
+
44
+ return df[column_name].apply(lambda x: isinstance(x, bool)).all()
45
+
46
+
47
+ # kontrola, zda sloupec obsahuje pouze object values
48
+ def are_all_values_objects_in_column(df: pd.DataFrame, column_name: object) -> bool:
49
+ """ Ověřovací funkce určená primárně jako vstup do testu, indikuje, zda se ve sloupci vyskytují pouze object hodnoty.
50
+
51
+ :param df: dataframe, v rámci kterého chci danou podmínku ověřovat
52
+ :param column_name: sloupec v rámci dataframu, na který chci danou podmínku aplikovat
53
+ :return: bool hodnota podle toho, jestli je nebo není podmínka pro vybraný sloupec splněna
54
+ """
55
+
56
+ column = df[column_name]
57
+ return all(isinstance(value, object) for value in column)
58
+
59
+
60
+ # kontrola duplicit
61
+ def no_duplicates_in_column(df: pd.DataFrame, column_name: object) -> bool:
62
+ """ Ověřovací funkce určená primárně jako vstup do testu, indikuje, zda se ve sloupci vyskytují duplicitní hodnoty.
63
+
64
+ :param df: dataframe, v rámci kterého chci danou podmínku ověřovat
65
+ :param column_name: sloupec v rámci dataframu, na který chci danou podmínku aplikovat
66
+ :return: bool hodnota podle toho, jestli je nebo není podmínka pro vybraný sloupec splněna
67
+ """
68
+
69
+ return not df[column_name].duplicated().any()
70
+
71
+
72
+ # kontrola, zda se ve sloupci nachází hodnoty strukturované do list[dict].
73
+ def is_column_list_of_dicts(df: pd.DataFrame, column_name: object) -> bool:
74
+ """ Ověřovací funkce určená primárně jako vstup do testu, ověřuje, zda buňky ve sloupci obsahují pouze obsah strukturovaný do listu slovníků (v případě, že nejsou prázdné).
75
+
76
+ :param df: dataframe, v rámci kterého chci danou podmínku ověřovat
77
+ :param column_name: sloupec v rámci dataframu, na který chci danou podmínku aplikovat
78
+ :return: bool hodnota podle toho, jestli je nebo není podmínka pro vybraný sloupec splněna
79
+ """
80
+
81
+ filtered_df = df[df[column_name].notna() & (df[column_name] != '')]
82
+ column = filtered_df[column_name]
83
+ for i, cell_value in enumerate(column):
84
+ if isinstance(cell_value, list):
85
+ for item in cell_value:
86
+ if not isinstance(item, dict):
87
+ print(f"Row {i} contains a non-dict element: {item}")
88
+ return False
89
+ elif isinstance(cell_value, str):
90
+ try:
91
+ parsed_value = ast.literal_eval(cell_value)
92
+ if not isinstance(parsed_value, list):
93
+ print(f"Row {i} is not a list: {parsed_value}")
94
+ return False
95
+ for item in parsed_value:
96
+ if not isinstance(item, dict):
97
+ print(f"Row {i} contains a non-dict element: {item}")
98
+ return False
99
+ except (ValueError, SyntaxError):
100
+ print(f"Row {i} is not a valid list/dictionary: {cell_value}")
101
+ return False
102
+ else:
103
+ print(f"Row {i} is not a list or string: {cell_value}")
104
+ return False
105
+
106
+ return True
107
+
108
+
109
+ # kontrola zda se ve sloupci v rámci list[dict] nenachází nespárované komponenty (např. prázdné values)
110
+ def notnan_in_columns_with_list_of_dicts(df: pd.DataFrame, column_name: object) -> bool:
111
+ """ Ověřovací funkce určená primárně jako vstup do testu, ověřuje, zda buňky s obsahem strukturovaným do listu slovníků neobsahují Nan values uvnitř slovníků.
112
+
113
+ :param df: dataframe, v rámci kterého chci danou podmínku ověřovat
114
+ :param column_name: sloupec v rámci dataframu, na který chci danou podmínku aplikovat
115
+ :return: bool hodnota podle toho, jestli je nebo není podmínka pro vybraný sloupec splněna
116
+ """
117
+
118
+ notnan_values = [': nan',': NaN']
119
+ for value in notnan_values:
120
+ if df[column_name].str.contains(value).any():
121
+ return False
122
+ else:
123
+ return True
@@ -0,0 +1,78 @@
1
+ """Soubor funkcí k práci s OpenAPI DataHubu."""
2
+
3
+ import requests
4
+
5
+
6
+ def post_data(json_data: list[dict], test: bool = True) -> requests.Response:
7
+ """ Nahraje přes OpenAPI data do DataHubu.
8
+
9
+ Než se data nahrají je vhodné provést kontroly správnosti dat.
10
+
11
+ :param json_data: data ve formátu JSON, které se mají nahrát do DataHubu
12
+ :param test: nahraje data buď na testovací nebo produkční prostředí
13
+ :return: objekt třídy Response, který reprezentuje odezvu serveru
14
+ """
15
+
16
+ if test:
17
+ server = 'http://datahub-test.tacr.cz:8080/'
18
+ else:
19
+ server = 'http://datahub.tacr.cz:8080/'
20
+
21
+ query = 'openapi/entities/v1/'
22
+ url = server + query
23
+
24
+ r = requests.post(url, json=json_data)
25
+
26
+ return r
27
+
28
+
29
+ def get_data(urn: str, test: bool = True) -> dict:
30
+ """ Získá data k vybrané entitě přes OpenAPI.
31
+
32
+ :param urn: URN entity, pro kterou chceme získat data
33
+ :param test: získá data buď z testovacího nebo produkčního prostředí
34
+ :return: data ve formátu JSON
35
+ """
36
+
37
+ if test:
38
+ server = 'http://datahub-test.tacr.cz:8080/'
39
+ else:
40
+ server = 'http://datahub.tacr.cz:8080/'
41
+
42
+ query = f'openapi/entities/v1/latest?urns={urn}'
43
+
44
+ url = server + query
45
+
46
+ r = requests.get(url)
47
+
48
+ json_data = r.json()['responses']
49
+
50
+ return json_data
51
+
52
+
53
+ def delete_data(urn: str, test: bool = True, soft: bool = False) -> requests.Response:
54
+ """ Vymaže data vybrané entity přes OpenAPI
55
+
56
+ :param urn: URN entity, kterou chceme vymazat
57
+ :param test: vymaže data z testovacího nebo produkčního prostředí
58
+ :param soft: můžeme entitu vymazat pouze z vyhledávání (soft = True) nebo kompletně (soft = False)
59
+ :return: objekt třídy Response, který reprezentuje odezvu serveru
60
+ """
61
+
62
+ if test:
63
+ server = 'http://datahub-test.tacr.cz:8080/'
64
+ else:
65
+ server = 'http://datahub.tacr.cz:8080/'
66
+
67
+ if soft:
68
+ soft_delete = '&soft=true'
69
+ else:
70
+ soft_delete = '&soft=false'
71
+
72
+ query = f'openapi/entities/v1/?urns={urn}'
73
+
74
+ url = server + query + soft_delete
75
+
76
+ r = requests.delete(url)
77
+
78
+ return r