semantic-link-labs 0.4.1__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.

Potentially problematic release.


This version of semantic-link-labs might be problematic. Click here for more details.

Files changed (52) hide show
  1. semantic_link_labs-0.4.1.dist-info/LICENSE +21 -0
  2. semantic_link_labs-0.4.1.dist-info/METADATA +22 -0
  3. semantic_link_labs-0.4.1.dist-info/RECORD +52 -0
  4. semantic_link_labs-0.4.1.dist-info/WHEEL +5 -0
  5. semantic_link_labs-0.4.1.dist-info/top_level.txt +1 -0
  6. sempy_labs/__init__.py +154 -0
  7. sempy_labs/_ai.py +496 -0
  8. sempy_labs/_clear_cache.py +39 -0
  9. sempy_labs/_connections.py +234 -0
  10. sempy_labs/_dax.py +70 -0
  11. sempy_labs/_generate_semantic_model.py +280 -0
  12. sempy_labs/_helper_functions.py +506 -0
  13. sempy_labs/_icons.py +4 -0
  14. sempy_labs/_list_functions.py +1372 -0
  15. sempy_labs/_model_auto_build.py +143 -0
  16. sempy_labs/_model_bpa.py +1354 -0
  17. sempy_labs/_model_dependencies.py +341 -0
  18. sempy_labs/_one_lake_integration.py +155 -0
  19. sempy_labs/_query_scale_out.py +447 -0
  20. sempy_labs/_refresh_semantic_model.py +184 -0
  21. sempy_labs/_tom.py +3766 -0
  22. sempy_labs/_translations.py +378 -0
  23. sempy_labs/_vertipaq.py +893 -0
  24. sempy_labs/directlake/__init__.py +45 -0
  25. sempy_labs/directlake/_directlake_schema_compare.py +110 -0
  26. sempy_labs/directlake/_directlake_schema_sync.py +128 -0
  27. sempy_labs/directlake/_fallback.py +62 -0
  28. sempy_labs/directlake/_get_directlake_lakehouse.py +69 -0
  29. sempy_labs/directlake/_get_shared_expression.py +59 -0
  30. sempy_labs/directlake/_guardrails.py +84 -0
  31. sempy_labs/directlake/_list_directlake_model_calc_tables.py +54 -0
  32. sempy_labs/directlake/_show_unsupported_directlake_objects.py +89 -0
  33. sempy_labs/directlake/_update_directlake_model_lakehouse_connection.py +81 -0
  34. sempy_labs/directlake/_update_directlake_partition_entity.py +64 -0
  35. sempy_labs/directlake/_warm_cache.py +210 -0
  36. sempy_labs/lakehouse/__init__.py +24 -0
  37. sempy_labs/lakehouse/_get_lakehouse_columns.py +81 -0
  38. sempy_labs/lakehouse/_get_lakehouse_tables.py +250 -0
  39. sempy_labs/lakehouse/_lakehouse.py +85 -0
  40. sempy_labs/lakehouse/_shortcuts.py +296 -0
  41. sempy_labs/migration/__init__.py +29 -0
  42. sempy_labs/migration/_create_pqt_file.py +239 -0
  43. sempy_labs/migration/_migrate_calctables_to_lakehouse.py +429 -0
  44. sempy_labs/migration/_migrate_calctables_to_semantic_model.py +150 -0
  45. sempy_labs/migration/_migrate_model_objects_to_semantic_model.py +524 -0
  46. sempy_labs/migration/_migrate_tables_columns_to_semantic_model.py +165 -0
  47. sempy_labs/migration/_migration_validation.py +227 -0
  48. sempy_labs/migration/_refresh_calc_tables.py +129 -0
  49. sempy_labs/report/__init__.py +35 -0
  50. sempy_labs/report/_generate_report.py +253 -0
  51. sempy_labs/report/_report_functions.py +855 -0
  52. sempy_labs/report/_report_rebind.py +131 -0
