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,120 @@
|
|
|
1
|
+
import sempy.fabric as fabric
|
|
2
|
+
import pandas as pd
|
|
3
|
+
from .._helper_functions import (
|
|
4
|
+
format_dax_object_name,
|
|
5
|
+
resolve_workspace_name_and_id,
|
|
6
|
+
resolve_dataset_name_and_id,
|
|
7
|
+
resolve_workspace_name,
|
|
8
|
+
)
|
|
9
|
+
from IPython.display import display
|
|
10
|
+
from ..lakehouse import get_lakehouse_columns
|
|
11
|
+
from ..directlake._dl_helper import get_direct_lake_source
|
|
12
|
+
from typing import Optional
|
|
13
|
+
import sempy_labs._icons as icons
|
|
14
|
+
from sempy._utils._log import log
|
|
15
|
+
from uuid import UUID
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@log
|
|
19
|
+
def direct_lake_schema_compare(
|
|
20
|
+
dataset: str | UUID,
|
|
21
|
+
workspace: Optional[str | UUID] = None,
|
|
22
|
+
**kwargs,
|
|
23
|
+
):
|
|
24
|
+
"""
|
|
25
|
+
Checks that all the tables in a Direct Lake semantic model map to tables in their corresponding lakehouse and that the columns in each table exist.
|
|
26
|
+
|
|
27
|
+
Parameters
|
|
28
|
+
----------
|
|
29
|
+
dataset : str | uuid.UUID
|
|
30
|
+
Name or ID of the semantic model.
|
|
31
|
+
workspace : str | uuid.UUID, default=None
|
|
32
|
+
The Fabric workspace name or ID.
|
|
33
|
+
Defaults to None which resolves to the workspace of the attached lakehouse
|
|
34
|
+
or if no lakehouse attached, resolves to the workspace of the notebook.
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
if "lakehouse" in kwargs:
|
|
38
|
+
print(
|
|
39
|
+
"The 'lakehouse' parameter has been deprecated as it is no longer necessary. Please remove this parameter from the function going forward."
|
|
40
|
+
)
|
|
41
|
+
del kwargs["lakehouse"]
|
|
42
|
+
if "lakehouse_workspace" in kwargs:
|
|
43
|
+
print(
|
|
44
|
+
"The 'lakehouse_workspace' parameter has been deprecated as it is no longer necessary. Please remove this parameter from the function going forward."
|
|
45
|
+
)
|
|
46
|
+
del kwargs["lakehouse_workspace"]
|
|
47
|
+
|
|
48
|
+
fabric.refresh_tom_cache(workspace=workspace)
|
|
49
|
+
|
|
50
|
+
(workspace_name, workspace_id) = resolve_workspace_name_and_id(workspace)
|
|
51
|
+
(dataset_name, dataset_id) = resolve_dataset_name_and_id(dataset, workspace_id)
|
|
52
|
+
|
|
53
|
+
artifact_type, lakehouse_name, lakehouse_id, lakehouse_workspace_id = (
|
|
54
|
+
get_direct_lake_source(dataset=dataset_id, workspace=workspace_id)
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
if artifact_type == "Warehouse":
|
|
58
|
+
raise ValueError(
|
|
59
|
+
f"{icons.red_dot} This function is only valid for Direct Lake semantic models which source from Fabric lakehouses (not warehouses)."
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
dfP = fabric.list_partitions(dataset=dataset_id, workspace=workspace_id)
|
|
63
|
+
|
|
64
|
+
if not any(r["Mode"] == "DirectLake" for _, r in dfP.iterrows()):
|
|
65
|
+
raise ValueError(
|
|
66
|
+
f"{icons.red_dot} The '{dataset_name}' semantic model within the '{workspace_name}' workspace is not in Direct Lake mode."
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
if artifact_type is None:
|
|
70
|
+
raise ValueError(
|
|
71
|
+
f"{icons.red_dot} This function only supports Direct Lake semantic models where the source lakehouse resides in the same workpace as the semantic model."
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
lakehouse_workspace = resolve_workspace_name(workspace_id=lakehouse_workspace_id)
|
|
75
|
+
dfT = fabric.list_tables(dataset=dataset_id, workspace=workspace_id)
|
|
76
|
+
dfC = fabric.list_columns(dataset=dataset_id, workspace=workspace_id)
|
|
77
|
+
lc = get_lakehouse_columns(lakehouse_name, lakehouse_workspace)
|
|
78
|
+
|
|
79
|
+
dfT.rename(columns={"Type": "Table Type"}, inplace=True)
|
|
80
|
+
dfP_filt = dfP[dfP["Mode"] == "DirectLake"]
|
|
81
|
+
dfC = pd.merge(dfC, dfP[["Table Name", "Query"]], on="Table Name", how="inner")
|
|
82
|
+
dfC = pd.merge(
|
|
83
|
+
dfC,
|
|
84
|
+
dfT[["Name", "Table Type"]],
|
|
85
|
+
left_on="Table Name",
|
|
86
|
+
right_on="Name",
|
|
87
|
+
how="inner",
|
|
88
|
+
)
|
|
89
|
+
dfC["Full Column Name"] = format_dax_object_name(dfC["Query"], dfC["Source"])
|
|
90
|
+
dfC_filt = dfC[dfC["Table Type"] == "Table"]
|
|
91
|
+
# Schema compare
|
|
92
|
+
missingtbls = dfP_filt[~dfP_filt["Query"].isin(lc["Table Name"])]
|
|
93
|
+
missingtbls = missingtbls[["Table Name", "Query"]]
|
|
94
|
+
missingtbls.rename(columns={"Query": "Source Table"}, inplace=True)
|
|
95
|
+
missingcols = dfC_filt[~dfC_filt["Full Column Name"].isin(lc["Full Column Name"])]
|
|
96
|
+
missingcols = missingcols[
|
|
97
|
+
["Table Name", "Column Name", "Type", "Data Type", "Source"]
|
|
98
|
+
]
|
|
99
|
+
missingcols.rename(columns={"Source": "Source Column"}, inplace=True)
|
|
100
|
+
|
|
101
|
+
if len(missingtbls) == 0:
|
|
102
|
+
print(
|
|
103
|
+
f"{icons.green_dot} All tables exist in the '{lakehouse_name}' lakehouse within the '{lakehouse_workspace}' workspace."
|
|
104
|
+
)
|
|
105
|
+
else:
|
|
106
|
+
print(
|
|
107
|
+
f"{icons.yellow_dot} The following tables exist in the '{dataset_name}' semantic model within the '{workspace_name}' workspace"
|
|
108
|
+
f" but do not exist in the '{lakehouse_name}' lakehouse within the '{lakehouse_workspace}' workspace."
|
|
109
|
+
)
|
|
110
|
+
display(missingtbls)
|
|
111
|
+
if len(missingcols) == 0:
|
|
112
|
+
print(
|
|
113
|
+
f"{icons.green_dot} All columns exist in the '{lakehouse_name}' lakehouse within the '{lakehouse_workspace}' workspace."
|
|
114
|
+
)
|
|
115
|
+
else:
|
|
116
|
+
print(
|
|
117
|
+
f"{icons.yellow_dot} The following columns exist in the '{dataset_name}' semantic model within the '{workspace_name}' workspace "
|
|
118
|
+
f"but do not exist in the '{lakehouse_name}' lakehouse within the '{lakehouse_workspace}' workspace."
|
|
119
|
+
)
|
|
120
|
+
display(missingcols)
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import sempy
|
|
2
|
+
import pandas as pd
|
|
3
|
+
from ..lakehouse import get_lakehouse_columns
|
|
4
|
+
from ._dl_helper import get_direct_lake_source
|
|
5
|
+
from ..tom import connect_semantic_model
|
|
6
|
+
from .._helper_functions import (
|
|
7
|
+
_convert_data_type,
|
|
8
|
+
resolve_workspace_name_and_id,
|
|
9
|
+
resolve_dataset_name_and_id,
|
|
10
|
+
)
|
|
11
|
+
from typing import Optional
|
|
12
|
+
from sempy._utils._log import log
|
|
13
|
+
import sempy_labs._icons as icons
|
|
14
|
+
from uuid import UUID
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@log
|
|
18
|
+
def direct_lake_schema_sync(
|
|
19
|
+
dataset: str | UUID,
|
|
20
|
+
workspace: Optional[str | UUID] = None,
|
|
21
|
+
add_to_model: bool = False,
|
|
22
|
+
remove_from_model: bool = False,
|
|
23
|
+
) -> pd.DataFrame:
|
|
24
|
+
"""
|
|
25
|
+
Shows/adds columns which exist in the lakehouse but do not exist in the semantic model (only for tables in the semantic model).
|
|
26
|
+
|
|
27
|
+
Parameters
|
|
28
|
+
----------
|
|
29
|
+
dataset : str | uuid.UUID
|
|
30
|
+
Name or ID of the semantic model.
|
|
31
|
+
workspace : str | uuid.UUID, default=None
|
|
32
|
+
The Fabric workspace name or ID.
|
|
33
|
+
Defaults to None which resolves to the workspace of the attached lakehouse
|
|
34
|
+
or if no lakehouse attached, resolves to the workspace of the notebook.
|
|
35
|
+
add_to_model : bool, default=False
|
|
36
|
+
If set to True, columns which exist in the lakehouse but do not exist in the semantic model are added to the semantic model. No new tables are added.
|
|
37
|
+
remove_from_model : bool, default=False
|
|
38
|
+
If set to True, columns which exist in the semantic model but do not exist in the lakehouse are removed from the semantic model. No new tables are removed.
|
|
39
|
+
|
|
40
|
+
Returns
|
|
41
|
+
-------
|
|
42
|
+
pandas.DataFrame
|
|
43
|
+
A pandas dataframe showing the status of columns in the semantic model and lakehouse (prior to adding/removing them from the model using this function).
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
sempy.fabric._client._utils._init_analysis_services()
|
|
47
|
+
import Microsoft.AnalysisServices.Tabular as TOM
|
|
48
|
+
|
|
49
|
+
(workspace_name, workspace_id) = resolve_workspace_name_and_id(workspace)
|
|
50
|
+
(dataset_name, dataset_id) = resolve_dataset_name_and_id(dataset, workspace_id)
|
|
51
|
+
|
|
52
|
+
artifact_type, lakehouse_name, lakehouse_id, lakehouse_workspace_id = (
|
|
53
|
+
get_direct_lake_source(dataset=dataset_id, workspace=workspace_id)
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
if artifact_type == "Warehouse":
|
|
57
|
+
raise ValueError(
|
|
58
|
+
f"{icons.red_dot} This function is only valid for Direct Lake semantic models which source from Fabric lakehouses (not warehouses)."
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
if artifact_type is None:
|
|
62
|
+
raise ValueError(
|
|
63
|
+
f"{icons.red_dot} This function only supports Direct Lake semantic models where the source lakehouse resides in the same workpace as the semantic model."
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
lc = get_lakehouse_columns(lakehouse_id, lakehouse_workspace_id)
|
|
67
|
+
|
|
68
|
+
readonly = True
|
|
69
|
+
if add_to_model or remove_from_model:
|
|
70
|
+
readonly = False
|
|
71
|
+
df = pd.DataFrame(
|
|
72
|
+
columns=[
|
|
73
|
+
"TableName",
|
|
74
|
+
"ColumnName",
|
|
75
|
+
"SourceTableName",
|
|
76
|
+
"SourceColumnName",
|
|
77
|
+
"Status",
|
|
78
|
+
]
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
with connect_semantic_model(
|
|
82
|
+
dataset=dataset_id, readonly=readonly, workspace=workspace_id
|
|
83
|
+
) as tom:
|
|
84
|
+
# Check if the columns in the semantic model exist in the lakehouse
|
|
85
|
+
for c in tom.all_columns():
|
|
86
|
+
column_name = c.Name
|
|
87
|
+
table_name = c.Parent.Name
|
|
88
|
+
partition_name = next(p.Name for p in c.Table.Partitions)
|
|
89
|
+
p = c.Table.Partitions[partition_name]
|
|
90
|
+
if p.SourceType == TOM.PartitionSourceType.Entity:
|
|
91
|
+
entity_name = p.Source.EntityName
|
|
92
|
+
source_column = c.SourceColumn
|
|
93
|
+
lc_filt = lc[
|
|
94
|
+
(lc["Table Name"] == entity_name)
|
|
95
|
+
& (lc["Column Name"] == source_column)
|
|
96
|
+
]
|
|
97
|
+
# Remove column from model if it doesn't exist in the lakehouse
|
|
98
|
+
if lc_filt.empty:
|
|
99
|
+
new_data = {
|
|
100
|
+
"TableName": table_name,
|
|
101
|
+
"ColumnName": column_name,
|
|
102
|
+
"SourceTableName": entity_name,
|
|
103
|
+
"SourceColumnName": source_column,
|
|
104
|
+
"Status": "Not in lakehouse",
|
|
105
|
+
}
|
|
106
|
+
df = pd.concat(
|
|
107
|
+
[df, pd.DataFrame(new_data, index=[0])], ignore_index=True
|
|
108
|
+
)
|
|
109
|
+
if remove_from_model:
|
|
110
|
+
tom.remove_object(object=c)
|
|
111
|
+
print(
|
|
112
|
+
f"{icons.green_dot} The '{table_name}'[{column_name}] column has been removed from the '{dataset_name}' semantic model within the '{workspace_name}' workspace."
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
# Check if the lakehouse columns exist in the semantic model
|
|
116
|
+
for i, r in lc.iterrows():
|
|
117
|
+
lakeTName = r["Table Name"]
|
|
118
|
+
lakeCName = r["Column Name"]
|
|
119
|
+
dType = r["Data Type"]
|
|
120
|
+
|
|
121
|
+
if any(
|
|
122
|
+
p.Source.EntityName == lakeTName
|
|
123
|
+
for p in tom.all_partitions()
|
|
124
|
+
if p.SourceType == TOM.PartitionSourceType.Entity
|
|
125
|
+
):
|
|
126
|
+
table_name = next(
|
|
127
|
+
t.Name
|
|
128
|
+
for t in tom.model.Tables
|
|
129
|
+
for p in t.Partitions
|
|
130
|
+
if p.SourceType == TOM.PartitionSourceType.Entity
|
|
131
|
+
and p.Source.EntityName == lakeTName
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
if not any(
|
|
135
|
+
c.SourceColumn == lakeCName and c.Parent.Name == table_name
|
|
136
|
+
for c in tom.all_columns()
|
|
137
|
+
):
|
|
138
|
+
new_data = {
|
|
139
|
+
"TableName": table_name,
|
|
140
|
+
"ColumnName": None,
|
|
141
|
+
"SourceTableName": lakeTName,
|
|
142
|
+
"SourceColumnName": lakeCName,
|
|
143
|
+
"Status": "Not in semantic model",
|
|
144
|
+
}
|
|
145
|
+
df = pd.concat(
|
|
146
|
+
[df, pd.DataFrame(new_data, index=[0])], ignore_index=True
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
if add_to_model:
|
|
150
|
+
dt = _convert_data_type(dType)
|
|
151
|
+
tom.add_data_column(
|
|
152
|
+
table_name=table_name,
|
|
153
|
+
column_name=lakeCName,
|
|
154
|
+
source_column=lakeCName,
|
|
155
|
+
data_type=dt,
|
|
156
|
+
)
|
|
157
|
+
print(
|
|
158
|
+
f"{icons.green_dot} The '{lakeCName}' column in the '{lakeTName}' lakehouse table was added to the '{dataset_name}' semantic model within the '{workspace_name}' workspace."
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
return df
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
import sempy.fabric as fabric
|
|
2
|
+
import numpy as np
|
|
3
|
+
import pandas as pd
|
|
4
|
+
from typing import Optional, List, Union, Tuple
|
|
5
|
+
from uuid import UUID
|
|
6
|
+
import sempy_labs._icons as icons
|
|
7
|
+
from sempy._utils._log import log
|
|
8
|
+
from .._helper_functions import (
|
|
9
|
+
retry,
|
|
10
|
+
_convert_data_type,
|
|
11
|
+
resolve_dataset_name_and_id,
|
|
12
|
+
resolve_workspace_name_and_id,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@log
|
|
17
|
+
def check_fallback_reason(
|
|
18
|
+
dataset: str | UUID, workspace: Optional[str | UUID] = None
|
|
19
|
+
) -> pd.DataFrame:
|
|
20
|
+
"""
|
|
21
|
+
Shows the reason a table in a Direct Lake semantic model would fallback to DirectQuery.
|
|
22
|
+
|
|
23
|
+
Parameters
|
|
24
|
+
----------
|
|
25
|
+
dataset : str | uuid.UUID
|
|
26
|
+
Name or ID of the semantic model.
|
|
27
|
+
workspace : str | uuid.UUID, default=None
|
|
28
|
+
The Fabric workspace name or ID.
|
|
29
|
+
Defaults to None which resolves to the workspace of the attached lakehouse
|
|
30
|
+
or if no lakehouse attached, resolves to the workspace of the notebook.
|
|
31
|
+
|
|
32
|
+
Returns
|
|
33
|
+
-------
|
|
34
|
+
pandas.DataFrame
|
|
35
|
+
The tables in the semantic model and their fallback reason.
|
|
36
|
+
"""
|
|
37
|
+
from sempy_labs.tom import connect_semantic_model
|
|
38
|
+
|
|
39
|
+
(workspace_name, workspace_id) = resolve_workspace_name_and_id(workspace)
|
|
40
|
+
(dataset_name, dataset_id) = resolve_dataset_name_and_id(
|
|
41
|
+
dataset, workspace=workspace_id
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
with connect_semantic_model(
|
|
45
|
+
dataset=dataset_id, workspace=workspace_id, readonly=True
|
|
46
|
+
) as tom:
|
|
47
|
+
if not tom.is_direct_lake():
|
|
48
|
+
raise ValueError(
|
|
49
|
+
f"{icons.red_dot} The '{dataset_name}' semantic model is not in Direct Lake. This function is only applicable to Direct Lake semantic models."
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
df = fabric.evaluate_dax(
|
|
53
|
+
dataset=dataset_id,
|
|
54
|
+
workspace=workspace_id,
|
|
55
|
+
dax_string="""
|
|
56
|
+
SELECT [TableName] AS [Table Name],[FallbackReason] AS [FallbackReasonID]
|
|
57
|
+
FROM $SYSTEM.TMSCHEMA_DELTA_TABLE_METADATA_STORAGES
|
|
58
|
+
""",
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
value_mapping = {
|
|
62
|
+
0: "No reason for fallback",
|
|
63
|
+
1: "This table is not framed",
|
|
64
|
+
2: "This object is a view in the lakehouse",
|
|
65
|
+
3: "The table does not exist in the lakehouse",
|
|
66
|
+
4: "Transient error",
|
|
67
|
+
5: "Using OLS will result in fallback to DQ",
|
|
68
|
+
6: "Using RLS will result in fallback to DQ",
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
# Create a new column based on the mapping
|
|
72
|
+
df["Fallback Reason Detail"] = np.vectorize(value_mapping.get)(
|
|
73
|
+
df["FallbackReasonID"]
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
return df
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
@log
|
|
80
|
+
def generate_direct_lake_semantic_model(
|
|
81
|
+
dataset: str,
|
|
82
|
+
lakehouse_tables: Union[str, List[str]],
|
|
83
|
+
workspace: Optional[str | UUID] = None,
|
|
84
|
+
lakehouse: Optional[str] = None,
|
|
85
|
+
lakehouse_workspace: Optional[str | UUID] = None,
|
|
86
|
+
schema: str = "dbo",
|
|
87
|
+
overwrite: bool = False,
|
|
88
|
+
refresh: bool = True,
|
|
89
|
+
):
|
|
90
|
+
"""
|
|
91
|
+
Dynamically generates a Direct Lake semantic model based on tables in a Fabric lakehouse.
|
|
92
|
+
|
|
93
|
+
Parameters
|
|
94
|
+
----------
|
|
95
|
+
dataset : str
|
|
96
|
+
Name of the semantic model to be created.
|
|
97
|
+
lakehouse_tables : str | List[str]
|
|
98
|
+
The table(s) within the Fabric lakehouse to add to the semantic model. All columns from these tables will be added to the semantic model.
|
|
99
|
+
workspace : str | uuid.UUID, default=None
|
|
100
|
+
The Fabric workspace name or ID in which the semantic model will reside.
|
|
101
|
+
Defaults to None which resolves to the workspace of the attached lakehouse
|
|
102
|
+
or if no lakehouse attached, resolves to the workspace of the notebook.
|
|
103
|
+
lakehouse : str, default=None
|
|
104
|
+
The lakehouse which stores the delta tables which will feed the Direct Lake semantic model.
|
|
105
|
+
Defaults to None which resolves to the attached lakehouse.
|
|
106
|
+
lakehouse_workspace : str | uuid.UUID, default=None
|
|
107
|
+
The Fabric workspace name or ID in which the lakehouse resides.
|
|
108
|
+
Defaults to None which resolves to the workspace of the attached lakehouse
|
|
109
|
+
or if no lakehouse attached, resolves to the workspace of the notebook.
|
|
110
|
+
schema : str, default="dbo"
|
|
111
|
+
The schema used for the lakehouse.
|
|
112
|
+
overwrite : bool, default=False
|
|
113
|
+
If set to True, overwrites the existing semantic model if it already exists.
|
|
114
|
+
refresh: bool, default=True
|
|
115
|
+
If True, refreshes the newly created semantic model after it is created.
|
|
116
|
+
"""
|
|
117
|
+
|
|
118
|
+
from sempy_labs.lakehouse import get_lakehouse_tables, get_lakehouse_columns
|
|
119
|
+
from sempy_labs.directlake._generate_shared_expression import (
|
|
120
|
+
generate_shared_expression,
|
|
121
|
+
)
|
|
122
|
+
from sempy_labs.tom import connect_semantic_model
|
|
123
|
+
from sempy_labs._generate_semantic_model import create_blank_semantic_model
|
|
124
|
+
from sempy_labs._refresh_semantic_model import refresh_semantic_model
|
|
125
|
+
|
|
126
|
+
if isinstance(lakehouse_tables, str):
|
|
127
|
+
lakehouse_tables = [lakehouse_tables]
|
|
128
|
+
|
|
129
|
+
(workspace_name, workspace_id) = resolve_workspace_name_and_id(workspace)
|
|
130
|
+
if lakehouse_workspace is None:
|
|
131
|
+
lakehouse_workspace = workspace
|
|
132
|
+
|
|
133
|
+
dfLT = get_lakehouse_tables(lakehouse=lakehouse, workspace=lakehouse_workspace)
|
|
134
|
+
|
|
135
|
+
icons.sll_tags.append("GenerateDLModel")
|
|
136
|
+
|
|
137
|
+
# Validate lakehouse tables
|
|
138
|
+
for t in lakehouse_tables:
|
|
139
|
+
if t not in dfLT["Table Name"].values:
|
|
140
|
+
raise ValueError(
|
|
141
|
+
f"{icons.red_dot} The '{t}' table does not exist as a delta table in the '{lakehouse}' within the '{workspace_name}' workspace."
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
dfLC = get_lakehouse_columns(lakehouse=lakehouse, workspace=lakehouse_workspace)
|
|
145
|
+
expr = generate_shared_expression(
|
|
146
|
+
item_name=lakehouse, item_type="Lakehouse", workspace=lakehouse_workspace
|
|
147
|
+
)
|
|
148
|
+
dfD = fabric.list_datasets(workspace=workspace_id, mode="rest")
|
|
149
|
+
dfD_filt = dfD[dfD["Dataset Name"] == dataset]
|
|
150
|
+
|
|
151
|
+
if len(dfD_filt) > 0 and not overwrite:
|
|
152
|
+
raise ValueError(
|
|
153
|
+
f"{icons.red_dot} The '{dataset}' semantic model within the '{workspace_name}' workspace already exists. Overwrite is set to False so the new semantic model has not been created."
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
create_blank_semantic_model(
|
|
157
|
+
dataset=dataset, workspace=workspace_id, overwrite=overwrite
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
@retry(
|
|
161
|
+
sleep_time=1,
|
|
162
|
+
timeout_error_message=f"{icons.red_dot} Function timed out after 1 minute",
|
|
163
|
+
)
|
|
164
|
+
def dyn_connect():
|
|
165
|
+
with connect_semantic_model(
|
|
166
|
+
dataset=dataset, readonly=True, workspace=workspace_id
|
|
167
|
+
) as tom:
|
|
168
|
+
|
|
169
|
+
tom.model
|
|
170
|
+
|
|
171
|
+
dyn_connect()
|
|
172
|
+
|
|
173
|
+
expression_name = "DatabaseQuery"
|
|
174
|
+
with connect_semantic_model(
|
|
175
|
+
dataset=dataset, workspace=workspace_id, readonly=False
|
|
176
|
+
) as tom:
|
|
177
|
+
if not any(e.Name == expression_name for e in tom.model.Expressions):
|
|
178
|
+
tom.add_expression(name=expression_name, expression=expr)
|
|
179
|
+
|
|
180
|
+
for t in lakehouse_tables:
|
|
181
|
+
tom.add_table(name=t)
|
|
182
|
+
tom.add_entity_partition(table_name=t, entity_name=t, schema_name=schema)
|
|
183
|
+
dfLC_filt = dfLC[dfLC["Table Name"] == t]
|
|
184
|
+
for i, r in dfLC_filt.iterrows():
|
|
185
|
+
lakeCName = r["Column Name"]
|
|
186
|
+
dType = r["Data Type"]
|
|
187
|
+
dt = _convert_data_type(dType)
|
|
188
|
+
tom.add_data_column(
|
|
189
|
+
table_name=t,
|
|
190
|
+
column_name=lakeCName,
|
|
191
|
+
source_column=lakeCName,
|
|
192
|
+
data_type=dt,
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
if refresh:
|
|
196
|
+
refresh_semantic_model(dataset=dataset, workspace=workspace_id)
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
@log
|
|
200
|
+
def get_direct_lake_source(
|
|
201
|
+
dataset: str | UUID, workspace: Optional[str | UUID] = None
|
|
202
|
+
) -> Tuple[str, str, UUID, UUID]:
|
|
203
|
+
"""
|
|
204
|
+
Obtains the source information for a direct lake semantic model (if the source is located in the same workspace as the semantic model).
|
|
205
|
+
|
|
206
|
+
Parameters
|
|
207
|
+
----------
|
|
208
|
+
dataset : str | uuid.UUID
|
|
209
|
+
The name or ID of the semantic model.
|
|
210
|
+
workspace : str | uuid.UUID, default=None
|
|
211
|
+
The Fabric workspace name or ID.
|
|
212
|
+
Defaults to None which resolves to the workspace of the attached lakehouse
|
|
213
|
+
or if no lakehouse attached, resolves to the workspace of the notebook.
|
|
214
|
+
|
|
215
|
+
Returns
|
|
216
|
+
-------
|
|
217
|
+
Tuple[str, str, UUID, UUID]
|
|
218
|
+
If the source of the direct lake semantic model is a lakehouse this will return: 'Lakehouse', Lakehouse Name, SQL Endpoint Id, Workspace Id
|
|
219
|
+
If the source of the direct lake semantic model is a warehouse this will return: 'Warehouse', Warehouse Name, Warehouse Id, Workspace Id
|
|
220
|
+
If the semantic model is not a Direct Lake semantic model, it will return None, None, None.
|
|
221
|
+
"""
|
|
222
|
+
|
|
223
|
+
from sempy_labs._helper_functions import get_direct_lake_sql_endpoint
|
|
224
|
+
|
|
225
|
+
(workspace_name, workspace_id) = resolve_workspace_name_and_id(workspace)
|
|
226
|
+
sql_endpoint_id = get_direct_lake_sql_endpoint(dataset=dataset, workspace=workspace)
|
|
227
|
+
dfI = fabric.list_items(workspace=workspace)
|
|
228
|
+
dfI_filt = dfI[
|
|
229
|
+
(dfI["Id"] == sql_endpoint_id)
|
|
230
|
+
& (dfI["Type"].isin(["SQLEndpoint", "Warehouse"]))
|
|
231
|
+
]
|
|
232
|
+
|
|
233
|
+
artifact_type, artifact_name, artifact_id = None, None, None
|
|
234
|
+
|
|
235
|
+
if not dfI_filt.empty:
|
|
236
|
+
artifact_name = dfI_filt["Display Name"].iloc[0]
|
|
237
|
+
artifact_id = dfI[
|
|
238
|
+
(dfI["Display Name"] == artifact_name)
|
|
239
|
+
& (dfI["Type"].isin(["Lakehouse", "Warehouse"]))
|
|
240
|
+
]["Id"].iloc[0]
|
|
241
|
+
artifact_type = dfI[
|
|
242
|
+
(dfI["Display Name"] == artifact_name)
|
|
243
|
+
& (dfI["Type"].isin(["Lakehouse", "Warehouse"]))
|
|
244
|
+
]["Type"].iloc[0]
|
|
245
|
+
|
|
246
|
+
return artifact_type, artifact_name, artifact_id, workspace_id
|
|
247
|
+
|
|
248
|
+
# payload = {
|
|
249
|
+
# "artifacts": [
|
|
250
|
+
# {
|
|
251
|
+
# "objectId": dataset_id,
|
|
252
|
+
# "type": "dataset",
|
|
253
|
+
# }
|
|
254
|
+
# ]
|
|
255
|
+
# }
|
|
256
|
+
|
|
257
|
+
# response = _base_api(request="metadata/relations/upstream?apiVersion=3", payload=payload, method="post")
|
|
258
|
+
|
|
259
|
+
# artifacts = response.json().get("artifacts", [])
|
|
260
|
+
# sql_id, sql_object_name, sql_workspace_id, artifact_type = None, None, None, None
|
|
261
|
+
|
|
262
|
+
# for artifact in artifacts:
|
|
263
|
+
# object_type = artifact.get("typeName")
|
|
264
|
+
# display_name = artifact.get("displayName")
|
|
265
|
+
# if object_type in ["Datawarehouse", "Lakewarehouse"]:
|
|
266
|
+
# artifact_type = (
|
|
267
|
+
# "Warehouse" if object_type == "Datawarehouse" else "Lakehouse"
|
|
268
|
+
# )
|
|
269
|
+
# sql_id = artifact.get("objectId")
|
|
270
|
+
# sql_workspace_id = artifact.get("workspace", {}).get("objectId")
|
|
271
|
+
# sql_object_name = display_name
|
|
272
|
+
# break
|
|
273
|
+
|
|
274
|
+
# return artifact_type, sql_object_name, sql_id, sql_workspace_id
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
from .._helper_functions import (
|
|
2
|
+
resolve_workspace_name_and_id,
|
|
3
|
+
_base_api,
|
|
4
|
+
resolve_lakehouse_name_and_id,
|
|
5
|
+
resolve_item_name_and_id,
|
|
6
|
+
_get_fabric_context_setting,
|
|
7
|
+
)
|
|
8
|
+
from typing import Optional
|
|
9
|
+
import sempy_labs._icons as icons
|
|
10
|
+
from uuid import UUID
|
|
11
|
+
from sempy._utils._log import log
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@log
|
|
15
|
+
def generate_shared_expression(
|
|
16
|
+
item_name: Optional[str] = None,
|
|
17
|
+
item_type: str = "Lakehouse",
|
|
18
|
+
workspace: Optional[str | UUID] = None,
|
|
19
|
+
use_sql_endpoint: bool = True,
|
|
20
|
+
) -> str:
|
|
21
|
+
"""
|
|
22
|
+
Dynamically generates the M expression used by a Direct Lake model for a given lakehouse/warehouse.
|
|
23
|
+
|
|
24
|
+
Parameters
|
|
25
|
+
----------
|
|
26
|
+
item_name : str, default=None
|
|
27
|
+
The Fabric lakehouse or warehouse name.
|
|
28
|
+
Defaults to None which resolves to the lakehouse attached to the notebook.
|
|
29
|
+
item_type : str, default="Lakehouse"
|
|
30
|
+
The Fabric item name. Valid options: 'Lakehouse', 'Warehouse'.
|
|
31
|
+
workspace : str | uuid.UUID, default=None
|
|
32
|
+
The Fabric workspace name or ID used by the item.
|
|
33
|
+
Defaults to None which resolves to the workspace of the attached lakehouse
|
|
34
|
+
or if no lakehouse attached, resolves to the workspace of the notebook.
|
|
35
|
+
use_sql_endpoint : bool, default=True
|
|
36
|
+
Whether to use the SQL Endpoint for the lakehouse/warehouse.
|
|
37
|
+
If False, the expression will be generated without using the SQL Endpoint.
|
|
38
|
+
|
|
39
|
+
Returns
|
|
40
|
+
-------
|
|
41
|
+
str
|
|
42
|
+
Shows the expression which can be used to connect a Direct Lake semantic model to its SQL Endpoint.
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
(workspace_name, workspace_id) = resolve_workspace_name_and_id(workspace)
|
|
46
|
+
item_types = ["Lakehouse", "Warehouse"]
|
|
47
|
+
item_type = item_type.capitalize()
|
|
48
|
+
if item_type not in item_types:
|
|
49
|
+
raise ValueError(
|
|
50
|
+
f"{icons.red_dot} Invalid item type. Valid options: {item_types}."
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
if item_type == "Lakehouse":
|
|
54
|
+
(item_name, item_id) = resolve_lakehouse_name_and_id(
|
|
55
|
+
lakehouse=item_name, workspace=workspace_id
|
|
56
|
+
)
|
|
57
|
+
else:
|
|
58
|
+
(item_name, item_id) = resolve_item_name_and_id(
|
|
59
|
+
item=item_name, type=item_type, workspace=workspace_id
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
if use_sql_endpoint:
|
|
63
|
+
item_type_rest = f"{item_type.lower()}s"
|
|
64
|
+
response = _base_api(
|
|
65
|
+
request=f"/v1/workspaces/{workspace_id}/{item_type_rest}/{item_id}"
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
prop = response.json().get("properties")
|
|
69
|
+
|
|
70
|
+
if item_type == "Lakehouse":
|
|
71
|
+
sqlprop = prop.get("sqlEndpointProperties")
|
|
72
|
+
sqlEPCS = sqlprop.get("connectionString")
|
|
73
|
+
sqlepid = sqlprop.get("id")
|
|
74
|
+
provStatus = sqlprop.get("provisioningStatus")
|
|
75
|
+
elif item_type == "Warehouse":
|
|
76
|
+
sqlEPCS = prop.get("connectionString")
|
|
77
|
+
sqlepid = item_id
|
|
78
|
+
provStatus = None
|
|
79
|
+
|
|
80
|
+
if provStatus == "InProgress":
|
|
81
|
+
raise ValueError(
|
|
82
|
+
f"{icons.red_dot} The SQL Endpoint for the '{item_name}' {item_type.lower()} within the '{workspace_name}' workspace has not yet been provisioned. Please wait until it has been provisioned."
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
start_expr = "let\n\tdatabase = "
|
|
86
|
+
end_expr = "\nin\n\tdatabase"
|
|
87
|
+
mid_expr = f'Sql.Database("{sqlEPCS}", "{sqlepid}")'
|
|
88
|
+
return f"{start_expr}{mid_expr}{end_expr}"
|
|
89
|
+
else:
|
|
90
|
+
# Build DL/OL expression
|
|
91
|
+
env = _get_fabric_context_setting("spark.trident.pbienv").lower()
|
|
92
|
+
env = "" if env == "prod" else f"{env}-"
|
|
93
|
+
|
|
94
|
+
return f"""let\n\tSource = AzureStorage.DataLake("https://{env}onelake.dfs.fabric.microsoft.com/{workspace_id}/{item_id}")\nin\n\tSource"""
|