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,70 @@
1
+ """
2
+ Modul pro běžné transformace a zpracování dat.
3
+ """
4
+
5
+ import pandas as pd
6
+
7
+
8
+ def create_mapping_dict(df: pd.DataFrame) -> dict:
9
+ """ Z dataframe, kde se k jedné hodnotě váže více pozorování v samostatných řádcích (např. projekt má N uchazečů)
10
+ vytvoří mapovací dict
11
+
12
+ :param df: dataframe s hodnotami one-to-many
13
+ :return: mapovací dict, kde unikátní ID je klíč a hodnotou je seznam hodnot, které patří k danému unikátnímu ID
14
+ """
15
+
16
+ if df.shape[1] < 2:
17
+ raise ValueError("DataFrame mmusí mít aspoň da sloupce pro vytvoření mapovacího slovníku.")
18
+
19
+ grouped_data = df.groupby(df.columns[0])[df.columns[1]]
20
+ return grouped_data.apply(list).to_dict()
21
+
22
+
23
+ def list_intersection(list1: list, list2: list, percentages: bool = True) -> dict:
24
+ """ Získá průnik hodnot mezi dvěma seznamy (listy) a vypočítá metriky průniku.
25
+
26
+ Metriky průniku:
27
+
28
+ - *intersect (list)* - seznam stejných hodnot
29
+ - *intersect_count (int)* - počet stejných hodnot
30
+ - *intersect_ratio (float)* - podíl stejných hodnot vůči všem unikátním hodnotám z obou seznamů
31
+ - *intersect_l1_ratio (float)* - podíl stejných hodnot vůči všem hodnotám v prvnímu seznamu
32
+ - *intersect_l2_ratio (float)* - podíl stejných hodnot vůči všem hodnotám v druhému seznamu
33
+
34
+ :param list1: seznam hodnot
35
+ :param list2: seznam hodnot
36
+ :param percentages: poměrové metriky zobrazí vrátí v procentech (0-100) s přesností na dvě desetinná místa
37
+ :return: dict metrik průniků
38
+ """
39
+
40
+ if not list1:
41
+ raise ValueError("list1 je prázdný")
42
+ if not list2:
43
+ raise ValueError("list2 je prázdný")
44
+
45
+ set1, set2 = set(list1), set(list2)
46
+ intersect = set1.intersection(set2)
47
+ l1_count = len(set1)
48
+ l2_count = len(set2)
49
+ all_count = len(set1.union(set2))
50
+
51
+ intersect_count = len(intersect)
52
+
53
+ intersect_dict = dict()
54
+ intersect_dict['intersect'] = list(intersect)
55
+ intersect_dict['intersect_count'] = intersect_count
56
+
57
+ intersect_ratio = intersect_count / all_count
58
+ intersect_l1_ratio = intersect_count / l1_count
59
+ intersect_l2_ratio = intersect_count / l2_count
60
+
61
+ if percentages:
62
+ rounding = lambda x: round(x * 100, 2) # prevod na procenta
63
+ intersect_dict['intersect_ratio'] = rounding(intersect_ratio)
64
+ intersect_dict['intersect_ratio_l1'] = rounding(intersect_l1_ratio)
65
+ intersect_dict['intersect_ratio_l2'] = rounding(intersect_l2_ratio)
66
+ else:
67
+ intersect_dict['intersect_ratio'] = intersect_ratio
68
+ intersect_dict['intersect_ratio_l1'] = intersect_l1_ratio
69
+ intersect_dict['intersect_ratio_l2'] = intersect_l2_ratio
70
+ return intersect_dict
@@ -0,0 +1,5 @@
1
+ from . import data_lineage
2
+ from . import datasets
3
+ from . import glossary
4
+ from . import import_checks
5
+ from . import openapi
@@ -0,0 +1,86 @@
1
+ """Soubor funkcí, které slouží k hromadnému vytváření podkladů k tvorbě a úpravě data lineage \
2
+ v DataHubu přes API."""
3
+
4
+
5
+ def _create_field_lineage(entity_urn: str, upstream_urn: str, field_dict: dict[str, str]) -> dict[str, any]:
6
+ """ Vytvoří data lineage na úrovni dvou fieldů.
7
+
8
+ :param entity_urn: URN datasetu, ve kterém je field, pro které tvoříme data lineage
9
+ :param upstream_urn: URN datasetu, ve kterém je field, na který má vazbu (lineage)
10
+ :param field_dict: informace a filed level lineage
11
+ :return: dict s informacemi o vazbě mezi dvěma fieldy
12
+ """
13
+
14
+ field_upstream_name = field_dict['upstream_lineage_field']
15
+ field_upstream_urn = f'urn:li:schemaField:({upstream_urn},{field_upstream_name})'
16
+ field_upstream_type = field_dict['upstream_field_type']
17
+
18
+ field_downstream_name = field_dict['downstream_lineage_field']
19
+ field_downstream_urn = f'urn:li:schemaField:({entity_urn},{field_downstream_name})'
20
+ field_downstream_type = field_dict['downstream_field_type']
21
+
22
+ field_lineage = {
23
+ 'upstreamType': field_upstream_type,
24
+ 'upstreams': [
25
+ field_upstream_urn
26
+ ],
27
+ 'downstreamType': field_downstream_type,
28
+ 'downstreams': [
29
+ field_downstream_urn
30
+ ]
31
+ }
32
+
33
+ return field_lineage
34
+
35
+
36
+ def create_dataset_lineage(entity_name: str, platform: str, lineage_dict: dict[str, any]
37
+ , field_level: bool = False) -> dict[str, any]:
38
+ """ Vytvoří data lineage (vazby) pro daný dataset.
39
+
40
+ Umožňuje vytvářet data lineage na úrovni datasetů i na úrovni jednotlivých fieldů.
41
+
42
+ :param entity_name: název datasetu, pro který se data lineage vytváří
43
+ :param platform: název platformy, která je zdrojem datasetu
44
+ (např. etalon, googlesheets, ISTA, Postgres, OpenAPI...)
45
+ :param lineage_dict: dict datasetu, který obsahuje jednotlivé vazby (na úrovni datasetů nebo na úrovni fieldů)
46
+ :param field_level: informace, jestli obsahuje dataset lineage na úrovni fieldů
47
+ :return: dict vazeb (data lineage) pro daný dataset
48
+ """
49
+
50
+ entity_urn = f'urn:li:dataset:(urn:li:dataPlatform:{platform},{entity_name},PROD)'
51
+
52
+ upstreams = []
53
+ field_lineages = []
54
+
55
+ for key, value in lineage_dict.items():
56
+ upstream_name = value['upstream_lineage_dataset']
57
+ upstream_platform = value['upstream_platform']
58
+ upstream_urn = f'urn:li:dataset:(urn:li:dataPlatform:{upstream_platform},{upstream_name},PROD)'
59
+ upstream_type = value['upstream_dataset_type']
60
+
61
+ upstream_dict = {
62
+ 'dataset': upstream_urn,
63
+ 'type': upstream_type
64
+ }
65
+
66
+ # pro field_lineage bude existovat více záznamů pro vazbu dvou datasetů, proto se "filtruje"
67
+ if upstream_dict not in upstreams:
68
+ upstreams.append(upstream_dict)
69
+
70
+ if field_level:
71
+ field_lineage = _create_field_lineage(entity_urn, upstream_urn, value)
72
+ field_lineages.append(field_lineage)
73
+
74
+ upstream_lineage = {
75
+ "entityType": "dataset",
76
+ "entityUrn": entity_urn,
77
+ "aspect": {
78
+ "__type": "UpstreamLineage",
79
+ "upstreams": upstreams,
80
+ "fineGrainedLineages": field_lineages
81
+ }
82
+ }
83
+
84
+ return upstream_lineage
85
+
86
+ # TODO: rozdílné funkce na vytvoření nové data lineage a update stávající data lineage
@@ -0,0 +1,421 @@
1
+ """Soubor funkcí, které slouží k vytváření podkladů pro DataHub a k nahrávání dat přes API \
2
+ včetně checků vyplněných údajů.
3
+ """
4
+
5
+ import pandas as pd
6
+ import os
7
+ import copy
8
+ import datetime
9
+
10
+ from enum import Enum
11
+ import typing
12
+
13
+ # template pro přidání fieldů
14
+ FIELD_TEMPLATE = {
15
+ 'fieldPath': str(), # název pole, odpovídá názvu sloupce v DB, pro etalon anglický název
16
+ 'nullable': None, # hodnoty nemusí být ve slouci vyplněny - True/False
17
+ 'description': None, # popis pole, co daný sloupec reprezentuje, pro etalon může být český ekvivalent názvu
18
+ 'type': {
19
+ # specifický datový typ DataHub - za tečkou Nulltype, pokud chceme pouze klasické datové typy
20
+ # com.linkedin.pegasus2avro.schema.NullType
21
+ # BooleanType, FixedType, StringType, BytesType, NumberType, DateType, TimeType,
22
+ # EnumType, MapType, ArrayType, UnionType, RecordType
23
+ 'type': {
24
+ "com.linkedin.pegasus2avro.schema.NullType": {}
25
+ }
26
+ },
27
+ # klasický datový typ - string, date, integer atd.
28
+ 'nativeDataType': None,
29
+ }
30
+
31
+ # template pro jeden dataset
32
+ DATASET_TEMPLATE = {
33
+ "auditHeader": None,
34
+ "proposedSnapshot": {
35
+ "com.linkedin.pegasus2avro.metadata.snapshot.DatasetSnapshot": {
36
+ # vloží (vytvoří) strukturu, kde se má dataset nacházet
37
+ # dataPlatform - PostgreSQL, MySQL, Hive ... i "vlastní" platformy - Etalon
38
+ # struktura v rámci platformy DB.schema.dataset
39
+ # např. promo--rejstrik.public.rejstrik_entity
40
+ # prostředí PROD, PROMO, TEST atd.
41
+ # pro etalony vkládat rovnou do platformy bez struktury
42
+ # urn:li:dataPlatform:etalon,odhad,PROD
43
+ "urn": str(),
44
+ # základní informace o data setu, struktura datasetu, vlastníci, dokumentace atd.
45
+ "aspects": [
46
+ {
47
+ "com.linkedin.pegasus2avro.dataset.DatasetProperties": {
48
+ "description": None,
49
+ "uri": None,
50
+ "tags": [],
51
+ "customProperties": {},
52
+ "name": str() # nastaví název, který se jinak generuje ze schemaName
53
+ }
54
+ },
55
+ {
56
+ "com.linkedin.pegasus2avro.schema.SchemaMetadata": {
57
+ # v GUI se rálně nezobrazuje, v GUI název odpovídá struktuře v rámci platformy
58
+ "schemaName": str(),
59
+ "platform": "urn:li:dataPlatform:etalon",
60
+ "version": 0,
61
+ "hash": "", # nevím k čemu, ale bez toho to nefunguje
62
+ "platformSchema": { # nevím k čemu, ale bez toho to nefunguje
63
+ "com.linkedin.pegasus2avro.schema.OtherSchema": {
64
+ "rawSchema": ""
65
+ }
66
+ },
67
+ "fields": None, # sloupce v tabulce (datasetu)
68
+ "foreignKeys": None # cizí klíče a odkaz na tabulky (datasety) cizích klíčů
69
+ }
70
+ }
71
+ ]
72
+ }
73
+ }
74
+ }
75
+
76
+ # template pro přidání cizích klíčů
77
+ FOREIGN_KEYS_TEMPLATE = {
78
+ "name": None,
79
+ "foreignFields": [], # list fieldů v cizím datasetu, na které se má vytvořit vazba
80
+ "sourceFields": [], # list fieldů ve zdrojovém datasetu, pro které se má vytvořit vazba
81
+ "foreignDataset": str() # URN cizího datasetu, na který se odkazujeme
82
+ }
83
+
84
+
85
+ class OwnerType(Enum):
86
+ DATA_STEWARD = 'Data Steward'
87
+ TECHNICAL_OWNER = 'Technical Owner'
88
+ BUSINESS_OWNER = 'Business Owner'
89
+
90
+
91
+ def _create_fields(entity_dict: list[dict], nested_fields: bool) -> list[dict]:
92
+ """Vytvoří záznamy pro jednotlivé fieldy v datasetu na základě šablony.
93
+
94
+ V případě, že dataset (datová entita) obsahuje nested fields, pak volá funkci (_create_nested_fields),
95
+ která vytvoří jejich technický název. (do formátu nested_field.field, nested_field.other_nested_field.field).
96
+
97
+ :param entity_dict: dict datové entity, který obsahuje jednotlivé fieldy a další informace fieldů
98
+ :param nested_fields: informace, jestli dataset obsahuje vnořené fieldy (nested fields)
99
+ :return: list dictů jednotlivých fieldů
100
+ """
101
+
102
+ all_fields = []
103
+
104
+ for field in entity_dict:
105
+ field_dict = FIELD_TEMPLATE.copy()
106
+
107
+ if nested_fields:
108
+ if pd.isnull(field['upstream_lineage']):
109
+ field_dict['fieldPath'] = field['field_name']
110
+ else:
111
+ field_dict['fieldPath'] = _create_nested_field(field['field_name']
112
+ , field['upstream_lineage'])
113
+ else:
114
+ field_dict['fieldPath'] = field['field_name']
115
+
116
+ field_dict['description'] = field['field_description']
117
+ field_dict['nativeDataType'] = field['field_data_type']
118
+
119
+ all_fields.append(field_dict)
120
+
121
+ return all_fields
122
+
123
+
124
+ def _create_nested_field(field_name: str, upstream_lineage: str) -> str:
125
+ """Vytvoří složený název pro nested fields.
126
+
127
+ Obecně slouží pro funkcionalitu rozbalování a zobrazení vnořených polí (nested fields) v DataHub.
128
+ Nejvyšší nested field -> nižší nested field -> technický název fieldů.
129
+ Formát - nested_field.field, nested_field.other_nested_field.field
130
+
131
+ :param field_name: technický název fieldů
132
+ :param upstream_lineage: seznam nested fieldů, ve kterých se field zobrazí. Odělené středníkem
133
+ :return: složený název fieldů (Formát - nested_field.field, nested_field.other_nested_field.field)
134
+ """
135
+
136
+ upstream_lineage_list = upstream_lineage.split(';')
137
+ upstream_lineage = '.'.join(upstream_lineage_list)
138
+ if field_name in upstream_lineage_list:
139
+ field_name = upstream_lineage
140
+
141
+ else:
142
+ field_name = f'{upstream_lineage}.{field_name}'
143
+ return field_name
144
+
145
+
146
+ def _create_foreign_keys(entity_dict: list[dict], urn: str, platform: str) -> list[dict]:
147
+ """Vytvoří odkaz na jiné datasety skrze cizí klíče (foreign keys) na základě šablony.
148
+
149
+ Funkce vytvoří potřebné URN datasetů a fieldů. V současné chvíli je možné přiřadit pouze
150
+ jeden foreign key k jednomu fieldu.
151
+
152
+ :param entity_dict: dict datové entity, který obsahuje jednotlivé fieldy a další informace fieldů
153
+ :param urn: URN zdrojového datasetu
154
+ :param platform: název platformy, která je zdrojem datasetu
155
+ (např. etalon, googlesheets, ISTA, Postgres, OpenAPI...)
156
+ :return: list dictů jednotlivých cizích klíčů (foreign keys)
157
+ """
158
+
159
+ foreign_keys = []
160
+
161
+ for field in entity_dict:
162
+ if pd.isnull(field['foreign_key']):
163
+ pass
164
+ else:
165
+ fk_dict = FOREIGN_KEYS_TEMPLATE.copy()
166
+ fk_dict['name'] = field['field_name']
167
+
168
+ if pd.isnull(field['upstream_lineage']):
169
+ fieldPath = field['field_name']
170
+ else:
171
+ fieldPath = _create_nested_field(field['field_name'], field['upstream_lineage'])
172
+
173
+ foreign_dataset_name = '_'.join(field['foreign_key'].lower().split(' '))
174
+ foreign_dataset_urn = f'urn:li:dataset:(urn:li:dataPlatform:{platform},{foreign_dataset_name},PROD)'
175
+ foreign_field_urn = f'urn:li:schemaField:({foreign_dataset_urn},Entity Id)'
176
+ source_urn = f'urn:li:schemaField:({urn},{fieldPath})'
177
+
178
+ fk_dict['foreignFields'] = [foreign_field_urn]
179
+ fk_dict['sourceFields'] = [source_urn]
180
+ fk_dict['foreignDataset'] = foreign_dataset_urn
181
+
182
+ foreign_keys.append(fk_dict)
183
+
184
+ return foreign_keys
185
+
186
+
187
+ def _create_dataset(entity: str, platform: str, entity_dict: list[dict]
188
+ , nested_fields: bool, foreign_keys: bool, subfolder: str = None) -> dict:
189
+ """ Doplní template dictu datasetu o potřebné atributy a objekty.
190
+
191
+ Vytvoří urn (ID) datasetu na základě jmenné konvence.
192
+ Volá interní funkce pro vytvoření záznamů jednoltivých fieldů (_create_fields) \
193
+ a přiřazení cizích klíčů (_create_foreign_keys).
194
+
195
+ :param entity: název datové entity (datasetu)
196
+ :param platform: název platformy, která je zdrojem datasetu
197
+ (např. etalon, googlesheets, ISTA, Postgres, OpenAPI...)
198
+ :param entity_dict: dict datové entity, který obsahuje jednotlivé fieldy a další informace fieldů
199
+ :param nested_fields: informace, jestli dataset obsahuje vnořené fieldy (nested fields)
200
+ :param foreign_keys: informace, jestli dataset obsahuje cizí klíče (foreign keys)
201
+ :param subfolder: podsložka, v rámci které má být dataset uložený
202
+ :return: dict reprezentace datové entity (datasetu), včetně fieldů a cizích klíčů
203
+ """
204
+
205
+ dataset = DATASET_TEMPLATE.copy()
206
+
207
+ if subfolder is None:
208
+ entity_name = entity
209
+ else:
210
+ entity_name = f'{subfolder}.{entity}'
211
+
212
+ urn = f"urn:li:dataset:(urn:li:dataPlatform:{platform},{entity_name},PROD)"
213
+
214
+ fields = _create_fields(entity_dict, nested_fields)
215
+
216
+ if foreign_keys:
217
+ foreign_keys_list = _create_foreign_keys(entity_dict, urn, platform)
218
+ else:
219
+ foreign_keys_list = None
220
+
221
+ level1 = 'proposedSnapshot'
222
+ level2 = 'com.linkedin.pegasus2avro.metadata.snapshot.DatasetSnapshot'
223
+
224
+ level4a = 'com.linkedin.pegasus2avro.dataset.DatasetProperties'
225
+ level4b = 'com.linkedin.pegasus2avro.schema.SchemaMetadata'
226
+
227
+ dataset[level1][level2]['aspects'][0][level4a]['name'] = entity
228
+
229
+ dataset[level1][level2]['urn'] = urn
230
+ dataset[level1][level2]['aspects'][1][level4b]['schemaName'] = entity_name
231
+ dataset[level1][level2]['aspects'][1][level4b]['platform'] = f'urn:li:dataPlatform:{platform}'
232
+ dataset[level1][level2]['aspects'][1][level4b]['fields'] = fields
233
+ dataset[level1][level2]['aspects'][1][level4b]['foreignKeys'] = foreign_keys_list
234
+
235
+ return dataset
236
+
237
+
238
+ def create_etalon(etalon_df: pd.DataFrame, etalon_name: str, platform: str
239
+ , nested_fields: bool, foreign_keys: bool, subfolder: str = None) -> dict[str, any]:
240
+ """
241
+ Vytvoří dict datasetu k importu do DataHub pro jednu datovou entitu.
242
+
243
+ Z načteného souboru získá, případně vytvoří potřebné informace k vytvoření informací o datasetu.
244
+ Název entity získává z názvu souboru => v názvu souboru musí být název entity.
245
+
246
+ Povinné sloupce v načteném souboru:
247
+
248
+ * field_name
249
+ * field_description
250
+ * field_data_type
251
+
252
+ Volitelné sloupce:
253
+
254
+ * upstream_lineage
255
+ * foreign_key
256
+
257
+ :param etalon_df: zdrojový dataframe s informacemi o etalonu
258
+ :param etalon_name: název etalonu
259
+ :param platform: název platformy, která je zdrojem datasetu
260
+ (např. etalon, googlesheets, ISTA, Postgres, OpenAPI...)
261
+ :param nested_fields: informace, jestli dataset obsahuje vnořené fieldy (nested fields)
262
+ :param foreign_keys: informace, jestli dataset obsahuje cizí klíče (foreign keys)
263
+ :param subfolder: podsložka, v rámci které má být dataset uložený
264
+ :return: etalon ve formě dict, které je možné převést do JSON a importovat do DataHub
265
+ """
266
+
267
+ entity_dict = etalon_df.to_dict('records')
268
+
269
+ etalon = _create_dataset(etalon_name, platform, entity_dict, nested_fields, foreign_keys, subfolder)
270
+
271
+ return etalon
272
+
273
+
274
+ def create_etalon_bulk(etalon_df: pd.DataFrame, platform: str
275
+ , nested_fields: bool, foreign_keys: bool, subfolder: str = None) -> list[dict[str, any]]:
276
+ """
277
+ Vytvoří dict datasetu k importu do DataHub pro více datových entit.
278
+
279
+ Z načteného souboru získá, případně vytvoří potřebné informace k vytvoření informací o datasetu.
280
+ Název entity získává ze sloupce entity_name, ten slouží i k filtrování jednotlivých entit
281
+ a jejich následné zpracování.
282
+
283
+ Povinné sloupce v načteném souboru:
284
+
285
+ * entity_name
286
+ * field_name
287
+ * field_description
288
+ * field_data_type
289
+
290
+ Volitelné sloupce:
291
+
292
+ * upstream_lineage
293
+ * foreign_key
294
+
295
+ :param etalon_df: zdrojový dataframe s informacemi o etalonu
296
+ :param platform: název platformy, která je zdrojem datasetu
297
+ (např. etalon, googlesheets, ISTA, Postgres, OpenAPI...)
298
+ :param nested_fields: informace, jestli dataset obsahuje vnořené fieldy (nested fields)
299
+ :param foreign_keys: informace, jestli dataset obsahuje cizí klíče (foreign keys)
300
+ :param subfolder: podsložka, v rámci které má být dataset uložený
301
+ :return: etalon ve formě dict, které je možné převést do JSON a importovat do DataHub
302
+ """
303
+
304
+ etalon_list = []
305
+
306
+ for entity in etalon_df['entity_name'].unique():
307
+ entity_df = etalon_df[etalon_df['entity_name'] == entity]
308
+ entity_df = entity_df.drop(columns=['entity_name'])
309
+
310
+ entity_dict = entity_df.to_dict('records')
311
+
312
+ entity_name = '_'.join(entity.lower().split(' '))
313
+ etalon = _create_dataset(entity_name, platform, entity_dict, nested_fields, foreign_keys, subfolder)
314
+ etalon_list.append(copy.deepcopy(etalon))
315
+
316
+ return etalon_list
317
+
318
+
319
+ def dataset_description(urn: str, description: str) -> list[dict[str, any]]:
320
+ """ Vytvoří JSON pro přiřazení popisu datové sady k existující datové sadě.
321
+
322
+ :param urn: URN existujícího datasetu, ve kterém se mají vytvořit/upravit údaje
323
+ :param description: popis datasetu
324
+ :return: list dictů s potřebnými informace k nahrání do DataHub
325
+ """
326
+
327
+ json_data = [{
328
+ 'entityType': 'dataset',
329
+ 'entityUrn': urn,
330
+ 'aspect': {
331
+ '__type': 'EditableDatasetProperties',
332
+ 'description': description
333
+ }
334
+ }]
335
+ return json_data
336
+
337
+
338
+ def dataset_link(urn: str, link_name: str, link_url: str, corpuser: str) -> list[dict[str, any]]:
339
+ """ Vytvoří JSON pro přiřazení odkazů k existujícímu datasetu.
340
+
341
+ V současné chvíli funguje pouze pro jeden odkaz.
342
+
343
+ :param urn: URN existujícího datasetu, ve kterém se mají vytvořit/upravit údaje
344
+ :param link_name: zobrazený název odkazu
345
+ :param link_url: odkaz ve formátu url
346
+ :param corpuser: uživatel, který odkaz vytvořil (ve formátu jmeno.prijmeni, bez diakritiky)
347
+ :return: list dictů s potřebnými informace k nahrání do DataHub
348
+ """
349
+
350
+ dt = datetime.datetime.now()
351
+ ts = int(datetime.datetime.timestamp(dt) * 1000)
352
+
353
+ corpuser_urn = f'urn:li:corpuser:{corpuser}'
354
+ json_data = [{
355
+ 'entityType': 'dataset',
356
+ 'entityUrn': urn,
357
+ 'aspect': {
358
+ '__type': 'InstitutionalMemory',
359
+ 'elements': [
360
+ {
361
+ 'url': link_url,
362
+ 'description': link_name,
363
+ 'createStamp': {
364
+ 'time': ts,
365
+ 'actor': corpuser_urn
366
+ }
367
+ }
368
+ ]
369
+ }
370
+ }]
371
+ return json_data
372
+
373
+
374
+ def dataset_ownership(urn: str, corpuser: str, owner_type: OwnerType) -> list[dict[str, any]]:
375
+ """ Vytvoří JSON pro přiřazení vlastníka k existujícímu datasetu.
376
+
377
+ V současné chvíli funguje pouze pro jednoho vlastníka.
378
+
379
+ :param urn: URN existujícího datasetu, ve kterém se mají vytvořit/upravit údaje
380
+ :param corpuser: uživatel, který se má přiřadit jako vlastník datasetu (ve formátu jmeno.prijmeni, bez diakritiky)
381
+ :param owner_type: typ vlastníka (DATA_STEWARD, TECHNICAL_OWNER, BUSINESS_OWNER)
382
+ :return: list dictů s potřebnými informace k nahrání do DataHub
383
+ """
384
+
385
+ corpuser_urn = f'urn:li:corpuser:{corpuser}'
386
+ json_data = [{
387
+ 'entityType': 'dataset',
388
+ 'entityUrn': urn,
389
+ 'aspect': {
390
+ '__type': 'Ownership',
391
+ 'owners': [
392
+ {
393
+ 'owner': corpuser_urn,
394
+ 'type': owner_type,
395
+ }
396
+ ]
397
+ }
398
+ }]
399
+ return json_data
400
+
401
+
402
+ def dataset_tag(urn: str, tags: list[str]) -> list[dict[str, any]]:
403
+ """ Vytvoří JSON pro přiřazení tagu/tagů k existujícímu datasetu.
404
+
405
+ :param urn: URN existujícího datasetu, ve kterém se mají vytvořit/upravit údaje
406
+ :param tags: list tagů, které se mají přiřadit k datasetu
407
+ :return: list dictů s potřebnými informace k nahrání do DataHub
408
+ """
409
+
410
+ tag_urns = [f'urn:li:tag:{tag}' for tag in tags]
411
+ json_data = [{
412
+ 'entityType': 'dataset',
413
+ 'entityUrn': urn,
414
+ 'aspect': {
415
+ '__type': 'GlobalTags',
416
+ 'tags': [
417
+ {'tag': tag_urn} for tag_urn in tag_urns
418
+ ]
419
+ }
420
+ }]
421
+ return json_data