@@ -0,0 +1,341 @@
1
+ import sempy
2
+ import sempy.fabric as fabric
3
+ import pandas as pd
4
+ from sempy_labs._helper_functions import format_dax_object_name
5
+ from typing import List, Optional, Union
6
+ from anytree import Node, RenderTree
7
+ from sempy._utils._log import log
8
+
9
+
10
+ def get_measure_dependencies(dataset: str, workspace: Optional[str] = None):
11
+ """
12
+ Shows all dependencies for all measures in a semantic model.
13
+
14
+ Parameters
15
+ ----------
16
+ dataset : str
17
+ Name of the semantic model.
18
+ workspace : str, default=None
19
+ The Fabric workspace name.
20
+ Defaults to None which resolves to the workspace of the attached lakehouse
21
+ or if no lakehouse attached, resolves to the workspace of the notebook.
22
+
23
+ Returns
24
+ -------
25
+ pandas.DataFrame
26
+ Shows all dependencies for all measures in the semantic model.
27
+ """
28
+
29
+ if workspace == None:
30
+ workspace_id = fabric.get_workspace_id()
31
+ workspace = fabric.resolve_workspace_name(workspace_id)
32
+
33
+ dep = fabric.evaluate_dax(
34
+ dataset=dataset,
35
+ workspace=workspace,
36
+ dax_string="""
37
+ SELECT
38
+ [TABLE] AS [Table Name]
39
+ ,[OBJECT] AS [Object Name]
40
+ ,[OBJECT_TYPE] AS [Object Type]
41
+ ,[REFERENCED_TABLE] AS [Referenced Table]
42
+ ,[REFERENCED_OBJECT] AS [Referenced Object]
43
+ ,[REFERENCED_OBJECT_TYPE] AS [Referenced Object Type]
44
+ FROM $SYSTEM.DISCOVER_CALC_DEPENDENCY
45
+ WHERE [OBJECT_TYPE] = 'MEASURE'
46
+ """,
47
+ )
48
+
49
+ dep["Object Type"] = dep["Object Type"].str.capitalize()
50
+ dep["Referenced Object Type"] = dep["Referenced Object Type"].str.capitalize()
51
+
52
+ dep["Full Object Name"] = format_dax_object_name(
53
+ dep["Table Name"], dep["Object Name"]
54
+ )
55
+ dep["Referenced Full Object Name"] = format_dax_object_name(
56
+ dep["Referenced Table"], dep["Referenced Object"]
57
+ )
58
+ dep["Parent Node"] = dep["Object Name"]
59
+
60
+ df = dep
61
+
62
+ df["Done"] = df.apply(
63
+ lambda row: False if row["Referenced Object Type"] == "Measure" else True,
64
+ axis=1,
65
+ )
66
+
67
+ while any(df["Done"] == False):
68
+ for i, r in df.iterrows():
69
+ rObjFull = r["Referenced Full Object Name"]
70
+ rObj = r["Referenced Object"]
71
+ if r["Done"] == False:
72
+ dep_filt = dep[dep["Full Object Name"] == rObjFull]
73
+
74
+ for index, dependency in dep_filt.iterrows():
75
+ d = True
76
+ if dependency[5] == "Measure":
77
+ d = False
78
+ df = pd.concat(
79
+ [
80
+ df,
81
+ pd.DataFrame(
82
+ [
83
+ {
84
+ "Table Name": r["Table Name"],
85
+ "Object Name": r["Object Name"],
86
+ "Object Type": r["Object Type"],
87
+ "Referenced Object": dependency[4],
88
+ "Referenced Table": dependency[3],
89
+ "Referenced Object Type": dependency[5],
90
+ "Done": d,
91
+ "Full Object Name": r["Full Object Name"],
92
+ "Referenced Full Object Name": dependency[
93
+ 7
94
+ ],
95
+ "Parent Node": rObj,
96
+ }
97
+ ]
98
+ ),
99
+ ],
100
+ ignore_index=True,
101
+ )
102
+ else:
103
+ df = pd.concat(
104
+ [
105
+ df,
106
+ pd.DataFrame(
107
+ [
108
+ {
109
+ "Table Name": r["Table Name"],
110
+ "Object Name": r["Object Name"],
111
+ "Object Type": r["Object Type"],
112
+ "Referenced Object": dependency[5],
113
+ "Referenced Table": dependency[4],
114
+ "Referenced Object Type": dependency[6],
115
+ "Done": d,
116
+ "Full Object Name": r["Full Object Name"],
117
+ "Referenced Full Object Name": dependency[
118
+ 7
119
+ ],
120
+ "Parent Node": rObj,
121
+ }
122
+ ]
123
+ ),
124
+ ],
125
+ ignore_index=True,
126
+ )
127
+
128
+ df.loc[i, "Done"] = True
129
+
130
+ df = df.drop(["Done", "Full Object Name", "Referenced Full Object Name"], axis=1)
131
+
132
+ return df
133
+
134
+
135
+ def get_model_calc_dependencies(dataset: str, workspace: Optional[str] = None):
136
+ """
137
+ Shows all dependencies for all objects in a semantic model.
138
+
139
+ Parameters
140
+ ----------
141
+ dataset : str
142
+ Name of the semantic model.
143
+ workspace : str, default=None
144
+ The Fabric workspace name.
145
+ Defaults to None which resolves to the workspace of the attached lakehouse
146
+ or if no lakehouse attached, resolves to the workspace of the notebook.
147
+
148
+ Returns
149
+ -------
150
+ pandas.DataFrame
151
+ Shows all dependencies for all objects in the semantic model.
152
+ """
153
+
154
+ if workspace == None:
155
+ workspace_id = fabric.get_workspace_id()
156
+ workspace = fabric.resolve_workspace_name(workspace_id)
157
+
158
+ dep = fabric.evaluate_dax(
159
+ dataset=dataset,
160
+ workspace=workspace,
161
+ dax_string="""
162
+ SELECT
163
+ [TABLE] AS [Table Name]
164
+ ,[OBJECT] AS [Object Name]
165
+ ,[OBJECT_TYPE] AS [Object Type]
166
+ ,[EXPRESSION] AS [Expression]
167
+ ,[REFERENCED_TABLE] AS [Referenced Table]
168
+ ,[REFERENCED_OBJECT] AS [Referenced Object]
169
+ ,[REFERENCED_OBJECT_TYPE] AS [Referenced Object Type]
170
+ FROM $SYSTEM.DISCOVER_CALC_DEPENDENCY
171
+ """,
172
+ )
173
+
174
+ dep["Object Type"] = dep["Object Type"].str.replace("_", " ").str.title()
175
+ dep["Referenced Object Type"] = (
176
+ dep["Referenced Object Type"].str.replace("_", " ").str.title()
177
+ )
178
+
179
+ dep["Full Object Name"] = format_dax_object_name(
180
+ dep["Table Name"], dep["Object Name"]
181
+ )
182
+ dep["Referenced Full Object Name"] = format_dax_object_name(
183
+ dep["Referenced Table"], dep["Referenced Object"]
184
+ )
185
+ dep["Parent Node"] = dep["Object Name"]
186
+
187
+ df = dep
188
+
189
+ objs = ["Measure", "Calc Column", "Calculation Item", "Calc Table"]
190
+
191
+ df["Done"] = df.apply(
192
+ lambda row: False if row["Referenced Object Type"] in objs else True, axis=1
193
+ )
194
+
195
+ while any(df["Done"] == False):
196
+ for i, r in df.iterrows():
197
+ rObjFull = r["Referenced Full Object Name"]
198
+ rObj = r["Referenced Object"]
199
+ if r["Done"] == False:
200
+ dep_filt = dep[dep["Full Object Name"] == rObjFull]
201
+
202
+ for index, dependency in dep_filt.iterrows():
203
+ d = True
204
+ if dependency[5] in objs:
205
+ d = False
206
+ df = pd.concat(
207
+ [
208
+ df,
209
+ pd.DataFrame(
210
+ [
211
+ {
212
+ "Table Name": r["Table Name"],
213
+ "Object Name": r["Object Name"],
214
+ "Object Type": r["Object Type"],
215
+ "Referenced Object": dependency[4],
216
+ "Referenced Table": dependency[3],
217
+ "Referenced Object Type": dependency[5],
218
+ "Done": d,
219
+ "Full Object Name": r["Full Object Name"],
220
+ "Referenced Full Object Name": dependency[
221
+ 7
222
+ ],
223
+ "Parent Node": rObj,
224
+ }
225
+ ]
226
+ ),
227
+ ],
228
+ ignore_index=True,
229
+ )
230
+ else:
231
+ df = pd.concat(
232
+ [
233
+ df,
234
+ pd.DataFrame(
235
+ [
236
+ {
237
+ "Table Name": r["Table Name"],
238
+ "Object Name": r["Object Name"],
239
+ "Object Type": r["Object Type"],
240
+ "Referenced Object": dependency[5],
241
+ "Referenced Table": dependency[4],
242
+ "Referenced Object Type": dependency[6],
243
+ "Done": d,
244
+ "Full Object Name": r["Full Object Name"],
245
+ "Referenced Full Object Name": dependency[
246
+ 7
247
+ ],
248
+ "Parent Node": rObj,
249
+ }
250
+ ]
251
+ ),
252
+ ],
253
+ ignore_index=True,
254
+ )
255
+
256
+ df.loc[i, "Done"] = True
257
+
258
+ df = df.drop(["Done"], axis=1)
259
+
260
+ return df
261
+
262
+
263
+ @log
264
+ def measure_dependency_tree(
265
+ dataset: str, measure_name: str, workspace: Optional[str] = None
266
+ ):
267
+ """
268
+ Prints a measure dependency tree of all dependent objects for a measure in a semantic model.
269
+
270
+ Parameters
271
+ ----------
272
+ dataset : str
273
+ Name of the semantic model.
274
+ measure_name : str
275
+ Name of the measure.
276
+ workspace : str, default=None
277
+ The Fabric workspace name.
278
+ Defaults to None which resolves to the workspace of the attached lakehouse
279
+ or if no lakehouse attached, resolves to the workspace of the notebook.
280
+
281
+ Returns
282
+ -------
283
+
284
+ """
285
+
286
+ if workspace == None:
287
+ workspace_id = fabric.get_workspace_id()
288
+ workspace = fabric.resolve_workspace_name(workspace_id)
289
+
290
+ dfM = fabric.list_measures(dataset=dataset, workspace=workspace)
291
+ dfM_filt = dfM[dfM["Measure Name"] == measure_name]
292
+
293
+ if len(dfM_filt) == 0:
294
+ print(
295
+ f"The '{measure_name}' measure does not exist in the '{dataset}' semantic model in the '{workspace}' workspace."
296
+ )
297
+ return
298
+
299
+ md = get_measure_dependencies(dataset, workspace)
300
+ df_filt = md[md["Object Name"] == measure_name]
301
+
302
+ # Create a dictionary to hold references to nodes
303
+ node_dict = {}
304
+ measureIcon = "\u2211"
305
+ tableIcon = "\u229E"
306
+ columnIcon = "\u229F"
307
+
308
+ # Populate the tree
309
+ for _, row in df_filt.iterrows():
310
+ # measure_name = row['Object Name']
311
+ ref_obj_table_name = row["Referenced Table"]
312
+ ref_obj_name = row["Referenced Object"]
313
+ ref_obj_type = row["Referenced Object Type"]
314
+ parent_node_name = row["Parent Node"]
315
+
316
+ # Create or get the parent node
317
+ parent_node = node_dict.get(parent_node_name)
318
+ if parent_node is None:
319
+ parent_node = Node(parent_node_name)
320
+ node_dict[parent_node_name] = parent_node
321
+ parent_node.custom_property = measureIcon + " "
322
+
323
+ # Create the child node
324
+ child_node_name = ref_obj_name
325
+ child_node = Node(child_node_name, parent=parent_node)
326
+ if ref_obj_type == "Column":
327
+ child_node.custom_property = columnIcon + " '" + ref_obj_table_name + "'"
328
+ elif ref_obj_type == "Table":
329
+ child_node.custom_property = tableIcon + " "
330
+ elif ref_obj_type == "Measure":
331
+ child_node.custom_property = measureIcon + " "
332
+
333
+ # Update the dictionary with the child node
334
+ node_dict[child_node_name] = child_node
335
+
336
+ # Visualize the tree structure using RenderTree
337
+ for pre, _, node in RenderTree(node_dict[measure_name]):
338
+ if tableIcon in node.custom_property:
339
+ print(f"{pre}{node.custom_property}'{node.name}'")
340
+ else:
341
+ print(f"{pre}{node.custom_property}[{node.name}]")
@@ -0,0 +1,155 @@
1
+ import sempy.fabric as fabric
2
+ import pandas as pd
3
+ from typing import Optional
4
+ from sempy._utils._log import log
5
+ from sempy_labs._helper_functions import resolve_workspace_name_and_id
6
+
7
+
8
+ @log
9
+ def export_model_to_onelake(
10
+ dataset: str,
11
+ workspace: Optional[str] = None,
12
+ destination_lakehouse: Optional[str] = None,
13
+ destination_workspace: Optional[str] = None,
14
+ ):
15
+ """
16
+ Exports a semantic model's tables to delta tables in the lakehouse. Creates shortcuts to the tables if a lakehouse is specified.
17
+
18
+ Parameters
19
+ ----------
20
+ dataset : str
21
+ Name of the semantic model.
22
+ workspace : str, default=None
23
+ The Fabric workspace name.
24
+ Defaults to None which resolves to the workspace of the attached lakehouse
25
+ or if no lakehouse attached, resolves to the workspace of the notebook.
26
+ destination_lakehouse : str, default=None
27
+ The name of the Fabric lakehouse where shortcuts will be created to access the delta tables created by the export. If the lakehouse specified does not exist, one will be created with that name. If no lakehouse is specified, shortcuts will not be created.
28
+ destination_workspace : str, default=None
29
+ The name of the Fabric workspace in which the lakehouse resides.
30
+ """
31
+
32
+ (workspace, workspace_id) = resolve_workspace_name_and_id(workspace)
33
+
34
+ if destination_workspace == None:
35
+ destination_workspace = workspace
36
+ destination_workspace_id = workspace_id
37
+ else:
38
+ destination_workspace_id = fabric.resolve_workspace_id(destination_workspace)
39
+
40
+ dfD = fabric.list_datasets(workspace=workspace)
41
+ dfD_filt = dfD[dfD["Dataset Name"] == dataset]
42
+
43
+ if len(dfD_filt) == 0:
44
+ print(
45
+ f"The '{dataset}' semantic model does not exist in the '{workspace}' workspace."
46
+ )
47
+ return
48
+
49
+ tmsl = f"""
50
+ {{
51
+ 'export': {{
52
+ 'layout': 'delta',
53
+ 'type': 'full',
54
+ 'objects': [
55
+ {{
56
+ 'database': '{dataset}'
57
+ }}
58
+ ]
59
+ }}
60
+ }}
61
+ """
62
+
63
+ # Export model's tables as delta tables
64
+ try:
65
+ fabric.execute_tmsl(script=tmsl, workspace=workspace)
66
+ print(
67
+ f"The '{dataset}' semantic model's tables have been exported as delta tables to the '{workspace}' workspace.\n"
68
+ )
69
+ except:
70
+ print(
71
+ f"ERROR: The '{dataset}' semantic model's tables have not been exported as delta tables to the '{workspace}' workspace."
72
+ )
73
+ print(
74
+ f"Make sure you enable OneLake integration for the '{dataset}' semantic model. Follow the instructions here: https://learn.microsoft.com/power-bi/enterprise/onelake-integration-overview#enable-onelake-integration"
75
+ )
76
+ return
77
+
78
+ # Create shortcuts if destination lakehouse is specified
79
+ if destination_lakehouse is not None:
80
+ # Destination...
81
+ dfI_Dest = fabric.list_items(workspace=destination_workspace, type="Lakehouse")
82
+ dfI_filt = dfI_Dest[(dfI_Dest["Display Name"] == destination_lakehouse)]
83
+
84
+ if len(dfI_filt) == 0:
85
+ print(
86
+ f"The '{destination_lakehouse}' lakehouse does not exist within the '{destination_workspace}' workspace."
87
+ )
88
+ # Create lakehouse
89
+ destination_lakehouse_id = fabric.create_lakehouse(
90
+ display_name=destination_lakehouse, workspace=destination_workspace
91
+ )
92
+ print(
93
+ f"The '{destination_lakehouse}' lakehouse has been created within the '{destination_workspace}' workspace.\n"
94
+ )
95
+ else:
96
+ destination_lakehouse_id = dfI_filt["Id"].iloc[0]
97
+
98
+ # Source...
99
+ dfI_Source = fabric.list_items(workspace=workspace, type="SemanticModel")
100
+ dfI_filtSource = dfI_Source[(dfI_Source["Display Name"] == dataset)]
101
+ sourceLakehouseId = dfI_filtSource["Id"].iloc[0]
102
+
103
+ # Valid tables
104
+ dfP = fabric.list_partitions(
105
+ dataset=dataset,
106
+ workspace=workspace,
107
+ additional_xmla_properties=["Parent.SystemManaged"],
108
+ )
109
+ dfP_filt = dfP[
110
+ (dfP["Mode"] == "Import")
111
+ & (dfP["Source Type"] != "CalculationGroup")
112
+ & (dfP["Parent System Managed"] == False)
113
+ ]
114
+ dfC = fabric.list_columns(dataset=dataset, workspace=workspace)
115
+ tmc = pd.DataFrame(dfP.groupby("Table Name")["Mode"].nunique()).reset_index()
116
+ oneMode = tmc[tmc["Mode"] == 1]
117
+ tableAll = dfP_filt[
118
+ dfP_filt["Table Name"].isin(dfC["Table Name"].values)
119
+ & (dfP_filt["Table Name"].isin(oneMode["Table Name"].values))
120
+ ]
121
+ tables = tableAll["Table Name"].unique()
122
+
123
+ client = fabric.FabricRestClient()
124
+
125
+ print("Creating shortcuts...\n")
126
+ for tableName in tables:
127
+ tablePath = "Tables/" + tableName
128
+ shortcutName = tableName.replace(" ", "")
129
+ request_body = {
130
+ "path": "Tables",
131
+ "name": shortcutName,
132
+ "target": {
133
+ "oneLake": {
134
+ "workspaceId": workspace_id,
135
+ "itemId": sourceLakehouseId,
136
+ "path": tablePath,
137
+ }
138
+ },
139
+ }
140
+
141
+ try:
142
+ response = client.post(
143
+ f"/v1/workspaces/{destination_workspace_id}/items/{destination_lakehouse_id}/shortcuts",
144
+ json=request_body,
145
+ )
146
+ if response.status_code == 201:
147
+ print(
148
+ f"\u2022 The shortcut '{shortcutName}' was created in the '{destination_lakehouse}' lakehouse within the '{destination_workspace}' workspace. It is based on the '{tableName}' table in the '{dataset}' semantic model within the '{workspace}' workspace.\n"
149
+ )
150
+ else:
151
+ print(response.status_code)
152
+ except:
153
+ print(
154
+ f"ERROR: Failed to create a shortcut for the '{tableName}' table."
155
+ )