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.
- semantic_link_labs-0.4.1.dist-info/LICENSE +21 -0
- semantic_link_labs-0.4.1.dist-info/METADATA +22 -0
- semantic_link_labs-0.4.1.dist-info/RECORD +52 -0
- semantic_link_labs-0.4.1.dist-info/WHEEL +5 -0
- semantic_link_labs-0.4.1.dist-info/top_level.txt +1 -0
- sempy_labs/__init__.py +154 -0
- sempy_labs/_ai.py +496 -0
- sempy_labs/_clear_cache.py +39 -0
- sempy_labs/_connections.py +234 -0
- sempy_labs/_dax.py +70 -0
- sempy_labs/_generate_semantic_model.py +280 -0
- sempy_labs/_helper_functions.py +506 -0
- sempy_labs/_icons.py +4 -0
- sempy_labs/_list_functions.py +1372 -0
- sempy_labs/_model_auto_build.py +143 -0
- sempy_labs/_model_bpa.py +1354 -0
- sempy_labs/_model_dependencies.py +341 -0
- sempy_labs/_one_lake_integration.py +155 -0
- sempy_labs/_query_scale_out.py +447 -0
- sempy_labs/_refresh_semantic_model.py +184 -0
- sempy_labs/_tom.py +3766 -0
- sempy_labs/_translations.py +378 -0
- sempy_labs/_vertipaq.py +893 -0
- sempy_labs/directlake/__init__.py +45 -0
- sempy_labs/directlake/_directlake_schema_compare.py +110 -0
- sempy_labs/directlake/_directlake_schema_sync.py +128 -0
- sempy_labs/directlake/_fallback.py +62 -0
- sempy_labs/directlake/_get_directlake_lakehouse.py +69 -0
- sempy_labs/directlake/_get_shared_expression.py +59 -0
- sempy_labs/directlake/_guardrails.py +84 -0
- sempy_labs/directlake/_list_directlake_model_calc_tables.py +54 -0
- sempy_labs/directlake/_show_unsupported_directlake_objects.py +89 -0
- sempy_labs/directlake/_update_directlake_model_lakehouse_connection.py +81 -0
- sempy_labs/directlake/_update_directlake_partition_entity.py +64 -0
- sempy_labs/directlake/_warm_cache.py +210 -0
- sempy_labs/lakehouse/__init__.py +24 -0
- sempy_labs/lakehouse/_get_lakehouse_columns.py +81 -0
- sempy_labs/lakehouse/_get_lakehouse_tables.py +250 -0
- sempy_labs/lakehouse/_lakehouse.py +85 -0
- sempy_labs/lakehouse/_shortcuts.py +296 -0
- sempy_labs/migration/__init__.py +29 -0
- sempy_labs/migration/_create_pqt_file.py +239 -0
- sempy_labs/migration/_migrate_calctables_to_lakehouse.py +429 -0
- sempy_labs/migration/_migrate_calctables_to_semantic_model.py +150 -0
- sempy_labs/migration/_migrate_model_objects_to_semantic_model.py +524 -0
- sempy_labs/migration/_migrate_tables_columns_to_semantic_model.py +165 -0
- sempy_labs/migration/_migration_validation.py +227 -0
- sempy_labs/migration/_refresh_calc_tables.py +129 -0
- sempy_labs/report/__init__.py +35 -0
- sempy_labs/report/_generate_report.py +253 -0
- sempy_labs/report/_report_functions.py +855 -0
- sempy_labs/report/_report_rebind.py +131 -0
sempy_labs/_ai.py
ADDED
|
@@ -0,0 +1,496 @@
|
|
|
1
|
+
import sempy
|
|
2
|
+
import sempy.fabric as fabric
|
|
3
|
+
import pandas as pd
|
|
4
|
+
from synapse.ml.services.openai import OpenAICompletion
|
|
5
|
+
from pyspark.sql.functions import col
|
|
6
|
+
from pyspark.sql import SparkSession
|
|
7
|
+
from typing import List, Optional, Union
|
|
8
|
+
from IPython.display import display
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def optimize_semantic_model(dataset: str, workspace: Optional[str] = None):
|
|
12
|
+
|
|
13
|
+
from ._model_bpa import run_model_bpa
|
|
14
|
+
from .directlake._fallback import check_fallback_reason
|
|
15
|
+
from ._helper_functions import format_dax_object_name
|
|
16
|
+
|
|
17
|
+
modelBPA = run_model_bpa(
|
|
18
|
+
dataset=dataset, workspace=workspace, return_dataframe=True
|
|
19
|
+
)
|
|
20
|
+
dfC = fabric.list_columns(dataset=dataset, workspace=workspace, extended=True)
|
|
21
|
+
dfC["Column Object"] = format_dax_object_name(dfC["Table Name"], dfC["Column Name"])
|
|
22
|
+
dfC["Total Size"] = dfC["Total Size"].astype("int")
|
|
23
|
+
dfP = fabric.list_partitions(dataset=dataset, workspace=workspace)
|
|
24
|
+
|
|
25
|
+
modelBPA_col = modelBPA[modelBPA["Object Type"] == "Column"]
|
|
26
|
+
modelBPA_col = pd.merge(
|
|
27
|
+
modelBPA_col,
|
|
28
|
+
dfC[["Column Object", "Total Size"]],
|
|
29
|
+
left_on="Object Name",
|
|
30
|
+
right_on="Column Object",
|
|
31
|
+
how="left",
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
isDirectLake = any(r["Mode"] == "DirectLake" for i, r in dfP.iterrows())
|
|
35
|
+
|
|
36
|
+
if isDirectLake:
|
|
37
|
+
fallback = check_fallback_reason(dataset=dataset, workspace=workspace)
|
|
38
|
+
fallback_filt = fallback[fallback["FallbackReasonID"] == 2]
|
|
39
|
+
|
|
40
|
+
if len(fallback_filt) > 0:
|
|
41
|
+
print(
|
|
42
|
+
f"The '{dataset}' semantic model is a Direct Lake semantic model which contains views. Since views always fall back to DirectQuery, it is recommended to only use lakehouse tables and not views."
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
# Potential model reduction estimate
|
|
46
|
+
ruleNames = [
|
|
47
|
+
"Remove unnecessary columns",
|
|
48
|
+
"Set IsAvailableInMdx to false on non-attribute columns",
|
|
49
|
+
]
|
|
50
|
+
|
|
51
|
+
for rule in ruleNames:
|
|
52
|
+
df = modelBPA_col[modelBPA_col["Rule Name"] == rule]
|
|
53
|
+
df_filt = df[["Object Name", "Total Size"]].sort_values(
|
|
54
|
+
by="Total Size", ascending=False
|
|
55
|
+
)
|
|
56
|
+
totSize = df["Total Size"].sum()
|
|
57
|
+
if len(df_filt) > 0:
|
|
58
|
+
print(
|
|
59
|
+
f"Potential savings of {totSize} bytes from following the '{rule}' rule."
|
|
60
|
+
)
|
|
61
|
+
display(df_filt)
|
|
62
|
+
else:
|
|
63
|
+
print(f"The '{rule}' rule has been followed.")
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def generate_measure_descriptions(
|
|
67
|
+
dataset: str,
|
|
68
|
+
measures: Union[str, List[str]],
|
|
69
|
+
gpt_model: Optional[str] = "gpt-35-turbo",
|
|
70
|
+
workspace: Optional[str] = None,
|
|
71
|
+
):
|
|
72
|
+
|
|
73
|
+
service_name = "synapseml-openai"
|
|
74
|
+
|
|
75
|
+
if isinstance(measures, str):
|
|
76
|
+
measures = [measures]
|
|
77
|
+
|
|
78
|
+
validModels = ["gpt-35-turbo", "gpt-35-turbo-16k", "gpt-4"]
|
|
79
|
+
if gpt_model not in validModels:
|
|
80
|
+
print(
|
|
81
|
+
f"The '{gpt_model}' model is not a valid model. Enter a gpt_model from this list: {validModels}."
|
|
82
|
+
)
|
|
83
|
+
return
|
|
84
|
+
|
|
85
|
+
dfM = fabric.list_measures(dataset=dataset, workspace=workspace)
|
|
86
|
+
|
|
87
|
+
if measures is not None:
|
|
88
|
+
dfM_filt = dfM[dfM["Measure Name"].isin(measures)]
|
|
89
|
+
else:
|
|
90
|
+
dfM_filt = dfM
|
|
91
|
+
|
|
92
|
+
df = dfM_filt[["Table Name", "Measure Name", "Measure Expression"]]
|
|
93
|
+
|
|
94
|
+
df["prompt"] = (
|
|
95
|
+
f"The following is DAX code used by Microsoft Power BI. Please explain this code in simple terms:"
|
|
96
|
+
+ df["Measure Expression"]
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
# Generate new column in df dataframe which has the AI-generated descriptions
|
|
100
|
+
completion = {
|
|
101
|
+
OpenAICompletion()
|
|
102
|
+
.setDeploymentName(gpt_model)
|
|
103
|
+
.setMaxTokens(200)
|
|
104
|
+
.setCustomServiceName(service_name)
|
|
105
|
+
.setPromptCol("prompt")
|
|
106
|
+
.setErrorCol("error")
|
|
107
|
+
.setOutputCol("completions")
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
completed_df = completion.transform(df).cache()
|
|
111
|
+
completed_df.select(
|
|
112
|
+
col("prompt"),
|
|
113
|
+
col("error"),
|
|
114
|
+
col("completions.choices.text").getItem(0).alias("text"),
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
# Update the model to use the new descriptions
|
|
118
|
+
tom_server = fabric.create_tom_server(readonly=False, workspace=workspace)
|
|
119
|
+
m = tom_server.Databases.GetByName(dataset).Model
|
|
120
|
+
|
|
121
|
+
# for t in m.Tables:
|
|
122
|
+
# tName = t.Name
|
|
123
|
+
# for ms in t.Measures:
|
|
124
|
+
# mName = ms.Name
|
|
125
|
+
# mDesc = promptValue
|
|
126
|
+
|
|
127
|
+
# m.SaveChanges()
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def generate_aggs(
|
|
131
|
+
dataset: str,
|
|
132
|
+
table_name: str,
|
|
133
|
+
columns: Union[str, List[str]],
|
|
134
|
+
workspace: Optional[str] = None,
|
|
135
|
+
lakehouse_workspace: Optional[str] = None,
|
|
136
|
+
):
|
|
137
|
+
|
|
138
|
+
from ._helper_functions import (
|
|
139
|
+
get_direct_lake_sql_endpoint,
|
|
140
|
+
create_abfss_path,
|
|
141
|
+
format_dax_object_name,
|
|
142
|
+
resolve_lakehouse_id,
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
sempy.fabric._client._utils._init_analysis_services()
|
|
146
|
+
import Microsoft.AnalysisServices.Tabular as TOM
|
|
147
|
+
import System
|
|
148
|
+
|
|
149
|
+
# columns = {
|
|
150
|
+
#'SalesAmount': 'Sum',
|
|
151
|
+
#'ProductKey': 'GroupBy',
|
|
152
|
+
#'OrderDateKey': 'GroupBy'
|
|
153
|
+
# }
|
|
154
|
+
|
|
155
|
+
if workspace == None:
|
|
156
|
+
workspace_id = fabric.get_workspace_id()
|
|
157
|
+
workspace = fabric.resolve_workspace_name(workspace_id)
|
|
158
|
+
|
|
159
|
+
if lakehouse_workspace == None:
|
|
160
|
+
lakehouse_workspace = workspace
|
|
161
|
+
lakehouse_workspace_id = workspace_id
|
|
162
|
+
else:
|
|
163
|
+
lakehouse_workspace_id = fabric.resolve_workspace_id(lakehouse_workspace)
|
|
164
|
+
|
|
165
|
+
if isinstance(columns, str):
|
|
166
|
+
columns = [columns]
|
|
167
|
+
|
|
168
|
+
columnValues = columns.keys()
|
|
169
|
+
|
|
170
|
+
aggTypes = ["Sum", "Count", "Min", "Max", "GroupBy"]
|
|
171
|
+
aggTypesAggregate = ["Sum", "Count", "Min", "Max"]
|
|
172
|
+
numericTypes = ["Int64", "Double", "Decimal"]
|
|
173
|
+
|
|
174
|
+
if any(value not in aggTypes for value in columns.values()):
|
|
175
|
+
print(
|
|
176
|
+
f"Invalid aggregation type(s) have been specified in the 'columns' parameter. Valid aggregation types: {aggTypes}."
|
|
177
|
+
)
|
|
178
|
+
return
|
|
179
|
+
|
|
180
|
+
dfC = fabric.list_columns(dataset=dataset, workspace=workspace)
|
|
181
|
+
dfP = fabric.list_partitions(dataset=dataset, workspace=workspace)
|
|
182
|
+
dfM = fabric.list_measures(dataset=dataset, workspace=workspace)
|
|
183
|
+
dfR = fabric.list_relationships(dataset=dataset, workspace=workspace)
|
|
184
|
+
if not any(r["Mode"] == "DirectLake" for i, r in dfP.iterrows()):
|
|
185
|
+
print(
|
|
186
|
+
f"The '{dataset}' semantic model within the '{workspace}' workspace is not in Direct Lake mode. This function is only relevant for Direct Lake semantic models."
|
|
187
|
+
)
|
|
188
|
+
return
|
|
189
|
+
|
|
190
|
+
dfC_filtT = dfC[dfC["Table Name"] == table_name]
|
|
191
|
+
|
|
192
|
+
if len(dfC_filtT) == 0:
|
|
193
|
+
print(
|
|
194
|
+
f"The '{table_name}' table does not exist in the '{dataset}' semantic model within the '{workspace}' workspace."
|
|
195
|
+
)
|
|
196
|
+
return
|
|
197
|
+
|
|
198
|
+
dfC_filt = dfC[
|
|
199
|
+
(dfC["Table Name"] == table_name) & (dfC["Column Name"].isin(columnValues))
|
|
200
|
+
]
|
|
201
|
+
|
|
202
|
+
if len(columns) != len(dfC_filt):
|
|
203
|
+
print(
|
|
204
|
+
f"Columns listed in '{columnValues}' do not exist in the '{table_name}' table in the '{dataset}' semantic model within the '{workspace}' workspace."
|
|
205
|
+
)
|
|
206
|
+
return
|
|
207
|
+
|
|
208
|
+
# Check if doing sum/count/min/max etc. on a non-number column
|
|
209
|
+
for col, agg in columns.items():
|
|
210
|
+
dfC_col = dfC_filt[dfC_filt["Column Name"] == col]
|
|
211
|
+
dataType = dfC_col["Data Type"].iloc[0]
|
|
212
|
+
if agg in aggTypesAggregate and dataType not in numericTypes:
|
|
213
|
+
print(
|
|
214
|
+
f"The '{col}' column in the '{table_name}' table is of '{dataType}' data type. Only columns of '{numericTypes}' data types can be aggregated as '{aggTypesAggregate}' aggregation types."
|
|
215
|
+
)
|
|
216
|
+
return
|
|
217
|
+
|
|
218
|
+
# Create/update lakehouse delta agg table
|
|
219
|
+
aggSuffix = "_agg"
|
|
220
|
+
aggTableName = f"{table_name}{aggSuffix}"
|
|
221
|
+
aggLakeTName = aggTableName.lower().replace(" ", "_")
|
|
222
|
+
dfP = fabric.list_partitions(dataset=dataset, workspace=workspace)
|
|
223
|
+
dfP_filt = dfP[dfP["Table Name"] == table_name]
|
|
224
|
+
lakeTName = dfP_filt["Query"].iloc[0]
|
|
225
|
+
|
|
226
|
+
sqlEndpointId = get_direct_lake_sql_endpoint(dataset=dataset, workspace=workspace)
|
|
227
|
+
|
|
228
|
+
dfI = fabric.list_items(workspace=lakehouse_workspace, type="SQLEndpoint")
|
|
229
|
+
dfI_filt = dfI[(dfI["Id"] == sqlEndpointId)]
|
|
230
|
+
|
|
231
|
+
if len(dfI_filt) == 0:
|
|
232
|
+
print(
|
|
233
|
+
f"The lakehouse (SQL Endpoint) used by the '{dataset}' semantic model does not reside in the '{lakehouse_workspace}' workspace. Please update the lakehouse_workspace parameter."
|
|
234
|
+
)
|
|
235
|
+
return
|
|
236
|
+
|
|
237
|
+
lakehouseName = dfI_filt["Display Name"].iloc[0]
|
|
238
|
+
lakehouse_id = resolve_lakehouse_id(
|
|
239
|
+
lakehouse=lakehouseName, workspace=lakehouse_workspace
|
|
240
|
+
)
|
|
241
|
+
|
|
242
|
+
# Generate SQL query
|
|
243
|
+
query = "SELECT"
|
|
244
|
+
groupBy = "\nGROUP BY"
|
|
245
|
+
for col, agg in columns.items():
|
|
246
|
+
colFilt = dfC_filt[dfC_filt["Column Name"] == col]
|
|
247
|
+
sourceCol = colFilt["Source"].iloc[0]
|
|
248
|
+
|
|
249
|
+
if agg == "GroupBy":
|
|
250
|
+
query = f"{query}\n{sourceCol},"
|
|
251
|
+
groupBy = f"{groupBy}\n{sourceCol},"
|
|
252
|
+
else:
|
|
253
|
+
query = f"{query}\n{agg}({sourceCol}) AS {sourceCol},"
|
|
254
|
+
|
|
255
|
+
query = query[:-1]
|
|
256
|
+
|
|
257
|
+
spark = SparkSession.builder.getOrCreate()
|
|
258
|
+
fromTablePath = create_abfss_path(
|
|
259
|
+
lakehouse_id=lakehouse_id,
|
|
260
|
+
lakehouse_workspace_id=lakehouse_workspace_id,
|
|
261
|
+
delta_table_name=lakeTName,
|
|
262
|
+
)
|
|
263
|
+
df = spark.read.format("delta").load(fromTablePath)
|
|
264
|
+
tempTableName = "delta_table_" + lakeTName
|
|
265
|
+
df.createOrReplaceTempView(tempTableName)
|
|
266
|
+
sqlQuery = f"{query} \n FROM {tempTableName} {groupBy}"
|
|
267
|
+
|
|
268
|
+
sqlQuery = sqlQuery[:-1]
|
|
269
|
+
print(sqlQuery)
|
|
270
|
+
|
|
271
|
+
# Save query to spark dataframe
|
|
272
|
+
spark_df = spark.sql(sqlQuery)
|
|
273
|
+
f"\nCreating/updating the '{aggLakeTName}' table in the lakehouse..."
|
|
274
|
+
# Write spark dataframe to delta table
|
|
275
|
+
aggFilePath = create_abfss_path(
|
|
276
|
+
lakehouse_id=lakehouse_id,
|
|
277
|
+
lakehouse_workspace_id=lakehouse_workspace_id,
|
|
278
|
+
delta_table_name=aggLakeTName,
|
|
279
|
+
)
|
|
280
|
+
spark_df.write.mode("overwrite").format("delta").save(aggFilePath)
|
|
281
|
+
f"The '{aggLakeTName}' table has been created/updated in the lakehouse."
|
|
282
|
+
|
|
283
|
+
# Create/update semantic model agg table
|
|
284
|
+
tom_server = fabric.create_tom_server(readonly=False, workspace=workspace)
|
|
285
|
+
m = tom_server.Databases.GetByName(dataset).Model
|
|
286
|
+
f"\nUpdating the '{dataset}' semantic model..."
|
|
287
|
+
dfC_agg = dfC[dfC["Table Name"] == aggTableName]
|
|
288
|
+
|
|
289
|
+
if len(dfC_agg) == 0:
|
|
290
|
+
print(f"Creating the '{aggTableName}' table...")
|
|
291
|
+
exp = m.Expressions["DatabaseQuery"]
|
|
292
|
+
tbl = TOM.Table()
|
|
293
|
+
tbl.Name = aggTableName
|
|
294
|
+
tbl.IsHidden = True
|
|
295
|
+
|
|
296
|
+
ep = TOM.EntityPartitionSource()
|
|
297
|
+
ep.Name = aggTableName
|
|
298
|
+
ep.EntityName = aggLakeTName
|
|
299
|
+
ep.ExpressionSource = exp
|
|
300
|
+
|
|
301
|
+
part = TOM.Partition()
|
|
302
|
+
part.Name = aggTableName
|
|
303
|
+
part.Source = ep
|
|
304
|
+
part.Mode = TOM.ModeType.DirectLake
|
|
305
|
+
|
|
306
|
+
tbl.Partitions.Add(part)
|
|
307
|
+
|
|
308
|
+
for i, r in dfC_filt.iterrows():
|
|
309
|
+
scName = r["Source"]
|
|
310
|
+
cName = r["Column Name"]
|
|
311
|
+
dType = r["Data Type"]
|
|
312
|
+
|
|
313
|
+
col = TOM.DataColumn()
|
|
314
|
+
col.Name = cName
|
|
315
|
+
col.IsHidden = True
|
|
316
|
+
col.SourceColumn = scName
|
|
317
|
+
col.DataType = System.Enum.Parse(TOM.DataType, dType)
|
|
318
|
+
|
|
319
|
+
tbl.Columns.Add(col)
|
|
320
|
+
print(
|
|
321
|
+
f"The '{aggTableName}'[{cName}] column has been added to the '{dataset}' semantic model."
|
|
322
|
+
)
|
|
323
|
+
|
|
324
|
+
m.Tables.Add(tbl)
|
|
325
|
+
print(
|
|
326
|
+
f"The '{aggTableName}' table has been added to the '{dataset}' semantic model."
|
|
327
|
+
)
|
|
328
|
+
else:
|
|
329
|
+
print(f"Updating the '{aggTableName}' table's columns...")
|
|
330
|
+
# Remove existing columns
|
|
331
|
+
for t in m.Tables:
|
|
332
|
+
tName = t.Name
|
|
333
|
+
for c in t.Columns:
|
|
334
|
+
cName = c.Name
|
|
335
|
+
if t.Name == aggTableName:
|
|
336
|
+
m.Tables[tName].Columns.Remove(cName)
|
|
337
|
+
# Add columns
|
|
338
|
+
for i, r in dfC_filt.iterrows():
|
|
339
|
+
scName = r["Source"]
|
|
340
|
+
cName = r["Column Name"]
|
|
341
|
+
dType = r["Data Type"]
|
|
342
|
+
|
|
343
|
+
col = TOM.DataColumn()
|
|
344
|
+
col.Name = cName
|
|
345
|
+
col.IsHidden = True
|
|
346
|
+
col.SourceColumn = scName
|
|
347
|
+
col.DataType = System.Enum.Parse(TOM.DataType, dType)
|
|
348
|
+
|
|
349
|
+
m.Tables[aggTableName].Columns.Add(col)
|
|
350
|
+
print(f"The '{aggTableName}'[{cName}] column has been added.")
|
|
351
|
+
|
|
352
|
+
# Create relationships
|
|
353
|
+
relMap = {"m": "Many", "1": "One", "0": "None"}
|
|
354
|
+
|
|
355
|
+
print(f"\nGenerating necessary relationships...")
|
|
356
|
+
for i, r in dfR.iterrows():
|
|
357
|
+
fromTable = r["From Table"]
|
|
358
|
+
fromColumn = r["From Column"]
|
|
359
|
+
toTable = r["To Table"]
|
|
360
|
+
toColumn = r["To Column"]
|
|
361
|
+
cfb = r["Cross Filtering Behavior"]
|
|
362
|
+
sfb = r["Security Filtering Behavior"]
|
|
363
|
+
mult = r["Multiplicity"]
|
|
364
|
+
|
|
365
|
+
crossFB = System.Enum.Parse(TOM.CrossFilteringBehavior, cfb)
|
|
366
|
+
secFB = System.Enum.Parse(TOM.SecurityFilteringBehavior, sfb)
|
|
367
|
+
fromCardinality = System.Enum.Parse(
|
|
368
|
+
TOM.RelationshipEndCardinality, relMap.get(mult[0])
|
|
369
|
+
)
|
|
370
|
+
toCardinality = System.Enum.Parse(
|
|
371
|
+
TOM.RelationshipEndCardinality, relMap.get(mult[-1])
|
|
372
|
+
)
|
|
373
|
+
|
|
374
|
+
rel = TOM.SingleColumnRelationship()
|
|
375
|
+
rel.FromCardinality = fromCardinality
|
|
376
|
+
rel.ToCardinality = toCardinality
|
|
377
|
+
rel.IsActive = r["Active"]
|
|
378
|
+
rel.CrossFilteringBehavior = crossFB
|
|
379
|
+
rel.SecurityFilteringBehavior = secFB
|
|
380
|
+
rel.RelyOnReferentialIntegrity = r["Rely On Referential Integrity"]
|
|
381
|
+
|
|
382
|
+
if fromTable == table_name:
|
|
383
|
+
try:
|
|
384
|
+
rel.FromColumn = m.Tables[aggTableName].Columns[fromColumn]
|
|
385
|
+
m.Relationships.Add(rel)
|
|
386
|
+
print(
|
|
387
|
+
f"'{aggTableName}'[{fromColumn}] -> '{toTable}'[{toColumn}] relationship has been added."
|
|
388
|
+
)
|
|
389
|
+
except:
|
|
390
|
+
print(
|
|
391
|
+
f"'{aggTableName}'[{fromColumn}] -> '{toTable}'[{toColumn}] relationship has not been created."
|
|
392
|
+
)
|
|
393
|
+
elif toTable == table_name:
|
|
394
|
+
try:
|
|
395
|
+
rel.ToColumn = m.Tables[aggTableName].Columns[toColumn]
|
|
396
|
+
m.Relationships.Add(rel)
|
|
397
|
+
print(
|
|
398
|
+
f"'{fromTable}'[{fromColumn}] -> '{aggTableName}'[{toColumn}] relationship has been added."
|
|
399
|
+
)
|
|
400
|
+
except:
|
|
401
|
+
print(
|
|
402
|
+
f"'{fromTable}'[{fromColumn}] -> '{aggTableName}'[{toColumn}] relationship has not been created."
|
|
403
|
+
)
|
|
404
|
+
f"Relationship creation is complete."
|
|
405
|
+
|
|
406
|
+
# Create IF measure
|
|
407
|
+
f"\nCreating measure to check if the agg table can be used..."
|
|
408
|
+
aggChecker = "IF("
|
|
409
|
+
dfR_filt = dfR[
|
|
410
|
+
(dfR["From Table"] == table_name) & (~dfR["From Column"].isin(columnValues))
|
|
411
|
+
]
|
|
412
|
+
|
|
413
|
+
for i, r in dfR_filt.iterrows():
|
|
414
|
+
toTable = r["To Table"]
|
|
415
|
+
aggChecker = f"{aggChecker}\nISCROSSFILTERED('{toTable}') ||"
|
|
416
|
+
|
|
417
|
+
aggChecker = aggChecker[:-3]
|
|
418
|
+
aggChecker = f"{aggChecker},1,0)"
|
|
419
|
+
print(aggChecker)
|
|
420
|
+
|
|
421
|
+
# Todo: add IFISFILTERED clause for columns
|
|
422
|
+
f"\n Creating the base measures in the agg table..."
|
|
423
|
+
# Create base agg measures
|
|
424
|
+
dep = fabric.evaluate_dax(
|
|
425
|
+
dataset=dataset,
|
|
426
|
+
workspace=workspace,
|
|
427
|
+
dax_string="""
|
|
428
|
+
SELECT
|
|
429
|
+
[TABLE] AS [Table Name]
|
|
430
|
+
,[OBJECT] AS [Object Name]
|
|
431
|
+
,[OBJECT_TYPE] AS [Object Type]
|
|
432
|
+
,[REFERENCED_TABLE] AS [Referenced Table]
|
|
433
|
+
,[REFERENCED_OBJECT] AS [Referenced Object]
|
|
434
|
+
,[REFERENCED_OBJECT_TYPE] AS [Referenced Object Type]
|
|
435
|
+
FROM $SYSTEM.DISCOVER_CALC_DEPENDENCY
|
|
436
|
+
WHERE [OBJECT_TYPE] = 'MEASURE'
|
|
437
|
+
""",
|
|
438
|
+
)
|
|
439
|
+
|
|
440
|
+
baseMeasures = dep[
|
|
441
|
+
(dep["Referenced Object Type"] == "COLUMN")
|
|
442
|
+
& (dep["Referenced Table"] == table_name)
|
|
443
|
+
& (dep["Referenced Object"].isin(columnValues))
|
|
444
|
+
]
|
|
445
|
+
for i, r in baseMeasures.iterrows():
|
|
446
|
+
tName = r["Table Name"]
|
|
447
|
+
mName = r["Object Name"]
|
|
448
|
+
cName = r["Referenced Object"]
|
|
449
|
+
dfM_filt = dfM[dfM["Measure Name"] == mName]
|
|
450
|
+
expr = dfM_filt["Measure Expression"].iloc[0]
|
|
451
|
+
|
|
452
|
+
colFQNonAgg = format_dax_object_name(tName, cName)
|
|
453
|
+
colFQAgg = format_dax_object_name(aggTableName, cName)
|
|
454
|
+
colNQNonAgg = f"{tName}[{cName}]"
|
|
455
|
+
|
|
456
|
+
if " " in tName:
|
|
457
|
+
newExpr = expr.replace(colFQNonAgg, colFQAgg)
|
|
458
|
+
else:
|
|
459
|
+
newExpr = expr.replace(colFQNonAgg, colFQAgg).replace(colNQNonAgg, colFQAgg)
|
|
460
|
+
print(expr)
|
|
461
|
+
print(newExpr)
|
|
462
|
+
|
|
463
|
+
aggMName = mName + aggSuffix
|
|
464
|
+
measure = TOM.Measure()
|
|
465
|
+
measure.Name = aggMName
|
|
466
|
+
measure.IsHidden = True
|
|
467
|
+
measure.Expression = newExpr
|
|
468
|
+
m.Tables[aggTableName].Measures.Add(measure)
|
|
469
|
+
f"The '{aggMName}' measure has been created in the '{aggTableName}' table."
|
|
470
|
+
|
|
471
|
+
# Update base detail measures
|
|
472
|
+
|
|
473
|
+
# m.SaveChanges()
|
|
474
|
+
|
|
475
|
+
|
|
476
|
+
# Identify views used within Direct Lake model
|
|
477
|
+
# workspace = 'MK Demo 6'
|
|
478
|
+
# lakehouse = 'MyLakehouse'
|
|
479
|
+
# dataset = 'MigrationTest'
|
|
480
|
+
# lakehouse_workspace = workspace
|
|
481
|
+
|
|
482
|
+
# dfView = pd.DataFrame(columns=['Workspace Name', 'Lakehouse Name', 'View Name'])
|
|
483
|
+
# dfP = fabric.list_partitions(dataset = dataset, workspace = workspace)
|
|
484
|
+
# isDirectLake = any(r['Mode'] == 'DirectLake' for i, r in dfP.iterrows())
|
|
485
|
+
|
|
486
|
+
# spark = SparkSession.builder.getOrCreate()
|
|
487
|
+
# views = spark.sql(f"SHOW VIEWS IN {lakehouse}").collect()
|
|
488
|
+
# for view in views:
|
|
489
|
+
# viewName = view['viewName']
|
|
490
|
+
# isTemporary = view['isTemporary']
|
|
491
|
+
# new_data = {'Workspace Name': workspace, 'Lakehouse Name': lakehouse, 'View Name': viewName}
|
|
492
|
+
# dfView = pd.concat([dfView, pd.DataFrame(new_data, index=[0])], ignore_index=True)
|
|
493
|
+
# dfView
|
|
494
|
+
# lakeT = get_lakehouse_tables(lakehouse, lakehouse_workspace)
|
|
495
|
+
# if not dfP['Query'].isin(lakeT['Table Name'].values):
|
|
496
|
+
# if
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import sempy
|
|
2
|
+
import sempy.fabric as fabric
|
|
3
|
+
from ._helper_functions import resolve_dataset_id
|
|
4
|
+
from typing import List, Optional, Union
|
|
5
|
+
import sempy_labs._icons as icons
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def clear_cache(dataset: str, workspace: Optional[str] = None):
|
|
9
|
+
"""
|
|
10
|
+
Clears the cache of a semantic model.
|
|
11
|
+
|
|
12
|
+
Parameters
|
|
13
|
+
----------
|
|
14
|
+
dataset : str
|
|
15
|
+
Name of the semantic model.
|
|
16
|
+
workspace : str, default=None
|
|
17
|
+
The Fabric workspace name.
|
|
18
|
+
Defaults to None which resolves to the workspace of the attached lakehouse
|
|
19
|
+
or if no lakehouse attached, resolves to the workspace of the notebook.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
if workspace == None:
|
|
23
|
+
workspace_id = fabric.get_workspace_id()
|
|
24
|
+
workspace = fabric.resolve_workspace_name(workspace_id)
|
|
25
|
+
|
|
26
|
+
datasetID = resolve_dataset_id(dataset=dataset, workspace=workspace)
|
|
27
|
+
|
|
28
|
+
xmla = f"""
|
|
29
|
+
<ClearCache xmlns="http://schemas.microsoft.com/analysisservices/2003/engine">
|
|
30
|
+
<Object>
|
|
31
|
+
<DatabaseID>{datasetID}</DatabaseID>
|
|
32
|
+
</Object>
|
|
33
|
+
</ClearCache>
|
|
34
|
+
"""
|
|
35
|
+
fabric.execute_xmla(dataset=dataset, xmla_command=xmla, workspace=workspace)
|
|
36
|
+
|
|
37
|
+
outputtext = f"{icons.green_dot} Cache cleared for the '{dataset}' semantic model within the '{workspace}' workspace."
|
|
38
|
+
|
|
39
|
+
return outputtext
|