semantic-link-labs 0.12.8__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.
- semantic_link_labs-0.12.8.dist-info/METADATA +354 -0
- semantic_link_labs-0.12.8.dist-info/RECORD +243 -0
- semantic_link_labs-0.12.8.dist-info/WHEEL +5 -0
- semantic_link_labs-0.12.8.dist-info/licenses/LICENSE +21 -0
- semantic_link_labs-0.12.8.dist-info/top_level.txt +1 -0
- sempy_labs/__init__.py +606 -0
- sempy_labs/_a_lib_info.py +2 -0
- sempy_labs/_ai.py +437 -0
- sempy_labs/_authentication.py +264 -0
- sempy_labs/_bpa_translation/_model/_translations_am-ET.po +869 -0
- sempy_labs/_bpa_translation/_model/_translations_ar-AE.po +908 -0
- sempy_labs/_bpa_translation/_model/_translations_bg-BG.po +968 -0
- sempy_labs/_bpa_translation/_model/_translations_ca-ES.po +963 -0
- sempy_labs/_bpa_translation/_model/_translations_cs-CZ.po +943 -0
- sempy_labs/_bpa_translation/_model/_translations_da-DK.po +945 -0
- sempy_labs/_bpa_translation/_model/_translations_de-DE.po +988 -0
- sempy_labs/_bpa_translation/_model/_translations_el-GR.po +993 -0
- sempy_labs/_bpa_translation/_model/_translations_es-ES.po +971 -0
- sempy_labs/_bpa_translation/_model/_translations_fa-IR.po +933 -0
- sempy_labs/_bpa_translation/_model/_translations_fi-FI.po +942 -0
- sempy_labs/_bpa_translation/_model/_translations_fr-FR.po +994 -0
- sempy_labs/_bpa_translation/_model/_translations_ga-IE.po +967 -0
- sempy_labs/_bpa_translation/_model/_translations_he-IL.po +902 -0
- sempy_labs/_bpa_translation/_model/_translations_hi-IN.po +944 -0
- sempy_labs/_bpa_translation/_model/_translations_hu-HU.po +963 -0
- sempy_labs/_bpa_translation/_model/_translations_id-ID.po +946 -0
- sempy_labs/_bpa_translation/_model/_translations_is-IS.po +939 -0
- sempy_labs/_bpa_translation/_model/_translations_it-IT.po +986 -0
- sempy_labs/_bpa_translation/_model/_translations_ja-JP.po +846 -0
- sempy_labs/_bpa_translation/_model/_translations_ko-KR.po +839 -0
- sempy_labs/_bpa_translation/_model/_translations_mt-MT.po +967 -0
- sempy_labs/_bpa_translation/_model/_translations_nl-NL.po +978 -0
- sempy_labs/_bpa_translation/_model/_translations_pl-PL.po +962 -0
- sempy_labs/_bpa_translation/_model/_translations_pt-BR.po +962 -0
- sempy_labs/_bpa_translation/_model/_translations_pt-PT.po +957 -0
- sempy_labs/_bpa_translation/_model/_translations_ro-RO.po +968 -0
- sempy_labs/_bpa_translation/_model/_translations_ru-RU.po +964 -0
- sempy_labs/_bpa_translation/_model/_translations_sk-SK.po +952 -0
- sempy_labs/_bpa_translation/_model/_translations_sl-SL.po +950 -0
- sempy_labs/_bpa_translation/_model/_translations_sv-SE.po +942 -0
- sempy_labs/_bpa_translation/_model/_translations_ta-IN.po +976 -0
- sempy_labs/_bpa_translation/_model/_translations_te-IN.po +947 -0
- sempy_labs/_bpa_translation/_model/_translations_th-TH.po +924 -0
- sempy_labs/_bpa_translation/_model/_translations_tr-TR.po +953 -0
- sempy_labs/_bpa_translation/_model/_translations_uk-UA.po +961 -0
- sempy_labs/_bpa_translation/_model/_translations_zh-CN.po +804 -0
- sempy_labs/_bpa_translation/_model/_translations_zu-ZA.po +969 -0
- sempy_labs/_capacities.py +1198 -0
- sempy_labs/_capacity_migration.py +660 -0
- sempy_labs/_clear_cache.py +351 -0
- sempy_labs/_connections.py +610 -0
- sempy_labs/_dashboards.py +69 -0
- sempy_labs/_data_access_security.py +98 -0
- sempy_labs/_data_pipelines.py +162 -0
- sempy_labs/_dataflows.py +668 -0
- sempy_labs/_dax.py +501 -0
- sempy_labs/_daxformatter.py +80 -0
- sempy_labs/_delta_analyzer.py +467 -0
- sempy_labs/_delta_analyzer_history.py +301 -0
- sempy_labs/_dictionary_diffs.py +221 -0
- sempy_labs/_documentation.py +147 -0
- sempy_labs/_domains.py +51 -0
- sempy_labs/_eventhouses.py +182 -0
- sempy_labs/_external_data_shares.py +230 -0
- sempy_labs/_gateways.py +521 -0
- sempy_labs/_generate_semantic_model.py +521 -0
- sempy_labs/_get_connection_string.py +84 -0
- sempy_labs/_git.py +543 -0
- sempy_labs/_graphQL.py +90 -0
- sempy_labs/_helper_functions.py +2833 -0
- sempy_labs/_icons.py +149 -0
- sempy_labs/_job_scheduler.py +609 -0
- sempy_labs/_kql_databases.py +149 -0
- sempy_labs/_kql_querysets.py +124 -0
- sempy_labs/_kusto.py +137 -0
- sempy_labs/_labels.py +124 -0
- sempy_labs/_list_functions.py +1720 -0
- sempy_labs/_managed_private_endpoints.py +253 -0
- sempy_labs/_mirrored_databases.py +416 -0
- sempy_labs/_mirrored_warehouses.py +60 -0
- sempy_labs/_ml_experiments.py +113 -0
- sempy_labs/_model_auto_build.py +140 -0
- sempy_labs/_model_bpa.py +557 -0
- sempy_labs/_model_bpa_bulk.py +378 -0
- sempy_labs/_model_bpa_rules.py +859 -0
- sempy_labs/_model_dependencies.py +343 -0
- sempy_labs/_mounted_data_factories.py +123 -0
- sempy_labs/_notebooks.py +441 -0
- sempy_labs/_one_lake_integration.py +151 -0
- sempy_labs/_onelake.py +131 -0
- sempy_labs/_query_scale_out.py +433 -0
- sempy_labs/_refresh_semantic_model.py +435 -0
- sempy_labs/_semantic_models.py +468 -0
- sempy_labs/_spark.py +455 -0
- sempy_labs/_sql.py +241 -0
- sempy_labs/_sql_audit_settings.py +207 -0
- sempy_labs/_sql_endpoints.py +214 -0
- sempy_labs/_tags.py +201 -0
- sempy_labs/_translations.py +43 -0
- sempy_labs/_user_delegation_key.py +44 -0
- sempy_labs/_utils.py +79 -0
- sempy_labs/_vertipaq.py +1021 -0
- sempy_labs/_vpax.py +388 -0
- sempy_labs/_warehouses.py +234 -0
- sempy_labs/_workloads.py +140 -0
- sempy_labs/_workspace_identity.py +72 -0
- sempy_labs/_workspaces.py +595 -0
- sempy_labs/admin/__init__.py +170 -0
- sempy_labs/admin/_activities.py +167 -0
- sempy_labs/admin/_apps.py +145 -0
- sempy_labs/admin/_artifacts.py +65 -0
- sempy_labs/admin/_basic_functions.py +463 -0
- sempy_labs/admin/_capacities.py +508 -0
- sempy_labs/admin/_dataflows.py +45 -0
- sempy_labs/admin/_datasets.py +186 -0
- sempy_labs/admin/_domains.py +522 -0
- sempy_labs/admin/_external_data_share.py +100 -0
- sempy_labs/admin/_git.py +72 -0
- sempy_labs/admin/_items.py +265 -0
- sempy_labs/admin/_labels.py +211 -0
- sempy_labs/admin/_reports.py +241 -0
- sempy_labs/admin/_scanner.py +118 -0
- sempy_labs/admin/_shared.py +82 -0
- sempy_labs/admin/_sharing_links.py +110 -0
- sempy_labs/admin/_tags.py +131 -0
- sempy_labs/admin/_tenant.py +503 -0
- sempy_labs/admin/_tenant_keys.py +89 -0
- sempy_labs/admin/_users.py +140 -0
- sempy_labs/admin/_workspaces.py +236 -0
- sempy_labs/deployment_pipeline/__init__.py +23 -0
- sempy_labs/deployment_pipeline/_items.py +580 -0
- sempy_labs/directlake/__init__.py +57 -0
- sempy_labs/directlake/_autosync.py +58 -0
- sempy_labs/directlake/_directlake_schema_compare.py +120 -0
- sempy_labs/directlake/_directlake_schema_sync.py +161 -0
- sempy_labs/directlake/_dl_helper.py +274 -0
- sempy_labs/directlake/_generate_shared_expression.py +94 -0
- sempy_labs/directlake/_get_directlake_lakehouse.py +62 -0
- sempy_labs/directlake/_get_shared_expression.py +34 -0
- sempy_labs/directlake/_guardrails.py +96 -0
- sempy_labs/directlake/_list_directlake_model_calc_tables.py +70 -0
- sempy_labs/directlake/_show_unsupported_directlake_objects.py +90 -0
- sempy_labs/directlake/_update_directlake_model_lakehouse_connection.py +239 -0
- sempy_labs/directlake/_update_directlake_partition_entity.py +259 -0
- sempy_labs/directlake/_warm_cache.py +236 -0
- sempy_labs/dotnet_lib/dotnet.runtime.config.json +10 -0
- sempy_labs/environment/__init__.py +23 -0
- sempy_labs/environment/_items.py +212 -0
- sempy_labs/environment/_pubstage.py +223 -0
- sempy_labs/eventstream/__init__.py +37 -0
- sempy_labs/eventstream/_items.py +263 -0
- sempy_labs/eventstream/_topology.py +652 -0
- sempy_labs/graph/__init__.py +59 -0
- sempy_labs/graph/_groups.py +651 -0
- sempy_labs/graph/_sensitivity_labels.py +120 -0
- sempy_labs/graph/_teams.py +125 -0
- sempy_labs/graph/_user_licenses.py +96 -0
- sempy_labs/graph/_users.py +516 -0
- sempy_labs/graph_model/__init__.py +15 -0
- sempy_labs/graph_model/_background_jobs.py +63 -0
- sempy_labs/graph_model/_items.py +149 -0
- sempy_labs/lakehouse/__init__.py +67 -0
- sempy_labs/lakehouse/_blobs.py +247 -0
- sempy_labs/lakehouse/_get_lakehouse_columns.py +102 -0
- sempy_labs/lakehouse/_get_lakehouse_tables.py +274 -0
- sempy_labs/lakehouse/_helper.py +250 -0
- sempy_labs/lakehouse/_lakehouse.py +351 -0
- sempy_labs/lakehouse/_livy_sessions.py +143 -0
- sempy_labs/lakehouse/_materialized_lake_views.py +157 -0
- sempy_labs/lakehouse/_partitioning.py +165 -0
- sempy_labs/lakehouse/_schemas.py +217 -0
- sempy_labs/lakehouse/_shortcuts.py +440 -0
- sempy_labs/migration/__init__.py +35 -0
- sempy_labs/migration/_create_pqt_file.py +238 -0
- sempy_labs/migration/_direct_lake_to_import.py +105 -0
- sempy_labs/migration/_migrate_calctables_to_lakehouse.py +398 -0
- sempy_labs/migration/_migrate_calctables_to_semantic_model.py +148 -0
- sempy_labs/migration/_migrate_model_objects_to_semantic_model.py +533 -0
- sempy_labs/migration/_migrate_tables_columns_to_semantic_model.py +172 -0
- sempy_labs/migration/_migration_validation.py +71 -0
- sempy_labs/migration/_refresh_calc_tables.py +131 -0
- sempy_labs/mirrored_azure_databricks_catalog/__init__.py +15 -0
- sempy_labs/mirrored_azure_databricks_catalog/_discover.py +213 -0
- sempy_labs/mirrored_azure_databricks_catalog/_refresh_catalog_metadata.py +45 -0
- sempy_labs/ml_model/__init__.py +23 -0
- sempy_labs/ml_model/_functions.py +427 -0
- sempy_labs/report/_BPAReportTemplate.json +232 -0
- sempy_labs/report/__init__.py +55 -0
- sempy_labs/report/_bpareporttemplate/.pbi/localSettings.json +9 -0
- sempy_labs/report/_bpareporttemplate/.platform +11 -0
- sempy_labs/report/_bpareporttemplate/StaticResources/SharedResources/BaseThemes/CY24SU06.json +710 -0
- sempy_labs/report/_bpareporttemplate/definition/pages/01d72098bda5055bd500/page.json +11 -0
- sempy_labs/report/_bpareporttemplate/definition/pages/01d72098bda5055bd500/visuals/1b08bce3bebabb0a27a8/visual.json +191 -0
- sempy_labs/report/_bpareporttemplate/definition/pages/01d72098bda5055bd500/visuals/2f22ddb70c301693c165/visual.json +438 -0
- sempy_labs/report/_bpareporttemplate/definition/pages/01d72098bda5055bd500/visuals/3b1182230aa6c600b43a/visual.json +127 -0
- sempy_labs/report/_bpareporttemplate/definition/pages/01d72098bda5055bd500/visuals/58577ba6380c69891500/visual.json +576 -0
- sempy_labs/report/_bpareporttemplate/definition/pages/01d72098bda5055bd500/visuals/a2a8fa5028b3b776c96c/visual.json +207 -0
- sempy_labs/report/_bpareporttemplate/definition/pages/01d72098bda5055bd500/visuals/adfd47ef30652707b987/visual.json +506 -0
- sempy_labs/report/_bpareporttemplate/definition/pages/01d72098bda5055bd500/visuals/b6a80ee459e716e170b1/visual.json +127 -0
- sempy_labs/report/_bpareporttemplate/definition/pages/01d72098bda5055bd500/visuals/ce3130a721c020cc3d81/visual.json +513 -0
- sempy_labs/report/_bpareporttemplate/definition/pages/92735ae19b31712208ad/page.json +8 -0
- sempy_labs/report/_bpareporttemplate/definition/pages/92735ae19b31712208ad/visuals/66e60dfb526437cd78d1/visual.json +112 -0
- sempy_labs/report/_bpareporttemplate/definition/pages/c597da16dc7e63222a82/page.json +11 -0
- sempy_labs/report/_bpareporttemplate/definition/pages/c597da16dc7e63222a82/visuals/07deb8bce824e1be37d7/visual.json +513 -0
- sempy_labs/report/_bpareporttemplate/definition/pages/c597da16dc7e63222a82/visuals/0b1c68838818b32ad03b/visual.json +352 -0
- sempy_labs/report/_bpareporttemplate/definition/pages/c597da16dc7e63222a82/visuals/0c171de9d2683d10b930/visual.json +37 -0
- sempy_labs/report/_bpareporttemplate/definition/pages/c597da16dc7e63222a82/visuals/0efa01be0510e40a645e/visual.json +542 -0
- sempy_labs/report/_bpareporttemplate/definition/pages/c597da16dc7e63222a82/visuals/6bf2f0eb830ab53cc668/visual.json +221 -0
- sempy_labs/report/_bpareporttemplate/definition/pages/c597da16dc7e63222a82/visuals/88d8141cb8500b60030c/visual.json +127 -0
- sempy_labs/report/_bpareporttemplate/definition/pages/c597da16dc7e63222a82/visuals/a753273590beed656a03/visual.json +576 -0
- sempy_labs/report/_bpareporttemplate/definition/pages/c597da16dc7e63222a82/visuals/b8fdc82cddd61ac447bc/visual.json +127 -0
- sempy_labs/report/_bpareporttemplate/definition/pages/d37dce724a0ccc30044b/page.json +9 -0
- sempy_labs/report/_bpareporttemplate/definition/pages/d37dce724a0ccc30044b/visuals/ce8532a7e25020271077/visual.json +38 -0
- sempy_labs/report/_bpareporttemplate/definition/pages/pages.json +10 -0
- sempy_labs/report/_bpareporttemplate/definition/report.json +176 -0
- sempy_labs/report/_bpareporttemplate/definition/version.json +4 -0
- sempy_labs/report/_bpareporttemplate/definition.pbir +14 -0
- sempy_labs/report/_download_report.py +76 -0
- sempy_labs/report/_export_report.py +257 -0
- sempy_labs/report/_generate_report.py +427 -0
- sempy_labs/report/_paginated.py +76 -0
- sempy_labs/report/_report_bpa.py +354 -0
- sempy_labs/report/_report_bpa_rules.py +115 -0
- sempy_labs/report/_report_functions.py +581 -0
- sempy_labs/report/_report_helper.py +227 -0
- sempy_labs/report/_report_list_functions.py +110 -0
- sempy_labs/report/_report_rebind.py +149 -0
- sempy_labs/report/_reportwrapper.py +3100 -0
- sempy_labs/report/_save_report.py +147 -0
- sempy_labs/snowflake_database/__init__.py +10 -0
- sempy_labs/snowflake_database/_items.py +105 -0
- sempy_labs/sql_database/__init__.py +21 -0
- sempy_labs/sql_database/_items.py +201 -0
- sempy_labs/sql_database/_mirroring.py +79 -0
- sempy_labs/theme/__init__.py +12 -0
- sempy_labs/theme/_org_themes.py +129 -0
- sempy_labs/tom/__init__.py +3 -0
- sempy_labs/tom/_model.py +5977 -0
- sempy_labs/variable_library/__init__.py +19 -0
- sempy_labs/variable_library/_functions.py +403 -0
- sempy_labs/warehouse/__init__.py +28 -0
- sempy_labs/warehouse/_items.py +234 -0
- sempy_labs/warehouse/_restore_points.py +309 -0
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
import pandas as pd
|
|
3
|
+
import datetime
|
|
4
|
+
from sempy._utils._log import log
|
|
5
|
+
from sempy_labs.report._reportwrapper import connect_report
|
|
6
|
+
from sempy_labs.report._report_bpa_rules import report_bpa_rules
|
|
7
|
+
from sempy_labs._helper_functions import (
|
|
8
|
+
format_dax_object_name,
|
|
9
|
+
save_as_delta_table,
|
|
10
|
+
resolve_item_name_and_id,
|
|
11
|
+
resolve_workspace_capacity,
|
|
12
|
+
_get_column_aggregate,
|
|
13
|
+
resolve_workspace_name_and_id,
|
|
14
|
+
)
|
|
15
|
+
from sempy_labs.lakehouse import get_lakehouse_tables, lakehouse_attached
|
|
16
|
+
import sempy_labs._icons as icons
|
|
17
|
+
from IPython.display import display, HTML
|
|
18
|
+
from uuid import UUID
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@log
|
|
22
|
+
def run_report_bpa(
|
|
23
|
+
report: str,
|
|
24
|
+
rules: Optional[pd.DataFrame] = None,
|
|
25
|
+
workspace: Optional[str | UUID] = None,
|
|
26
|
+
# language: Optional[str] = None,
|
|
27
|
+
export: bool = False,
|
|
28
|
+
return_dataframe: bool = False,
|
|
29
|
+
):
|
|
30
|
+
"""
|
|
31
|
+
Displays an HTML visualization of the results of the Best Practice Analyzer scan for a report.
|
|
32
|
+
Note: As with all functions which rely on the ReportWrapper, this function requires the report to be in the 'PBIR' format.
|
|
33
|
+
|
|
34
|
+
Parameters
|
|
35
|
+
----------
|
|
36
|
+
report : str
|
|
37
|
+
Name of the report.
|
|
38
|
+
rules : pandas.DataFrame, default=None
|
|
39
|
+
A pandas dataframe containing rules to be evaluated.
|
|
40
|
+
workspace : str | uuid.UUID, default=None
|
|
41
|
+
The Fabric workspace name or ID.
|
|
42
|
+
Defaults to None which resolves to the workspace of the attached lakehouse
|
|
43
|
+
or if no lakehouse attached, resolves to the workspace of the notebook.
|
|
44
|
+
export : bool, default=False
|
|
45
|
+
If True, exports the resulting dataframe to a delta table in the lakehouse attached to the notebook.
|
|
46
|
+
return_dataframe : bool, default=False
|
|
47
|
+
If True, returns a pandas dataframe instead of the visualization.
|
|
48
|
+
|
|
49
|
+
Returns
|
|
50
|
+
-------
|
|
51
|
+
pandas.DataFrame
|
|
52
|
+
A pandas dataframe in HTML format showing report objects which violated the best practice analyzer rules.
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
with connect_report(
|
|
56
|
+
report=report, workspace=workspace, readonly=True, show_diffs=False
|
|
57
|
+
) as rpt:
|
|
58
|
+
|
|
59
|
+
dfCV = rpt.list_custom_visuals()
|
|
60
|
+
dfP = rpt.list_pages()
|
|
61
|
+
dfRF = rpt.list_report_filters()
|
|
62
|
+
dfRF["Filter Object"] = format_dax_object_name(
|
|
63
|
+
dfRF["Table Name"], dfRF["Object Name"]
|
|
64
|
+
)
|
|
65
|
+
dfPF = rpt.list_page_filters()
|
|
66
|
+
|
|
67
|
+
dfPF["Filter Object"] = (
|
|
68
|
+
dfPF["Page Display Name"]
|
|
69
|
+
+ " : "
|
|
70
|
+
+ format_dax_object_name(dfPF["Table Name"], dfPF["Object Name"])
|
|
71
|
+
)
|
|
72
|
+
dfVF = rpt.list_visual_filters()
|
|
73
|
+
dfVF["Filter Object"] = (
|
|
74
|
+
format_dax_object_name(dfVF["Page Display Name"], dfVF["Visual Name"])
|
|
75
|
+
+ " : "
|
|
76
|
+
+ format_dax_object_name(dfVF["Table Name"], dfVF["Object Name"])
|
|
77
|
+
)
|
|
78
|
+
dfRLM = rpt.list_report_level_measures()
|
|
79
|
+
dfV = rpt.list_visuals()
|
|
80
|
+
dfV["Visual Full Name"] = format_dax_object_name(
|
|
81
|
+
dfV["Page Display Name"], dfV["Visual Name"]
|
|
82
|
+
)
|
|
83
|
+
dfSMO = rpt.list_semantic_model_objects(extended=True)
|
|
84
|
+
|
|
85
|
+
# Translations
|
|
86
|
+
if rules is None:
|
|
87
|
+
rules = report_bpa_rules()
|
|
88
|
+
|
|
89
|
+
scope_to_dataframe = {
|
|
90
|
+
"Custom Visual": (dfCV, ["Custom Visual Display Name"]),
|
|
91
|
+
"Page": (dfP, ["Page Display Name"]),
|
|
92
|
+
"Visual": (dfV, ["Visual Full Name"]),
|
|
93
|
+
"Report Filter": (dfRF, ["Filter Object"]),
|
|
94
|
+
"Page Filter": (dfPF, ["Filter Object"]),
|
|
95
|
+
"Visual Filter": (dfVF, ["Filter Object"]),
|
|
96
|
+
"Report Level Measure": (dfRLM, ["Measure Name"]),
|
|
97
|
+
"Semantic Model": (dfSMO, ["Report Source Object"]),
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
def execute_rule(row):
|
|
101
|
+
scopes = row["Scope"]
|
|
102
|
+
|
|
103
|
+
# support both str and list as scope type
|
|
104
|
+
if isinstance(scopes, str):
|
|
105
|
+
scopes = [scopes]
|
|
106
|
+
|
|
107
|
+
# collect output dataframes
|
|
108
|
+
df_outputs = []
|
|
109
|
+
|
|
110
|
+
for scope in scopes:
|
|
111
|
+
# common fields for each scope
|
|
112
|
+
(df, violation_cols_or_func) = scope_to_dataframe[scope]
|
|
113
|
+
|
|
114
|
+
# execute rule and subset df
|
|
115
|
+
df_violations = df[row["Expression"](df)]
|
|
116
|
+
|
|
117
|
+
# subset the right output columns (e.g. Table Name & Column Name)
|
|
118
|
+
if isinstance(violation_cols_or_func, list):
|
|
119
|
+
violation_func = lambda violations: violations[
|
|
120
|
+
violation_cols_or_func
|
|
121
|
+
]
|
|
122
|
+
else:
|
|
123
|
+
violation_func = violation_cols_or_func
|
|
124
|
+
|
|
125
|
+
# build output data frame
|
|
126
|
+
df_output = violation_func(df_violations).copy()
|
|
127
|
+
|
|
128
|
+
df_output.columns = ["Object Name"]
|
|
129
|
+
df_output["Rule Name"] = row["Rule Name"]
|
|
130
|
+
df_output["Category"] = row["Category"]
|
|
131
|
+
|
|
132
|
+
# Handle 'Object Type' based on scope
|
|
133
|
+
if scope == "Semantic Model":
|
|
134
|
+
# Ensure 'Report Source Object' is unique in dfSMO
|
|
135
|
+
dfSMO_unique = dfSMO.drop_duplicates(subset="Report Source Object")
|
|
136
|
+
# Map 'Object Name' to the 'Report Source' column in dfSMO
|
|
137
|
+
df_output["Object Type"] = df_output["Object Name"].map(
|
|
138
|
+
dfSMO_unique.set_index("Report Source Object")["Report Source"]
|
|
139
|
+
)
|
|
140
|
+
else:
|
|
141
|
+
df_output["Object Type"] = scope
|
|
142
|
+
|
|
143
|
+
df_output["Severity"] = row["Severity"]
|
|
144
|
+
df_output["Description"] = row["Description"]
|
|
145
|
+
if scope in ["Page", "Page Filter"]:
|
|
146
|
+
if not df_output.empty:
|
|
147
|
+
for idx, r in df_output.iterrows():
|
|
148
|
+
df_output.loc[idx, "URL"] = rpt._get_url(
|
|
149
|
+
page_name=r["Object Name"]
|
|
150
|
+
)
|
|
151
|
+
elif scope in ["Visual", "Visual Filter"]:
|
|
152
|
+
if not df_output.empty:
|
|
153
|
+
for idx, r in df_output.iterrows():
|
|
154
|
+
df_output.loc[idx, "URL"] = rpt._get_url(
|
|
155
|
+
page_name=r["Object Name"].split("[")[0][1:-1],
|
|
156
|
+
visual_name=r["Object Name"].split("[")[1].rstrip("]"),
|
|
157
|
+
)
|
|
158
|
+
else:
|
|
159
|
+
df_output["URL"] = rpt._get_url()
|
|
160
|
+
df_outputs.append(df_output)
|
|
161
|
+
|
|
162
|
+
return df_outputs
|
|
163
|
+
|
|
164
|
+
# flatten list of lists
|
|
165
|
+
flatten_dfs = [
|
|
166
|
+
df for dfs in rules.apply(execute_rule, axis=1).tolist() for df in dfs
|
|
167
|
+
]
|
|
168
|
+
|
|
169
|
+
finalDF = pd.concat(flatten_dfs, ignore_index=True)
|
|
170
|
+
finalDF = finalDF[
|
|
171
|
+
[
|
|
172
|
+
"Category",
|
|
173
|
+
"Rule Name",
|
|
174
|
+
"Object Type",
|
|
175
|
+
"Object Name",
|
|
176
|
+
"Severity",
|
|
177
|
+
"Description",
|
|
178
|
+
"URL",
|
|
179
|
+
# "Report URL",
|
|
180
|
+
]
|
|
181
|
+
]
|
|
182
|
+
|
|
183
|
+
if return_dataframe:
|
|
184
|
+
return finalDF
|
|
185
|
+
|
|
186
|
+
if export:
|
|
187
|
+
if not lakehouse_attached():
|
|
188
|
+
raise ValueError(
|
|
189
|
+
f"{icons.red_dot} In order to export the BPA results, a lakehouse must be attached to the notebook."
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
now = datetime.datetime.now()
|
|
193
|
+
delta_table_name = "reportbparesults"
|
|
194
|
+
lakeT = get_lakehouse_tables()
|
|
195
|
+
lakeT_filt = lakeT[lakeT["Table Name"] == delta_table_name]
|
|
196
|
+
|
|
197
|
+
if len(lakeT_filt) == 0:
|
|
198
|
+
runId = 1
|
|
199
|
+
else:
|
|
200
|
+
max_run_id = _get_column_aggregate(table_name=delta_table_name)
|
|
201
|
+
runId = max_run_id + 1
|
|
202
|
+
|
|
203
|
+
(workspace_name, workspace_id) = resolve_workspace_name_and_id(workspace)
|
|
204
|
+
(report_name, report_id) = resolve_item_name_and_id(
|
|
205
|
+
item=report, type="Report", workspace=workspace_id
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
export_df = finalDF.copy()
|
|
209
|
+
capacity_id, capacity_name = resolve_workspace_capacity(workspace=workspace_id)
|
|
210
|
+
export_df["Capacity Name"] = capacity_name
|
|
211
|
+
export_df["Capacity Id"] = capacity_id
|
|
212
|
+
export_df["Workspace Name"] = workspace_name
|
|
213
|
+
export_df["Workspace Id"] = workspace_id
|
|
214
|
+
export_df["Report Name"] = report_name
|
|
215
|
+
export_df["Report Id"] = report_id
|
|
216
|
+
export_df["RunId"] = runId
|
|
217
|
+
export_df["Timestamp"] = now
|
|
218
|
+
export_df["RunId"] = export_df["RunId"].astype(int)
|
|
219
|
+
|
|
220
|
+
export_df = export_df[
|
|
221
|
+
[
|
|
222
|
+
"Capacity Name",
|
|
223
|
+
"Capacity Id",
|
|
224
|
+
"Workspace Name",
|
|
225
|
+
"Workspace Id",
|
|
226
|
+
"Report Name",
|
|
227
|
+
"Report Id",
|
|
228
|
+
"Category",
|
|
229
|
+
"Rule Name",
|
|
230
|
+
"Object Type",
|
|
231
|
+
"Object Name",
|
|
232
|
+
"Severity",
|
|
233
|
+
"Description",
|
|
234
|
+
"URL",
|
|
235
|
+
]
|
|
236
|
+
]
|
|
237
|
+
|
|
238
|
+
save_as_delta_table(
|
|
239
|
+
dataframe=export_df,
|
|
240
|
+
delta_table_name=delta_table_name,
|
|
241
|
+
write_mode="append",
|
|
242
|
+
merge_schema=True,
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
return
|
|
246
|
+
|
|
247
|
+
finalDF.replace(
|
|
248
|
+
{"Warning": icons.warning, "Error": icons.error, "Info": icons.info},
|
|
249
|
+
inplace=True,
|
|
250
|
+
)
|
|
251
|
+
|
|
252
|
+
pd.set_option("display.max_colwidth", 100)
|
|
253
|
+
|
|
254
|
+
finalDF = (
|
|
255
|
+
finalDF[
|
|
256
|
+
[
|
|
257
|
+
"Category",
|
|
258
|
+
"Rule Name",
|
|
259
|
+
"Object Type",
|
|
260
|
+
"Object Name",
|
|
261
|
+
"Severity",
|
|
262
|
+
"Description",
|
|
263
|
+
"URL",
|
|
264
|
+
# "Report URL",
|
|
265
|
+
]
|
|
266
|
+
]
|
|
267
|
+
.sort_values(["Category", "Rule Name", "Object Type", "Object Name"])
|
|
268
|
+
.set_index(["Category", "Rule Name"])
|
|
269
|
+
)
|
|
270
|
+
|
|
271
|
+
bpa2 = finalDF.reset_index()
|
|
272
|
+
bpa_dict = {
|
|
273
|
+
cat: bpa2[bpa2["Category"] == cat].drop("Category", axis=1)
|
|
274
|
+
for cat in bpa2["Category"].drop_duplicates().values
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
styles = """
|
|
278
|
+
<style>
|
|
279
|
+
.tab { overflow: hidden; border: 1px solid #ccc; background-color: #f1f1f1; }
|
|
280
|
+
.tab button { background-color: inherit; float: left; border: none; outline: none; cursor: pointer; padding: 14px 16px; transition: 0.3s; }
|
|
281
|
+
.tab button:hover { background-color: #ddd; }
|
|
282
|
+
.tab button.active { background-color: #ccc; }
|
|
283
|
+
.tabcontent { display: none; padding: 6px 12px; border: 1px solid #ccc; border-top: none; }
|
|
284
|
+
.tabcontent.active { display: block; }
|
|
285
|
+
.tooltip { position: relative; display: inline-block; }
|
|
286
|
+
.tooltip .tooltiptext { visibility: hidden; width: 300px; background-color: #555; color: #fff; text-align: center; border-radius: 6px; padding: 5px; position: absolute; z-index: 1; bottom: 125%; left: 50%; margin-left: -110px; opacity: 0; transition: opacity 0.3s; }
|
|
287
|
+
.tooltip:hover .tooltiptext { visibility: visible; opacity: 1; }
|
|
288
|
+
</style>
|
|
289
|
+
"""
|
|
290
|
+
|
|
291
|
+
# JavaScript for tab functionality
|
|
292
|
+
script = """
|
|
293
|
+
<script>
|
|
294
|
+
function openTab(evt, tabName) {
|
|
295
|
+
var i, tabcontent, tablinks;
|
|
296
|
+
tabcontent = document.getElementsByClassName("tabcontent");
|
|
297
|
+
for (i = 0; i < tabcontent.length; i++) {
|
|
298
|
+
tabcontent[i].style.display = "none";
|
|
299
|
+
}
|
|
300
|
+
tablinks = document.getElementsByClassName("tablinks");
|
|
301
|
+
for (i = 0; i < tablinks.length; i++) {
|
|
302
|
+
tablinks[i].className = tablinks[i].className.replace(" active", "");
|
|
303
|
+
}
|
|
304
|
+
document.getElementById(tabName).style.display = "block";
|
|
305
|
+
evt.currentTarget.className += " active";
|
|
306
|
+
}
|
|
307
|
+
</script>
|
|
308
|
+
"""
|
|
309
|
+
|
|
310
|
+
# HTML for tabs
|
|
311
|
+
tab_html = '<div class="tab">'
|
|
312
|
+
content_html = ""
|
|
313
|
+
for i, (title, df) in enumerate(bpa_dict.items()):
|
|
314
|
+
if df.shape[0] == 0:
|
|
315
|
+
continue
|
|
316
|
+
|
|
317
|
+
tab_id = f"tab{i}"
|
|
318
|
+
active_class = ""
|
|
319
|
+
if i == 0:
|
|
320
|
+
active_class = "active"
|
|
321
|
+
|
|
322
|
+
summary = " + ".join(
|
|
323
|
+
[f"{idx} ({v})" for idx, v in df["Severity"].value_counts().items()]
|
|
324
|
+
)
|
|
325
|
+
tab_html += f'<button class="tablinks {active_class}" onclick="openTab(event, \'{tab_id}\')"><b>{title}</b><br/>{summary}</button>'
|
|
326
|
+
content_html += f'<div id="{tab_id}" class="tabcontent {active_class}">'
|
|
327
|
+
|
|
328
|
+
# Adding tooltip for Rule Name using Description column
|
|
329
|
+
content_html += '<table border="1">'
|
|
330
|
+
content_html += "<tr><th>Rule Name</th><th>Object Type</th><th>Object Name</th><th>Severity</th></tr>"
|
|
331
|
+
for _, row in df.iterrows():
|
|
332
|
+
content_html += "<tr>"
|
|
333
|
+
if pd.notnull(row["URL"]):
|
|
334
|
+
content_html += f'<td class="tooltip" onmouseover="adjustTooltipPosition(event)"><a href="{row["URL"]}">{row["Rule Name"]}</a><span class="tooltiptext">{row["Description"]}</span></td>'
|
|
335
|
+
elif pd.notnull(row["Description"]):
|
|
336
|
+
content_html += f'<td class="tooltip" onmouseover="adjustTooltipPosition(event)">{row["Rule Name"]}<span class="tooltiptext">{row["Description"]}</span></td>'
|
|
337
|
+
else:
|
|
338
|
+
content_html += f'<td>{row["Rule Name"]}</td>'
|
|
339
|
+
content_html += f'<td>{row["Object Type"]}</td>'
|
|
340
|
+
if pd.notnull(row["URL"]):
|
|
341
|
+
content_html += (
|
|
342
|
+
f'<td><a href="{row["URL"]}">{row["Object Name"]}</a></td>'
|
|
343
|
+
)
|
|
344
|
+
else:
|
|
345
|
+
content_html += f'<td>{row["Object Name"]}</td>'
|
|
346
|
+
content_html += f'<td style="text-align: center;">{row["Severity"]}</td>'
|
|
347
|
+
content_html += "</tr>"
|
|
348
|
+
content_html += "</table>"
|
|
349
|
+
|
|
350
|
+
content_html += "</div>"
|
|
351
|
+
tab_html += "</div>"
|
|
352
|
+
|
|
353
|
+
# Display the tabs, tab contents, and run the script
|
|
354
|
+
return display(HTML(styles + tab_html + content_html + script))
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import pandas as pd
|
|
2
|
+
from sempy._utils._log import log
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
@log
|
|
6
|
+
def report_bpa_rules() -> pd.DataFrame:
|
|
7
|
+
"""
|
|
8
|
+
Shows the default rules for the report BPA used by the run_report_bpa function.
|
|
9
|
+
|
|
10
|
+
Returns
|
|
11
|
+
-------
|
|
12
|
+
pandas.DataFrame
|
|
13
|
+
A pandas dataframe containing the default rules for the run_report_bpa function.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
rules = pd.DataFrame(
|
|
17
|
+
[
|
|
18
|
+
(
|
|
19
|
+
"Error Prevention",
|
|
20
|
+
"Semantic Model",
|
|
21
|
+
"Error",
|
|
22
|
+
"Fix report objects which reference invalid semantic model objects",
|
|
23
|
+
lambda df: df["Valid Semantic Model Object"] == False,
|
|
24
|
+
"This rule highlights visuals, report filters, page filters or visual filters which reference an invalid semantic model object (i.e Measure/Column/Hierarchy).",
|
|
25
|
+
),
|
|
26
|
+
(
|
|
27
|
+
"Performance",
|
|
28
|
+
"Custom Visual",
|
|
29
|
+
"Warning",
|
|
30
|
+
"Remove custom visuals which are not used in the report",
|
|
31
|
+
lambda df: df["Used in Report"] == False,
|
|
32
|
+
"Removing unused custom visuals from a report may lead to faster report performance.",
|
|
33
|
+
),
|
|
34
|
+
(
|
|
35
|
+
"Performance",
|
|
36
|
+
"Page",
|
|
37
|
+
"Warning",
|
|
38
|
+
"Reduce the number of visible visuals on the page",
|
|
39
|
+
lambda df: df["Visible Visual Count"] > 15,
|
|
40
|
+
'Reducing the number of visable visuals on a page will lead to faster report performance. This rule flags pages with over " + visVisuals + " visible visuals.',
|
|
41
|
+
),
|
|
42
|
+
(
|
|
43
|
+
"Performance",
|
|
44
|
+
"Visual",
|
|
45
|
+
"Warning",
|
|
46
|
+
"Reduce the number of objects within visuals",
|
|
47
|
+
lambda df: df["Visual Object Count"] > 5,
|
|
48
|
+
"Reducing the number of objects (i.e. measures, columns) which are used in a visual will lead to faster report performance.",
|
|
49
|
+
),
|
|
50
|
+
(
|
|
51
|
+
"Performance",
|
|
52
|
+
["Report Filter", "Page Filter", "Visual Filter"],
|
|
53
|
+
"Warning",
|
|
54
|
+
"Reduce usage of filters on measures",
|
|
55
|
+
lambda df: df["Object Type"] == "Measure",
|
|
56
|
+
"Measure filters may cause performance degradation, especially against a large semantic model.",
|
|
57
|
+
),
|
|
58
|
+
(
|
|
59
|
+
"Performance",
|
|
60
|
+
"Visual",
|
|
61
|
+
"Warning",
|
|
62
|
+
"Avoid setting 'Show items with no data' on columns",
|
|
63
|
+
lambda df: df["Show Items With No Data"],
|
|
64
|
+
"This setting will show all column values for all columns in the visual which may lead to performance degradation.",
|
|
65
|
+
"https://learn.microsoft.com/power-bi/create-reports/desktop-show-items-no-data",
|
|
66
|
+
),
|
|
67
|
+
(
|
|
68
|
+
"Performance",
|
|
69
|
+
"Page",
|
|
70
|
+
"Warning",
|
|
71
|
+
"Avoid tall report pages with vertical scrolling",
|
|
72
|
+
lambda df: df["Height"] > 720,
|
|
73
|
+
"Report pages are designed to be in a single view and not scroll. Pages with scrolling is an indicator that the page has too many elements.",
|
|
74
|
+
),
|
|
75
|
+
(
|
|
76
|
+
"Performance",
|
|
77
|
+
"Custom Visual",
|
|
78
|
+
"Info",
|
|
79
|
+
"Reduce usage of custom visuals",
|
|
80
|
+
lambda df: df["Custom Visual Name"] == df["Custom Visual Name"],
|
|
81
|
+
"Using custom visuals may lead to performance degradation.",
|
|
82
|
+
),
|
|
83
|
+
(
|
|
84
|
+
"Maintenance",
|
|
85
|
+
"Report Level Measure",
|
|
86
|
+
"Info",
|
|
87
|
+
"Move report-level measures into the semantic model.",
|
|
88
|
+
lambda df: df["Measure Name"] == df["Measure Name"],
|
|
89
|
+
"It is a best practice to keep measures defined in the semantic model and not in the report.",
|
|
90
|
+
),
|
|
91
|
+
(
|
|
92
|
+
"Performance",
|
|
93
|
+
["Report Filter", "Page Filter", "Visual Filter"],
|
|
94
|
+
"Info",
|
|
95
|
+
"Reduce usage of TopN filtering within visuals",
|
|
96
|
+
lambda df: df["Type"] == "TopN",
|
|
97
|
+
"TopN filtering may cause performance degradation, especially against a high cardinality column.",
|
|
98
|
+
),
|
|
99
|
+
# ('Performance', 'Custom Visual', 'Warning', "Set 'Edit Interactions' for non-data visuals to 'none'",
|
|
100
|
+
# lambda df: df['Custom Visual Name'] == df['Custom Visual Name'],
|
|
101
|
+
# "Setting 'Edit Interactions' to 'None' for non-data visuals may improve performance (since these visuals do not necessitate interactions between other visuals). 'Edit Interactions' may be found in the 'Format' tab of the ribbon in Power BI Desktop.",
|
|
102
|
+
# )
|
|
103
|
+
],
|
|
104
|
+
columns=[
|
|
105
|
+
"Category",
|
|
106
|
+
"Scope",
|
|
107
|
+
"Severity",
|
|
108
|
+
"Rule Name",
|
|
109
|
+
"Expression",
|
|
110
|
+
"Description",
|
|
111
|
+
"URL",
|
|
112
|
+
],
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
return rules
|