semantic-link-labs 0.8.4__py3-none-any.whl → 0.8.6__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.8.4.dist-info → semantic_link_labs-0.8.6.dist-info}/METADATA +9 -3
- {semantic_link_labs-0.8.4.dist-info → semantic_link_labs-0.8.6.dist-info}/RECORD +49 -47
- {semantic_link_labs-0.8.4.dist-info → semantic_link_labs-0.8.6.dist-info}/WHEEL +1 -1
- sempy_labs/__init__.py +29 -1
- sempy_labs/_data_pipelines.py +3 -3
- sempy_labs/_dataflows.py +116 -3
- sempy_labs/_dax.py +189 -3
- sempy_labs/_deployment_pipelines.py +3 -3
- sempy_labs/_environments.py +3 -3
- sempy_labs/_eventhouses.py +3 -3
- sempy_labs/_eventstreams.py +3 -3
- sempy_labs/_external_data_shares.py +1 -1
- sempy_labs/_generate_semantic_model.py +3 -3
- sempy_labs/_git.py +7 -7
- sempy_labs/_helper_functions.py +25 -1
- sempy_labs/_kql_databases.py +3 -3
- sempy_labs/_kql_querysets.py +3 -3
- sempy_labs/_mirrored_databases.py +428 -0
- sempy_labs/_mirrored_warehouses.py +1 -1
- sempy_labs/_ml_experiments.py +3 -3
- sempy_labs/_ml_models.py +4 -4
- sempy_labs/_model_bpa.py +209 -180
- sempy_labs/_model_bpa_bulk.py +48 -24
- sempy_labs/_model_dependencies.py +42 -86
- sempy_labs/_notebooks.py +2 -2
- sempy_labs/_query_scale_out.py +4 -4
- sempy_labs/_refresh_semantic_model.py +2 -2
- sempy_labs/_spark.py +6 -6
- sempy_labs/_vertipaq.py +31 -19
- sempy_labs/_warehouses.py +3 -3
- sempy_labs/_workspace_identity.py +2 -2
- sempy_labs/_workspaces.py +7 -7
- sempy_labs/admin/__init__.py +2 -0
- sempy_labs/admin/_basic_functions.py +54 -8
- sempy_labs/admin/_domains.py +1 -1
- sempy_labs/directlake/_update_directlake_partition_entity.py +1 -1
- sempy_labs/directlake/_warm_cache.py +10 -9
- sempy_labs/lakehouse/_get_lakehouse_tables.py +1 -1
- sempy_labs/lakehouse/_shortcuts.py +2 -2
- sempy_labs/migration/_create_pqt_file.py +9 -4
- sempy_labs/report/__init__.py +2 -0
- sempy_labs/report/_download_report.py +75 -0
- sempy_labs/report/_generate_report.py +3 -3
- sempy_labs/report/_report_functions.py +3 -3
- sempy_labs/report/_report_rebind.py +1 -1
- sempy_labs/report/_reportwrapper.py +4 -2
- sempy_labs/tom/_model.py +71 -35
- {semantic_link_labs-0.8.4.dist-info → semantic_link_labs-0.8.6.dist-info}/LICENSE +0 -0
- {semantic_link_labs-0.8.4.dist-info → semantic_link_labs-0.8.6.dist-info}/top_level.txt +0 -0
sempy_labs/_model_bpa.py
CHANGED
|
@@ -123,210 +123,239 @@ def run_model_bpa(
|
|
|
123
123
|
dataset=dataset, workspace=workspace, readonly=True
|
|
124
124
|
) as tom:
|
|
125
125
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
translation_file = (
|
|
131
|
-
f"{current_dir}/_bpa_translation/_model/_translations_{language}.po"
|
|
126
|
+
# Do not run BPA for models with no tables
|
|
127
|
+
if tom.model.Tables.Count == 0:
|
|
128
|
+
print(
|
|
129
|
+
f"{icons.warning} The '{dataset}' semantic model within the '{workspace}' workspace has no tables and therefore there are no valid BPA results."
|
|
132
130
|
)
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
131
|
+
finalDF = pd.DataFrame(
|
|
132
|
+
columns=[
|
|
133
|
+
"Category",
|
|
134
|
+
"Rule Name",
|
|
135
|
+
"Severity",
|
|
136
|
+
"Object Type",
|
|
137
|
+
"Object Name",
|
|
138
|
+
"Description",
|
|
139
|
+
"URL",
|
|
140
|
+
]
|
|
141
|
+
)
|
|
142
|
+
else:
|
|
143
|
+
dep = get_model_calc_dependencies(dataset=dataset, workspace=workspace)
|
|
140
144
|
|
|
141
|
-
|
|
145
|
+
def translate_using_po(rule_file):
|
|
146
|
+
current_dir = os.path.dirname(os.path.abspath(__file__))
|
|
147
|
+
translation_file = (
|
|
148
|
+
f"{current_dir}/_bpa_translation/_model/_translations_{language}.po"
|
|
149
|
+
)
|
|
150
|
+
for c in ["Category", "Description", "Rule Name"]:
|
|
151
|
+
po = polib.pofile(translation_file)
|
|
152
|
+
for entry in po:
|
|
153
|
+
if entry.tcomment == c.lower().replace(" ", "_"):
|
|
154
|
+
rule_file.loc[rule_file["Rule Name"] == entry.msgid, c] = (
|
|
155
|
+
entry.msgstr
|
|
156
|
+
)
|
|
142
157
|
|
|
143
|
-
|
|
144
|
-
if language is not None and rules is None and language in language_list:
|
|
145
|
-
rules = model_bpa_rules(dependencies=dep)
|
|
146
|
-
translate_using_po(rules)
|
|
147
|
-
translated = True
|
|
148
|
-
if rules is None:
|
|
149
|
-
rules = model_bpa_rules(dependencies=dep)
|
|
150
|
-
if language is not None and not translated:
|
|
158
|
+
translated = False
|
|
151
159
|
|
|
152
|
-
|
|
160
|
+
# Translations
|
|
161
|
+
if language is not None and rules is None and language in language_list:
|
|
162
|
+
rules = model_bpa_rules(dependencies=dep)
|
|
163
|
+
translate_using_po(rules)
|
|
164
|
+
translated = True
|
|
165
|
+
if rules is None:
|
|
166
|
+
rules = model_bpa_rules(dependencies=dep)
|
|
167
|
+
if language is not None and not translated:
|
|
153
168
|
|
|
154
|
-
|
|
155
|
-
from pyspark.sql import SparkSession
|
|
169
|
+
def translate_using_spark(rule_file):
|
|
156
170
|
|
|
157
|
-
|
|
158
|
-
|
|
171
|
+
from synapse.ml.services import Translate
|
|
172
|
+
from pyspark.sql import SparkSession
|
|
159
173
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
StructField("Rule Name", StringType(), True),
|
|
165
|
-
StructField("Description", StringType(), True),
|
|
166
|
-
]
|
|
167
|
-
)
|
|
174
|
+
rules_temp = rule_file.copy()
|
|
175
|
+
rules_temp = rules_temp.drop(
|
|
176
|
+
["Expression", "URL", "Severity"], axis=1
|
|
177
|
+
)
|
|
168
178
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
.setTextCol(clm)
|
|
177
|
-
.setToLanguage(language)
|
|
178
|
-
.setOutputCol("translation")
|
|
179
|
-
.setConcurrency(5)
|
|
179
|
+
schema = StructType(
|
|
180
|
+
[
|
|
181
|
+
StructField("Category", StringType(), True),
|
|
182
|
+
StructField("Scope", StringType(), True),
|
|
183
|
+
StructField("Rule Name", StringType(), True),
|
|
184
|
+
StructField("Description", StringType(), True),
|
|
185
|
+
]
|
|
180
186
|
)
|
|
181
187
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
.
|
|
188
|
+
spark = SparkSession.builder.getOrCreate()
|
|
189
|
+
dfRules = spark.createDataFrame(rules_temp, schema)
|
|
190
|
+
|
|
191
|
+
columns = ["Category", "Rule Name", "Description"]
|
|
192
|
+
for clm in columns:
|
|
193
|
+
translate = (
|
|
194
|
+
Translate()
|
|
195
|
+
.setTextCol(clm)
|
|
196
|
+
.setToLanguage(language)
|
|
197
|
+
.setOutputCol("translation")
|
|
198
|
+
.setConcurrency(5)
|
|
190
199
|
)
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
200
|
+
|
|
201
|
+
if clm == "Rule Name":
|
|
202
|
+
transDF = (
|
|
203
|
+
translate.transform(dfRules)
|
|
204
|
+
.withColumn(
|
|
205
|
+
"translation",
|
|
206
|
+
flatten(col("translation.translations")),
|
|
207
|
+
)
|
|
208
|
+
.withColumn("translation", col("translation.text"))
|
|
209
|
+
.select(clm, "translation")
|
|
210
|
+
)
|
|
211
|
+
else:
|
|
212
|
+
transDF = (
|
|
213
|
+
translate.transform(dfRules)
|
|
214
|
+
.withColumn(
|
|
215
|
+
"translation",
|
|
216
|
+
flatten(col("translation.translations")),
|
|
217
|
+
)
|
|
218
|
+
.withColumn("translation", col("translation.text"))
|
|
219
|
+
.select("Rule Name", clm, "translation")
|
|
196
220
|
)
|
|
197
|
-
.withColumn("translation", col("translation.text"))
|
|
198
|
-
.select("Rule Name", clm, "translation")
|
|
199
|
-
)
|
|
200
221
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
222
|
+
df_panda = transDF.toPandas()
|
|
223
|
+
rule_file = pd.merge(
|
|
224
|
+
rule_file,
|
|
225
|
+
df_panda[["Rule Name", "translation"]],
|
|
226
|
+
on="Rule Name",
|
|
227
|
+
how="left",
|
|
228
|
+
)
|
|
208
229
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
230
|
+
rule_file = rule_file.rename(
|
|
231
|
+
columns={"translation": f"{clm}Translated"}
|
|
232
|
+
)
|
|
233
|
+
rule_file[f"{clm}Translated"] = rule_file[
|
|
234
|
+
f"{clm}Translated"
|
|
235
|
+
].apply(lambda x: x[0] if x is not None else None)
|
|
215
236
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
237
|
+
for clm in columns:
|
|
238
|
+
rule_file = rule_file.drop([clm], axis=1)
|
|
239
|
+
rule_file = rule_file.rename(columns={f"{clm}Translated": clm})
|
|
219
240
|
|
|
220
|
-
|
|
241
|
+
return rule_file
|
|
221
242
|
|
|
222
|
-
|
|
243
|
+
rules = translate_using_spark(rules)
|
|
223
244
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
245
|
+
rules.loc[rules["Severity"] == "Warning", "Severity"] = icons.warning
|
|
246
|
+
rules.loc[rules["Severity"] == "Error", "Severity"] = icons.error
|
|
247
|
+
rules.loc[rules["Severity"] == "Info", "Severity"] = icons.info
|
|
227
248
|
|
|
228
|
-
|
|
249
|
+
pd.set_option("display.max_colwidth", 1000)
|
|
229
250
|
|
|
230
|
-
|
|
251
|
+
violations = pd.DataFrame(columns=["Object Name", "Scope", "Rule Name"])
|
|
231
252
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
253
|
+
scope_to_dataframe = {
|
|
254
|
+
"Relationship": (
|
|
255
|
+
tom.model.Relationships,
|
|
256
|
+
lambda obj: create_relationship_name(
|
|
257
|
+
obj.FromTable.Name,
|
|
258
|
+
obj.FromColumn.Name,
|
|
259
|
+
obj.ToTable.Name,
|
|
260
|
+
obj.ToColumn.Name,
|
|
261
|
+
),
|
|
240
262
|
),
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
263
|
+
"Column": (
|
|
264
|
+
tom.all_columns(),
|
|
265
|
+
lambda obj: format_dax_object_name(obj.Parent.Name, obj.Name),
|
|
266
|
+
),
|
|
267
|
+
"Measure": (tom.all_measures(), lambda obj: obj.Name),
|
|
268
|
+
"Hierarchy": (
|
|
269
|
+
tom.all_hierarchies(),
|
|
270
|
+
lambda obj: format_dax_object_name(obj.Parent.Name, obj.Name),
|
|
271
|
+
),
|
|
272
|
+
"Table": (tom.model.Tables, lambda obj: obj.Name),
|
|
273
|
+
"Role": (tom.model.Roles, lambda obj: obj.Name),
|
|
274
|
+
"Model": (tom.model, lambda obj: obj.Model.Name),
|
|
275
|
+
"Calculation Item": (
|
|
276
|
+
tom.all_calculation_items(),
|
|
277
|
+
lambda obj: format_dax_object_name(obj.Parent.Table.Name, obj.Name),
|
|
278
|
+
),
|
|
279
|
+
"Row Level Security": (
|
|
280
|
+
tom.all_rls(),
|
|
281
|
+
lambda obj: format_dax_object_name(obj.Parent.Name, obj.Name),
|
|
282
|
+
),
|
|
283
|
+
"Partition": (
|
|
284
|
+
tom.all_partitions(),
|
|
285
|
+
lambda obj: format_dax_object_name(obj.Parent.Name, obj.Name),
|
|
286
|
+
),
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
for i, r in rules.iterrows():
|
|
290
|
+
ruleName = r["Rule Name"]
|
|
291
|
+
expr = r["Expression"]
|
|
292
|
+
scopes = r["Scope"]
|
|
293
|
+
|
|
294
|
+
if isinstance(scopes, str):
|
|
295
|
+
scopes = [scopes]
|
|
296
|
+
|
|
297
|
+
for scope in scopes:
|
|
298
|
+
func = scope_to_dataframe[scope][0]
|
|
299
|
+
nm = scope_to_dataframe[scope][1]
|
|
300
|
+
|
|
301
|
+
if scope == "Model":
|
|
302
|
+
x = []
|
|
303
|
+
if expr(func, tom):
|
|
304
|
+
x = ["Model"]
|
|
305
|
+
elif scope == "Measure":
|
|
306
|
+
x = [nm(obj) for obj in tom.all_measures() if expr(obj, tom)]
|
|
307
|
+
elif scope == "Column":
|
|
308
|
+
x = [nm(obj) for obj in tom.all_columns() if expr(obj, tom)]
|
|
309
|
+
elif scope == "Partition":
|
|
310
|
+
x = [nm(obj) for obj in tom.all_partitions() if expr(obj, tom)]
|
|
311
|
+
elif scope == "Hierarchy":
|
|
312
|
+
x = [nm(obj) for obj in tom.all_hierarchies() if expr(obj, tom)]
|
|
313
|
+
elif scope == "Table":
|
|
314
|
+
x = [nm(obj) for obj in tom.model.Tables if expr(obj, tom)]
|
|
315
|
+
elif scope == "Relationship":
|
|
316
|
+
x = [
|
|
317
|
+
nm(obj) for obj in tom.model.Relationships if expr(obj, tom)
|
|
318
|
+
]
|
|
319
|
+
elif scope == "Role":
|
|
320
|
+
x = [nm(obj) for obj in tom.model.Roles if expr(obj, tom)]
|
|
321
|
+
elif scope == "Row Level Security":
|
|
322
|
+
x = [nm(obj) for obj in tom.all_rls() if expr(obj, tom)]
|
|
323
|
+
elif scope == "Calculation Item":
|
|
324
|
+
x = [
|
|
325
|
+
nm(obj)
|
|
326
|
+
for obj in tom.all_calculation_items()
|
|
327
|
+
if expr(obj, tom)
|
|
328
|
+
]
|
|
329
|
+
|
|
330
|
+
if len(x) > 0:
|
|
331
|
+
new_data = {
|
|
332
|
+
"Object Name": x,
|
|
333
|
+
"Scope": scope,
|
|
334
|
+
"Rule Name": ruleName,
|
|
335
|
+
}
|
|
336
|
+
violations = pd.concat(
|
|
337
|
+
[violations, pd.DataFrame(new_data)], ignore_index=True
|
|
338
|
+
)
|
|
310
339
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
340
|
+
prepDF = pd.merge(
|
|
341
|
+
violations,
|
|
342
|
+
rules[["Rule Name", "Category", "Severity", "Description", "URL"]],
|
|
343
|
+
left_on="Rule Name",
|
|
344
|
+
right_on="Rule Name",
|
|
345
|
+
how="left",
|
|
346
|
+
)
|
|
347
|
+
prepDF.rename(columns={"Scope": "Object Type"}, inplace=True)
|
|
348
|
+
finalDF = prepDF[
|
|
349
|
+
[
|
|
350
|
+
"Category",
|
|
351
|
+
"Rule Name",
|
|
352
|
+
"Severity",
|
|
353
|
+
"Object Type",
|
|
354
|
+
"Object Name",
|
|
355
|
+
"Description",
|
|
356
|
+
"URL",
|
|
357
|
+
]
|
|
328
358
|
]
|
|
329
|
-
]
|
|
330
359
|
|
|
331
360
|
if export:
|
|
332
361
|
if not lakehouse_attached():
|
sempy_labs/_model_bpa_bulk.py
CHANGED
|
@@ -25,6 +25,7 @@ def run_model_bpa_bulk(
|
|
|
25
25
|
language: Optional[str] = None,
|
|
26
26
|
workspace: Optional[str | List[str]] = None,
|
|
27
27
|
skip_models: Optional[str | List[str]] = ["ModelBPA", "Fabric Capacity Metrics"],
|
|
28
|
+
skip_models_in_workspace: Optional[dict] = None,
|
|
28
29
|
):
|
|
29
30
|
"""
|
|
30
31
|
Runs the semantic model Best Practice Analyzer across all semantic models in a workspace (or all accessible workspaces).
|
|
@@ -33,8 +34,6 @@ def run_model_bpa_bulk(
|
|
|
33
34
|
|
|
34
35
|
Parameters
|
|
35
36
|
----------
|
|
36
|
-
dataset : str
|
|
37
|
-
Name of the semantic model.
|
|
38
37
|
rules : pandas.DataFrame, default=None
|
|
39
38
|
A pandas dataframe containing rules to be evaluated. Based on the format of the dataframe produced by the model_bpa_rules function.
|
|
40
39
|
extended : bool, default=False
|
|
@@ -47,6 +46,12 @@ def run_model_bpa_bulk(
|
|
|
47
46
|
Defaults to None which scans all accessible workspaces.
|
|
48
47
|
skip_models : str | List[str], default=['ModelBPA', 'Fabric Capacity Metrics']
|
|
49
48
|
The semantic models to always skip when running this analysis.
|
|
49
|
+
skip_models_in_workspace : dict, default=None
|
|
50
|
+
A dictionary showing specific semantic models within specific workspaces to skip. See the example below:
|
|
51
|
+
{
|
|
52
|
+
"Workspace A": ["Dataset1", "Dataset2"],
|
|
53
|
+
"Workspace B": ["Dataset5", "Dataset 8"],
|
|
54
|
+
}
|
|
50
55
|
"""
|
|
51
56
|
|
|
52
57
|
if not lakehouse_attached():
|
|
@@ -68,7 +73,6 @@ def run_model_bpa_bulk(
|
|
|
68
73
|
)
|
|
69
74
|
lakeT = get_lakehouse_tables(lakehouse=lakehouse, workspace=lakehouse_workspace)
|
|
70
75
|
lakeT_filt = lakeT[lakeT["Table Name"] == output_table]
|
|
71
|
-
# query = f"SELECT MAX(RunId) FROM {lakehouse}.{output_table}"
|
|
72
76
|
if len(lakeT_filt) == 0:
|
|
73
77
|
runId = 1
|
|
74
78
|
else:
|
|
@@ -84,6 +88,11 @@ def run_model_bpa_bulk(
|
|
|
84
88
|
else:
|
|
85
89
|
dfW_filt = dfW[dfW["Name"].isin(workspace)]
|
|
86
90
|
|
|
91
|
+
if len(dfW_filt) == 0:
|
|
92
|
+
raise ValueError(
|
|
93
|
+
f"{icons.red_dot} There are no valid workspaces to assess. This is likely due to not having proper permissions to the workspace(s) entered in the 'workspace' parameter."
|
|
94
|
+
)
|
|
95
|
+
|
|
87
96
|
for i, r in dfW_filt.iterrows():
|
|
88
97
|
wksp = r["Name"]
|
|
89
98
|
wksp_id = r["Id"]
|
|
@@ -91,6 +100,13 @@ def run_model_bpa_bulk(
|
|
|
91
100
|
df = pd.DataFrame(columns=list(icons.bpa_schema.keys()))
|
|
92
101
|
dfD = fabric.list_datasets(workspace=wksp, mode="rest")
|
|
93
102
|
|
|
103
|
+
# Skip models in workspace
|
|
104
|
+
if skip_models_in_workspace is not None and isinstance(
|
|
105
|
+
skip_models_in_workspace, dict
|
|
106
|
+
):
|
|
107
|
+
skip_models_wkspc = skip_models_in_workspace.get(wksp)
|
|
108
|
+
dfD = dfD[~dfD["Dataset Name"].isin(skip_models_wkspc)]
|
|
109
|
+
|
|
94
110
|
# Exclude default semantic models
|
|
95
111
|
if len(dfD) > 0:
|
|
96
112
|
dfI = fabric.list_items(workspace=wksp)
|
|
@@ -132,7 +148,10 @@ def run_model_bpa_bulk(
|
|
|
132
148
|
|
|
133
149
|
bpa_df["RunId"] = bpa_df["RunId"].astype("int")
|
|
134
150
|
|
|
135
|
-
df
|
|
151
|
+
if df.empty:
|
|
152
|
+
df = bpa_df
|
|
153
|
+
if not bpa_df.empty:
|
|
154
|
+
df = pd.concat([df, bpa_df], ignore_index=True)
|
|
136
155
|
print(
|
|
137
156
|
f"{icons.green_dot} Collected Model BPA stats for the '{dataset_name}' semantic model within the '{wksp}' workspace."
|
|
138
157
|
)
|
|
@@ -142,28 +161,33 @@ def run_model_bpa_bulk(
|
|
|
142
161
|
)
|
|
143
162
|
print(e)
|
|
144
163
|
|
|
145
|
-
df
|
|
164
|
+
if len(df) == 0:
|
|
165
|
+
print(
|
|
166
|
+
f"{icons.yellow_dot} No BPA results to save for the '{wksp}' workspace."
|
|
167
|
+
)
|
|
168
|
+
else:
|
|
169
|
+
df["Severity"].replace(icons.severity_mapping, inplace=True)
|
|
146
170
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
171
|
+
# Append save results individually for each workspace (so as not to create a giant dataframe)
|
|
172
|
+
print(
|
|
173
|
+
f"{icons.in_progress} Saving the Model BPA results of the '{wksp}' workspace to the '{output_table}' within the '{lakehouse}' lakehouse within the '{lakehouse_workspace}' workspace..."
|
|
174
|
+
)
|
|
151
175
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
176
|
+
schema = {
|
|
177
|
+
key.replace(" ", "_"): value
|
|
178
|
+
for key, value in icons.bpa_schema.items()
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
save_as_delta_table(
|
|
182
|
+
dataframe=df,
|
|
183
|
+
delta_table_name=output_table,
|
|
184
|
+
write_mode="append",
|
|
185
|
+
schema=schema,
|
|
186
|
+
merge_schema=True,
|
|
187
|
+
)
|
|
188
|
+
print(
|
|
189
|
+
f"{icons.green_dot} Saved BPA results to the '{output_table}' delta table."
|
|
190
|
+
)
|
|
167
191
|
|
|
168
192
|
print(f"{icons.green_dot} Bulk BPA scan complete.")
|
|
169
193
|
|