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,398 @@
|
|
|
1
|
+
import sempy
|
|
2
|
+
import sempy.fabric as fabric
|
|
3
|
+
import pandas as pd
|
|
4
|
+
import re
|
|
5
|
+
from sempy_labs.lakehouse._get_lakehouse_tables import get_lakehouse_tables
|
|
6
|
+
from sempy_labs._helper_functions import (
|
|
7
|
+
retry,
|
|
8
|
+
generate_guid,
|
|
9
|
+
save_as_delta_table,
|
|
10
|
+
resolve_lakehouse_name_and_id,
|
|
11
|
+
resolve_workspace_name_and_id,
|
|
12
|
+
)
|
|
13
|
+
from sempy_labs.tom import connect_semantic_model
|
|
14
|
+
from typing import Optional
|
|
15
|
+
from sempy._utils._log import log
|
|
16
|
+
import sempy_labs._icons as icons
|
|
17
|
+
from uuid import UUID
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@log
|
|
21
|
+
def migrate_calc_tables_to_lakehouse(
|
|
22
|
+
dataset: str,
|
|
23
|
+
new_dataset: str,
|
|
24
|
+
workspace: Optional[str | UUID] = None,
|
|
25
|
+
new_dataset_workspace: Optional[str | UUID] = None,
|
|
26
|
+
lakehouse: Optional[str | UUID] = None,
|
|
27
|
+
lakehouse_workspace: Optional[str | UUID] = None,
|
|
28
|
+
):
|
|
29
|
+
"""
|
|
30
|
+
Creates delta tables in your lakehouse based on the DAX expression of a calculated table in an import/DirectQuery semantic model.
|
|
31
|
+
The DAX expression encapsulating the calculated table logic is stored in the new Direct Lake semantic model as model annotations.
|
|
32
|
+
|
|
33
|
+
Parameters
|
|
34
|
+
----------
|
|
35
|
+
dataset : str
|
|
36
|
+
Name of the import/DirectQuery semantic model.
|
|
37
|
+
new_dataset : str
|
|
38
|
+
Name of the Direct Lake semantic model.
|
|
39
|
+
workspace : str | uuid.UUID, default=None
|
|
40
|
+
The Fabric workspace name in which the import/DirectQuery semantic model exists.
|
|
41
|
+
Defaults to None which resolves to the workspace of the attached lakehouse
|
|
42
|
+
or if no lakehouse attached, resolves to the workspace of the notebook.
|
|
43
|
+
new_dataset_workspace : str | uuid.UUID
|
|
44
|
+
The Fabric workspace name in which the Direct Lake semantic model will be created.
|
|
45
|
+
Defaults to None which resolves to the workspace of the attached lakehouse
|
|
46
|
+
or if no lakehouse attached, resolves to the workspace of the notebook.
|
|
47
|
+
lakehouse : str | uuid.UUID, default=None
|
|
48
|
+
The Fabric lakehouse used by the Direct Lake semantic model.
|
|
49
|
+
Defaults to None which resolves to the lakehouse attached to the notebook.
|
|
50
|
+
lakehouse_workspace : str | uuid.UUID, default=None
|
|
51
|
+
The Fabric workspace used by the lakehouse.
|
|
52
|
+
Defaults to None which resolves to the workspace of the attached lakehouse
|
|
53
|
+
or if no lakehouse attached, resolves to the workspace of the notebook.
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
if dataset == new_dataset:
|
|
57
|
+
raise ValueError(
|
|
58
|
+
f"{icons.red_dot} The 'dataset' and 'new_dataset' parameters are both set to '{dataset}'. These parameters must be set to different values."
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
(workspace_name, workspace_id) = resolve_workspace_name_and_id(workspace)
|
|
62
|
+
(new_dataset_workspace_name, new_dataset_workspace_id) = (
|
|
63
|
+
resolve_workspace_name_and_id(new_dataset_workspace)
|
|
64
|
+
)
|
|
65
|
+
(lakehouse_workspace_id, lakehouse_workspace_name) = resolve_workspace_name_and_id(
|
|
66
|
+
lakehouse_workspace
|
|
67
|
+
)
|
|
68
|
+
(lakehouse_name, lakehouse_id) = resolve_lakehouse_name_and_id(
|
|
69
|
+
lakehouse, lakehouse_workspace
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
dfP = fabric.list_partitions(dataset=dataset, workspace=workspace)
|
|
73
|
+
dfP_filt = dfP[(dfP["Source Type"] == "Calculated")]
|
|
74
|
+
dfP_filt = dfP_filt[
|
|
75
|
+
~dfP_filt["Query"].str.contains("NAMEOF")
|
|
76
|
+
] # Remove field parameters
|
|
77
|
+
# dfC_CalcColumn = dfC[dfC['Type'] == 'Calculated']
|
|
78
|
+
lakeTables = get_lakehouse_tables(lakehouse, lakehouse_workspace)
|
|
79
|
+
|
|
80
|
+
# Do not execute the function if lakehouse tables already exist with the same name
|
|
81
|
+
killFunction = False
|
|
82
|
+
for i, r in dfP_filt.iterrows():
|
|
83
|
+
tName = r["Table Name"]
|
|
84
|
+
dtName = tName.replace(" ", "_")
|
|
85
|
+
|
|
86
|
+
if dtName in lakeTables["Table Name"].values:
|
|
87
|
+
print(
|
|
88
|
+
f"{icons.red_dot} The '{tName}' table already exists as '{dtName}' in the '{lakehouse_name}' lakehouse in the '{lakehouse_workspace_name}' workspace."
|
|
89
|
+
)
|
|
90
|
+
killFunction = True
|
|
91
|
+
|
|
92
|
+
if killFunction:
|
|
93
|
+
return
|
|
94
|
+
|
|
95
|
+
if len(dfP_filt) == 0:
|
|
96
|
+
print(
|
|
97
|
+
f"{icons.yellow_dot} The '{dataset}' semantic model in the '{workspace_name}' workspace has no calculated tables."
|
|
98
|
+
)
|
|
99
|
+
return
|
|
100
|
+
|
|
101
|
+
with connect_semantic_model(
|
|
102
|
+
dataset=dataset, workspace=workspace, readonly=True
|
|
103
|
+
) as tom:
|
|
104
|
+
for t in tom.model.Tables:
|
|
105
|
+
if tom.is_auto_date_table(table_name=t.Name):
|
|
106
|
+
print(
|
|
107
|
+
f"{icons.yellow_dot} The '{t.Name}' table is an auto-datetime table and is not supported in the Direct Lake migration process. "
|
|
108
|
+
"Please create a proper Date/Calendar table in your lakehoues and use it in your Direct Lake model."
|
|
109
|
+
)
|
|
110
|
+
else:
|
|
111
|
+
for p in t.Partitions:
|
|
112
|
+
if str(p.SourceType) == "Calculated":
|
|
113
|
+
query = p.Source.Expression
|
|
114
|
+
if "NAMEOF" not in query: # exclude field parameters
|
|
115
|
+
daxQuery = ""
|
|
116
|
+
if query.lower().startswith("calendar") and any(
|
|
117
|
+
str(c.Type) == "Calculated" for c in t.Columns
|
|
118
|
+
):
|
|
119
|
+
daxQuery = f"ADDCOLUMNS(\n{query},"
|
|
120
|
+
for c in t.Columns:
|
|
121
|
+
if str(c.Type) == "Calculated":
|
|
122
|
+
expr = c.Expression
|
|
123
|
+
expr = expr.replace(f"'{t.Name}'", "").replace(
|
|
124
|
+
f"{t.Name}[Date]", "[Date]"
|
|
125
|
+
)
|
|
126
|
+
expr = expr.replace(
|
|
127
|
+
"[MonthNo]", "MONTH([Date])"
|
|
128
|
+
).replace(
|
|
129
|
+
"[QuarterNo]",
|
|
130
|
+
"INT((MONTH([Date]) + 2) / 3)",
|
|
131
|
+
)
|
|
132
|
+
daxQuery = f'{daxQuery}\n"{c.Name}",{expr},'
|
|
133
|
+
daxQuery = "EVALUATE\n" + daxQuery.rstrip(",") + "\n)"
|
|
134
|
+
else:
|
|
135
|
+
daxQuery = f"EVALUATE\n{query}"
|
|
136
|
+
daxQueryTopN = (
|
|
137
|
+
daxQuery.replace("EVALUATE\n", "EVALUATE\nTOPN(1,")
|
|
138
|
+
+ ")"
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
try:
|
|
142
|
+
df = fabric.evaluate_dax(
|
|
143
|
+
dataset=dataset,
|
|
144
|
+
dax_string=daxQueryTopN,
|
|
145
|
+
workspace=workspace,
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
for col in df.columns:
|
|
149
|
+
pattern = r"\[([^\]]+)\]"
|
|
150
|
+
|
|
151
|
+
matches = re.findall(pattern, col)
|
|
152
|
+
new_column_name = matches[0].replace(" ", "")
|
|
153
|
+
|
|
154
|
+
df.rename(
|
|
155
|
+
columns={col: new_column_name},
|
|
156
|
+
inplace=True,
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
try:
|
|
160
|
+
dataType = next(
|
|
161
|
+
str(c.DataType)
|
|
162
|
+
for c in tom.model.Tables[t.Name].Columns
|
|
163
|
+
if str(c.Type) == "CalculatedTableColumn"
|
|
164
|
+
and c.SourceColumn == col
|
|
165
|
+
)
|
|
166
|
+
except Exception:
|
|
167
|
+
dataType = next(
|
|
168
|
+
str(c.DataType)
|
|
169
|
+
for c in tom.model.Tables[t.Name].Columns
|
|
170
|
+
if str(c.Type) == "Calculated"
|
|
171
|
+
and c.Name == new_column_name
|
|
172
|
+
)
|
|
173
|
+
if dataType == "Int64":
|
|
174
|
+
df[new_column_name] = df[
|
|
175
|
+
new_column_name
|
|
176
|
+
].astype(int)
|
|
177
|
+
elif dataType in ["Decimal", "Double"]:
|
|
178
|
+
df[new_column_name] = df[
|
|
179
|
+
new_column_name
|
|
180
|
+
].astype(float)
|
|
181
|
+
elif dataType == "Boolean":
|
|
182
|
+
df[new_column_name] = df[
|
|
183
|
+
new_column_name
|
|
184
|
+
].astype(bool)
|
|
185
|
+
elif dataType == "DateTime":
|
|
186
|
+
df[new_column_name] = pd.to_datetime(
|
|
187
|
+
df[new_column_name]
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
delta_table_name = t.Name.replace(" ", "_").lower()
|
|
191
|
+
|
|
192
|
+
save_as_delta_table(
|
|
193
|
+
dataframe=df,
|
|
194
|
+
delta_table_name=delta_table_name,
|
|
195
|
+
lakehouse=lakehouse,
|
|
196
|
+
workspace=lakehouse_workspace,
|
|
197
|
+
write_mode="overwrite",
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
@retry(
|
|
201
|
+
sleep_time=1,
|
|
202
|
+
timeout_error_message=f"{icons.red_dot} Function timed out after 1 minute",
|
|
203
|
+
)
|
|
204
|
+
def dyn_connect():
|
|
205
|
+
with connect_semantic_model(
|
|
206
|
+
dataset=new_dataset,
|
|
207
|
+
readonly=True,
|
|
208
|
+
workspace=new_dataset_workspace,
|
|
209
|
+
) as tom2:
|
|
210
|
+
|
|
211
|
+
tom2.model
|
|
212
|
+
|
|
213
|
+
dyn_connect()
|
|
214
|
+
|
|
215
|
+
with connect_semantic_model(
|
|
216
|
+
dataset=new_dataset,
|
|
217
|
+
readonly=False,
|
|
218
|
+
workspace=new_dataset_workspace,
|
|
219
|
+
) as tom2:
|
|
220
|
+
tom2.set_annotation(
|
|
221
|
+
object=tom2.model,
|
|
222
|
+
name=t.Name,
|
|
223
|
+
value=daxQuery,
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
print(
|
|
227
|
+
f"{icons.green_dot} Calculated table '{t.Name}' has been created as delta table '{delta_table_name.lower()}' "
|
|
228
|
+
f"in the '{lakehouse_name}' lakehouse within the '{lakehouse_workspace_name}' workspace."
|
|
229
|
+
)
|
|
230
|
+
except Exception as e:
|
|
231
|
+
print(
|
|
232
|
+
f"{icons.red_dot} Failed to create calculated table '{t.Name}' as a delta table in the lakehouse."
|
|
233
|
+
)
|
|
234
|
+
print(e)
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
@log
|
|
238
|
+
def migrate_field_parameters(
|
|
239
|
+
dataset: str,
|
|
240
|
+
new_dataset: str,
|
|
241
|
+
workspace: Optional[str | UUID] = None,
|
|
242
|
+
new_dataset_workspace: Optional[str | UUID] = None,
|
|
243
|
+
):
|
|
244
|
+
"""
|
|
245
|
+
Migrates field parameters from one semantic model to another.
|
|
246
|
+
|
|
247
|
+
Parameters
|
|
248
|
+
----------
|
|
249
|
+
dataset : str
|
|
250
|
+
Name of the import/DirectQuery semantic model.
|
|
251
|
+
new_dataset : str
|
|
252
|
+
Name of the Direct Lake semantic model.
|
|
253
|
+
workspace : str | uuid.UUID, default=None
|
|
254
|
+
The Fabric workspace name in which the import/DirectQuery semantic model exists.
|
|
255
|
+
Defaults to None which resolves to the workspace of the attached lakehouse
|
|
256
|
+
or if no lakehouse attached, resolves to the workspace of the notebook.
|
|
257
|
+
new_dataset_workspace : str | uuid.UUID, default=None
|
|
258
|
+
The Fabric workspace name in which the Direct Lake semantic model will be created.
|
|
259
|
+
Defaults to None which resolves to the workspace of the attached lakehouse
|
|
260
|
+
or if no lakehouse attached, resolves to the workspace of the notebook.
|
|
261
|
+
"""
|
|
262
|
+
|
|
263
|
+
from sempy_labs import format_dax_object_name
|
|
264
|
+
|
|
265
|
+
sempy.fabric._client._utils._init_analysis_services()
|
|
266
|
+
import Microsoft.AnalysisServices.Tabular as TOM
|
|
267
|
+
|
|
268
|
+
icons.sll_tags.append("DirectLakeMigration")
|
|
269
|
+
fabric.refresh_tom_cache(workspace=workspace)
|
|
270
|
+
(new_dataset_workspace_name, new_dataset_workspace_id) = (
|
|
271
|
+
resolve_workspace_name_and_id(new_dataset_workspace)
|
|
272
|
+
)
|
|
273
|
+
|
|
274
|
+
dfC = fabric.list_columns(dataset=dataset, workspace=workspace)
|
|
275
|
+
dfC["Column Object"] = format_dax_object_name(dfC["Table Name"], dfC["Column Name"])
|
|
276
|
+
dfP = fabric.list_partitions(dataset=dataset, workspace=workspace)
|
|
277
|
+
dfP_filt = dfP[(dfP["Source Type"] == "Calculated")]
|
|
278
|
+
dfP_filt = dfP_filt[
|
|
279
|
+
dfP_filt["Query"].str.contains("NAMEOF")
|
|
280
|
+
] # Only field parameters
|
|
281
|
+
dfC_CalcColumn = dfC[dfC["Type"] == "Calculated"]
|
|
282
|
+
|
|
283
|
+
if len(dfP_filt) == 0:
|
|
284
|
+
print(
|
|
285
|
+
f"{icons.green_dot} The '{dataset}' semantic model in the '{workspace}' workspace has no field parameters."
|
|
286
|
+
)
|
|
287
|
+
return
|
|
288
|
+
|
|
289
|
+
@retry(
|
|
290
|
+
sleep_time=1,
|
|
291
|
+
timeout_error_message=f"{icons.red_dot} Function timed out after 1 minute",
|
|
292
|
+
)
|
|
293
|
+
def dyn_connect():
|
|
294
|
+
with connect_semantic_model(
|
|
295
|
+
dataset=new_dataset, readonly=True, workspace=new_dataset_workspace
|
|
296
|
+
) as tom:
|
|
297
|
+
|
|
298
|
+
tom.model
|
|
299
|
+
|
|
300
|
+
dyn_connect()
|
|
301
|
+
|
|
302
|
+
with connect_semantic_model(
|
|
303
|
+
dataset=new_dataset, workspace=new_dataset_workspace, readonly=False
|
|
304
|
+
) as tom:
|
|
305
|
+
|
|
306
|
+
for i, r in dfP_filt.iterrows():
|
|
307
|
+
tName = r["Table Name"]
|
|
308
|
+
query = r["Query"]
|
|
309
|
+
|
|
310
|
+
# For field parameters, remove calc columns from the query
|
|
311
|
+
rows = query.strip().split("\n")
|
|
312
|
+
filtered_rows = [
|
|
313
|
+
row
|
|
314
|
+
for row in rows
|
|
315
|
+
if not any(
|
|
316
|
+
value in row for value in dfC_CalcColumn["Column Object"].values
|
|
317
|
+
)
|
|
318
|
+
]
|
|
319
|
+
updated_query_string = "\n".join(filtered_rows)
|
|
320
|
+
|
|
321
|
+
# Remove extra comma
|
|
322
|
+
lines = updated_query_string.strip().split("\n")
|
|
323
|
+
lines[-2] = lines[-2].rstrip(",")
|
|
324
|
+
expr = "\n".join(lines)
|
|
325
|
+
|
|
326
|
+
try:
|
|
327
|
+
par = TOM.Partition()
|
|
328
|
+
par.Name = tName
|
|
329
|
+
par.Mode = TOM.ModeType.Import
|
|
330
|
+
|
|
331
|
+
parSource = TOM.CalculatedPartitionSource()
|
|
332
|
+
par.Source = parSource
|
|
333
|
+
parSource.Expression = expr
|
|
334
|
+
|
|
335
|
+
tbl = TOM.Table()
|
|
336
|
+
tbl.Name = tName
|
|
337
|
+
tbl.LineageTag = generate_guid()
|
|
338
|
+
tbl.Partitions.Add(par)
|
|
339
|
+
|
|
340
|
+
columns = ["Value1", "Value2", "Value3"]
|
|
341
|
+
|
|
342
|
+
for colName in columns:
|
|
343
|
+
col = TOM.CalculatedTableColumn()
|
|
344
|
+
col.Name = colName
|
|
345
|
+
col.SourceColumn = "[" + colName + "]"
|
|
346
|
+
col.DataType = TOM.DataType.String
|
|
347
|
+
col.LineageTag = generate_guid()
|
|
348
|
+
|
|
349
|
+
tbl.Columns.Add(col)
|
|
350
|
+
|
|
351
|
+
tom.model.Tables.Add(tbl)
|
|
352
|
+
|
|
353
|
+
ep = TOM.JsonExtendedProperty()
|
|
354
|
+
ep.Name = "ParameterMetadata"
|
|
355
|
+
ep.Value = '{"version":3,"kind":2}'
|
|
356
|
+
|
|
357
|
+
rcd = TOM.RelatedColumnDetails()
|
|
358
|
+
gpc = TOM.GroupByColumn()
|
|
359
|
+
gpc.GroupingColumn = tom.model.Tables[tName].Columns["Value2"]
|
|
360
|
+
rcd.GroupByColumns.Add(gpc)
|
|
361
|
+
|
|
362
|
+
# Update column properties
|
|
363
|
+
tom.model.Tables[tName].Columns["Value2"].IsHidden = True
|
|
364
|
+
tom.model.Tables[tName].Columns["Value3"].IsHidden = True
|
|
365
|
+
tom.model.Tables[tName].Columns["Value3"].DataType = TOM.DataType.Int64
|
|
366
|
+
tom.model.Tables[tName].Columns["Value1"].SortByColumn = (
|
|
367
|
+
tom.model.Tables[tName].Columns["Value3"]
|
|
368
|
+
)
|
|
369
|
+
tom.model.Tables[tName].Columns["Value2"].SortByColumn = (
|
|
370
|
+
tom.model.Tables[tName].Columns["Value3"]
|
|
371
|
+
)
|
|
372
|
+
tom.model.Tables[tName].Columns["Value2"].ExtendedProperties.Add(ep)
|
|
373
|
+
tom.model.Tables[tName].Columns["Value1"].RelatedColumnDetails = rcd
|
|
374
|
+
|
|
375
|
+
dfC_filt1 = dfC[
|
|
376
|
+
(dfC["Table Name"] == tName) & (dfC["Source"] == "[Value1]")
|
|
377
|
+
]
|
|
378
|
+
col1 = dfC_filt1["Column Name"].iloc[0]
|
|
379
|
+
dfC_filt2 = dfC[
|
|
380
|
+
(dfC["Table Name"] == tName) & (dfC["Source"] == "[Value2]")
|
|
381
|
+
]
|
|
382
|
+
col2 = dfC_filt2["Column Name"].iloc[0]
|
|
383
|
+
dfC_filt3 = dfC[
|
|
384
|
+
(dfC["Table Name"] == tName) & (dfC["Source"] == "[Value3]")
|
|
385
|
+
]
|
|
386
|
+
col3 = dfC_filt3["Column Name"].iloc[0]
|
|
387
|
+
|
|
388
|
+
tom.model.Tables[tName].Columns["Value1"].Name = col1
|
|
389
|
+
tom.model.Tables[tName].Columns["Value2"].Name = col2
|
|
390
|
+
tom.model.Tables[tName].Columns["Value3"].Name = col3
|
|
391
|
+
|
|
392
|
+
print(
|
|
393
|
+
f"{icons.green_dot} The '{tName}' table has been added as a field parameter to the '{new_dataset}' semantic model in the '{new_dataset_workspace_name}' workspace."
|
|
394
|
+
)
|
|
395
|
+
except Exception:
|
|
396
|
+
print(
|
|
397
|
+
f"{icons.red_dot} The '{tName}' table has not been added as a field parameter."
|
|
398
|
+
)
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import sempy.fabric as fabric
|
|
2
|
+
import re
|
|
3
|
+
from sempy_labs.lakehouse._get_lakehouse_tables import get_lakehouse_tables
|
|
4
|
+
from sempy_labs._helper_functions import (
|
|
5
|
+
format_dax_object_name,
|
|
6
|
+
retry,
|
|
7
|
+
)
|
|
8
|
+
from sempy_labs.tom import connect_semantic_model
|
|
9
|
+
from typing import Optional
|
|
10
|
+
from sempy._utils._log import log
|
|
11
|
+
import sempy_labs._icons as icons
|
|
12
|
+
from uuid import UUID
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@log
|
|
16
|
+
def migrate_calc_tables_to_semantic_model(
|
|
17
|
+
dataset: str,
|
|
18
|
+
new_dataset: str,
|
|
19
|
+
workspace: Optional[str | UUID] = None,
|
|
20
|
+
new_dataset_workspace: Optional[str | UUID] = None,
|
|
21
|
+
lakehouse: Optional[str | UUID] = None,
|
|
22
|
+
lakehouse_workspace: Optional[str | UUID] = None,
|
|
23
|
+
):
|
|
24
|
+
"""
|
|
25
|
+
Creates new tables in the Direct Lake semantic model based on the lakehouse tables created using the 'migrate_calc_tables_to_lakehouse' function.
|
|
26
|
+
|
|
27
|
+
Parameters
|
|
28
|
+
----------
|
|
29
|
+
dataset : str
|
|
30
|
+
Name of the import/DirectQuery semantic model.
|
|
31
|
+
new_dataset : str
|
|
32
|
+
Name of the Direct Lake semantic model.
|
|
33
|
+
workspace : str | uuid.UUID, default=None
|
|
34
|
+
The Fabric workspace name in which the import/DirectQuery semantic model exists.
|
|
35
|
+
Defaults to None which resolves to the workspace of the attached lakehouse
|
|
36
|
+
or if no lakehouse attached, resolves to the workspace of the notebook.
|
|
37
|
+
new_dataset_workspace : str | uuid.UUID, default=None
|
|
38
|
+
The Fabric workspace name in which the Direct Lake semantic model will be created.
|
|
39
|
+
Defaults to None which resolves to the workspace of the attached lakehouse
|
|
40
|
+
or if no lakehouse attached, resolves to the workspace of the notebook.
|
|
41
|
+
lakehouse : str | uuid.UUID, default=None
|
|
42
|
+
The Fabric lakehouse used by the Direct Lake semantic model.
|
|
43
|
+
Defaults to None which resolves to the lakehouse attached to the notebook.
|
|
44
|
+
lakehouse_workspace : str | uuid.UUID, default=None
|
|
45
|
+
The Fabric workspace used by the lakehouse.
|
|
46
|
+
Defaults to None which resolves to the workspace of the attached lakehouse
|
|
47
|
+
or if no lakehouse attached, resolves to the workspace of the notebook.
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
if dataset == new_dataset:
|
|
51
|
+
raise ValueError(
|
|
52
|
+
f"{icons.red_dot} The 'dataset' and 'new_dataset' parameters are both set to '{dataset}'. These parameters must be set to different values."
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
fabric.refresh_tom_cache(workspace=workspace)
|
|
56
|
+
|
|
57
|
+
icons.sll_tags.append("DirectLakeMigration")
|
|
58
|
+
|
|
59
|
+
# Get calc tables but not field parameters
|
|
60
|
+
dfP = fabric.list_partitions(dataset=dataset, workspace=workspace)
|
|
61
|
+
dfP_filt = dfP[(dfP["Source Type"] == "Calculated")]
|
|
62
|
+
dfP_filt = dfP_filt[~dfP_filt["Query"].str.contains("NAMEOF")]
|
|
63
|
+
|
|
64
|
+
dfC = fabric.list_columns(dataset=dataset, workspace=workspace)
|
|
65
|
+
lc = get_lakehouse_tables(lakehouse=lakehouse, workspace=lakehouse_workspace)
|
|
66
|
+
# Get all calc table columns of calc tables not including field parameters
|
|
67
|
+
dfC_filt = dfC[
|
|
68
|
+
(dfC["Table Name"].isin(dfP_filt["Table Name"]))
|
|
69
|
+
] # & (dfC['Type'] == 'CalculatedTableColumn')]
|
|
70
|
+
# dfA = fabric.list_annotations(new_dataset, new_dataset_workspace)
|
|
71
|
+
# dfA_filt = dfA[(dfA['Object Type'] == 'Model') & ~ (dfA['Annotation Value'].str.contains('NAMEOF'))]
|
|
72
|
+
|
|
73
|
+
if len(dfP_filt) == 0:
|
|
74
|
+
print(
|
|
75
|
+
f"{icons.green_dot} The '{dataset}' semantic model has no calculated tables."
|
|
76
|
+
)
|
|
77
|
+
return
|
|
78
|
+
|
|
79
|
+
@retry(
|
|
80
|
+
sleep_time=1,
|
|
81
|
+
timeout_error_message=f"{icons.red_dot} Function timed out after 1 minute",
|
|
82
|
+
)
|
|
83
|
+
def dyn_connect():
|
|
84
|
+
with connect_semantic_model(
|
|
85
|
+
dataset=new_dataset, readonly=True, workspace=new_dataset_workspace
|
|
86
|
+
) as tom:
|
|
87
|
+
|
|
88
|
+
tom.model
|
|
89
|
+
|
|
90
|
+
dyn_connect()
|
|
91
|
+
|
|
92
|
+
with connect_semantic_model(
|
|
93
|
+
dataset=new_dataset, readonly=False, workspace=new_dataset_workspace
|
|
94
|
+
) as tom:
|
|
95
|
+
for tName in dfC_filt["Table Name"].unique():
|
|
96
|
+
if tName.lower() in lc["Table Name"].values:
|
|
97
|
+
if not any(t.Name == tName for t in tom.model.Tables):
|
|
98
|
+
tom.add_table(name=tName)
|
|
99
|
+
tom.add_entity_partition(
|
|
100
|
+
table_name=tName,
|
|
101
|
+
entity_name=tName.replace(" ", "_").lower(),
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
columns_in_table = dfC_filt.loc[
|
|
105
|
+
dfC_filt["Table Name"] == tName, "Column Name"
|
|
106
|
+
].unique()
|
|
107
|
+
|
|
108
|
+
for cName in columns_in_table:
|
|
109
|
+
scName = dfC.loc[
|
|
110
|
+
(dfC["Table Name"] == tName) & (dfC["Column Name"] == cName),
|
|
111
|
+
"Source",
|
|
112
|
+
].iloc[0]
|
|
113
|
+
cDataType = dfC.loc[
|
|
114
|
+
(dfC["Table Name"] == tName) & (dfC["Column Name"] == cName),
|
|
115
|
+
"Data Type",
|
|
116
|
+
].iloc[0]
|
|
117
|
+
# cType = dfC.loc[
|
|
118
|
+
# (dfC["Table Name"] == tName)
|
|
119
|
+
# & (dfC["Column Name"] == cName),
|
|
120
|
+
# "Type",
|
|
121
|
+
# ].iloc[0]
|
|
122
|
+
|
|
123
|
+
# av = tom.get_annotation_value(object = tom.model, name = tName)
|
|
124
|
+
|
|
125
|
+
# if cType == 'CalculatedTableColumn':
|
|
126
|
+
# lakeColumn = scName.replace(' ','_')
|
|
127
|
+
# elif cType == 'Calculated':
|
|
128
|
+
pattern = r"\[([^]]+)\]"
|
|
129
|
+
|
|
130
|
+
matches = re.findall(pattern, scName)
|
|
131
|
+
lakeColumn = matches[0].replace(" ", "")
|
|
132
|
+
if not any(
|
|
133
|
+
c.Name == cName and c.Parent.Name == tName
|
|
134
|
+
for c in tom.all_columns()
|
|
135
|
+
):
|
|
136
|
+
tom.add_data_column(
|
|
137
|
+
table_name=tName,
|
|
138
|
+
column_name=cName,
|
|
139
|
+
source_column=lakeColumn,
|
|
140
|
+
data_type=cDataType,
|
|
141
|
+
)
|
|
142
|
+
print(
|
|
143
|
+
f"{icons.green_dot} The {format_dax_object_name(tName,cName)} column has been added."
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
print(
|
|
147
|
+
f"\n{icons.green_dot} All viable calculated tables have been added to the model."
|
|
148
|
+
)
|