semantic-link-labs 0.4.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of semantic-link-labs might be problematic. Click here for more details.
- semantic_link_labs-0.4.1.dist-info/LICENSE +21 -0
- semantic_link_labs-0.4.1.dist-info/METADATA +22 -0
- semantic_link_labs-0.4.1.dist-info/RECORD +52 -0
- semantic_link_labs-0.4.1.dist-info/WHEEL +5 -0
- semantic_link_labs-0.4.1.dist-info/top_level.txt +1 -0
- sempy_labs/__init__.py +154 -0
- sempy_labs/_ai.py +496 -0
- sempy_labs/_clear_cache.py +39 -0
- sempy_labs/_connections.py +234 -0
- sempy_labs/_dax.py +70 -0
- sempy_labs/_generate_semantic_model.py +280 -0
- sempy_labs/_helper_functions.py +506 -0
- sempy_labs/_icons.py +4 -0
- sempy_labs/_list_functions.py +1372 -0
- sempy_labs/_model_auto_build.py +143 -0
- sempy_labs/_model_bpa.py +1354 -0
- sempy_labs/_model_dependencies.py +341 -0
- sempy_labs/_one_lake_integration.py +155 -0
- sempy_labs/_query_scale_out.py +447 -0
- sempy_labs/_refresh_semantic_model.py +184 -0
- sempy_labs/_tom.py +3766 -0
- sempy_labs/_translations.py +378 -0
- sempy_labs/_vertipaq.py +893 -0
- sempy_labs/directlake/__init__.py +45 -0
- sempy_labs/directlake/_directlake_schema_compare.py +110 -0
- sempy_labs/directlake/_directlake_schema_sync.py +128 -0
- sempy_labs/directlake/_fallback.py +62 -0
- sempy_labs/directlake/_get_directlake_lakehouse.py +69 -0
- sempy_labs/directlake/_get_shared_expression.py +59 -0
- sempy_labs/directlake/_guardrails.py +84 -0
- sempy_labs/directlake/_list_directlake_model_calc_tables.py +54 -0
- sempy_labs/directlake/_show_unsupported_directlake_objects.py +89 -0
- sempy_labs/directlake/_update_directlake_model_lakehouse_connection.py +81 -0
- sempy_labs/directlake/_update_directlake_partition_entity.py +64 -0
- sempy_labs/directlake/_warm_cache.py +210 -0
- sempy_labs/lakehouse/__init__.py +24 -0
- sempy_labs/lakehouse/_get_lakehouse_columns.py +81 -0
- sempy_labs/lakehouse/_get_lakehouse_tables.py +250 -0
- sempy_labs/lakehouse/_lakehouse.py +85 -0
- sempy_labs/lakehouse/_shortcuts.py +296 -0
- sempy_labs/migration/__init__.py +29 -0
- sempy_labs/migration/_create_pqt_file.py +239 -0
- sempy_labs/migration/_migrate_calctables_to_lakehouse.py +429 -0
- sempy_labs/migration/_migrate_calctables_to_semantic_model.py +150 -0
- sempy_labs/migration/_migrate_model_objects_to_semantic_model.py +524 -0
- sempy_labs/migration/_migrate_tables_columns_to_semantic_model.py +165 -0
- sempy_labs/migration/_migration_validation.py +227 -0
- sempy_labs/migration/_refresh_calc_tables.py +129 -0
- sempy_labs/report/__init__.py +35 -0
- sempy_labs/report/_generate_report.py +253 -0
- sempy_labs/report/_report_functions.py +855 -0
- sempy_labs/report/_report_rebind.py +131 -0
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import sempy
|
|
2
|
+
import sempy.fabric as fabric
|
|
3
|
+
import pandas as pd
|
|
4
|
+
import datetime, time
|
|
5
|
+
from sempy_labs._list_functions import list_tables
|
|
6
|
+
from sempy_labs.directlake._get_shared_expression import get_shared_expression
|
|
7
|
+
from sempy_labs._helper_functions import resolve_lakehouse_name
|
|
8
|
+
from sempy_labs.lakehouse._lakehouse import lakehouse_attached
|
|
9
|
+
from sempy_labs._tom import connect_semantic_model
|
|
10
|
+
from typing import List, Optional, Union
|
|
11
|
+
from sempy._utils._log import log
|
|
12
|
+
import sempy_labs._icons as icons
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@log
|
|
16
|
+
def migrate_tables_columns_to_semantic_model(
|
|
17
|
+
dataset: str,
|
|
18
|
+
new_dataset: str,
|
|
19
|
+
workspace: Optional[str] = None,
|
|
20
|
+
new_dataset_workspace: Optional[str] = None,
|
|
21
|
+
lakehouse: Optional[str] = None,
|
|
22
|
+
lakehouse_workspace: Optional[str] = None,
|
|
23
|
+
):
|
|
24
|
+
"""
|
|
25
|
+
Adds tables/columns to the new Direct Lake semantic model based on an import/DirectQuery semantic model.
|
|
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, 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
|
|
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, 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, 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
|
+
|
|
51
|
+
workspace = fabric.resolve_workspace_name(workspace)
|
|
52
|
+
|
|
53
|
+
if new_dataset_workspace == None:
|
|
54
|
+
new_dataset_workspace = workspace
|
|
55
|
+
|
|
56
|
+
if lakehouse_workspace == None:
|
|
57
|
+
lakehouse_workspace = new_dataset_workspace
|
|
58
|
+
|
|
59
|
+
if lakehouse == None:
|
|
60
|
+
lakehouse_id = fabric.get_lakehouse_id()
|
|
61
|
+
lakehouse = resolve_lakehouse_name(lakehouse_id, lakehouse_workspace)
|
|
62
|
+
|
|
63
|
+
# Check that lakehouse is attached to the notebook
|
|
64
|
+
lakeAttach = lakehouse_attached()
|
|
65
|
+
|
|
66
|
+
# Run if lakehouse is attached to the notebook or a lakehouse & lakehouse workspace are specified
|
|
67
|
+
if lakeAttach or (lakehouse is not None and lakehouse_workspace is not None):
|
|
68
|
+
shEx = get_shared_expression(lakehouse, lakehouse_workspace)
|
|
69
|
+
|
|
70
|
+
dfC = fabric.list_columns(dataset=dataset, workspace=workspace)
|
|
71
|
+
dfT = list_tables(dataset, workspace)
|
|
72
|
+
dfT.rename(columns={"Type": "Table Type"}, inplace=True)
|
|
73
|
+
dfC = pd.merge(
|
|
74
|
+
dfC,
|
|
75
|
+
dfT[["Name", "Table Type"]],
|
|
76
|
+
left_on="Table Name",
|
|
77
|
+
right_on="Name",
|
|
78
|
+
how="left",
|
|
79
|
+
)
|
|
80
|
+
dfT_filt = dfT[dfT["Table Type"] == "Table"]
|
|
81
|
+
dfC_filt = dfC[
|
|
82
|
+
(dfC["Table Type"] == "Table")
|
|
83
|
+
& ~(dfC["Column Name"].str.startswith("RowNumber-"))
|
|
84
|
+
& (dfC["Type"] != "Calculated")
|
|
85
|
+
]
|
|
86
|
+
|
|
87
|
+
print(f"{icons.in_progress} Updating '{new_dataset}' based on '{dataset}'...")
|
|
88
|
+
start_time = datetime.datetime.now()
|
|
89
|
+
timeout = datetime.timedelta(minutes=1)
|
|
90
|
+
success = False
|
|
91
|
+
|
|
92
|
+
while not success:
|
|
93
|
+
try:
|
|
94
|
+
with connect_semantic_model(
|
|
95
|
+
dataset=new_dataset, readonly=False, workspace=new_dataset_workspace
|
|
96
|
+
) as tom:
|
|
97
|
+
success = True
|
|
98
|
+
try:
|
|
99
|
+
tom.model.Expressions["DatabaseQuery"]
|
|
100
|
+
except:
|
|
101
|
+
tom.add_expression("DatabaseQuery", expression=shEx)
|
|
102
|
+
print(
|
|
103
|
+
f"{icons.green_dot} The 'DatabaseQuery' expression has been added."
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
for i, r in dfT_filt.iterrows():
|
|
107
|
+
tName = r["Name"]
|
|
108
|
+
tDC = r["Data Category"]
|
|
109
|
+
tHid = bool(r["Hidden"])
|
|
110
|
+
tDesc = r["Description"]
|
|
111
|
+
|
|
112
|
+
try:
|
|
113
|
+
tom.model.Tables[tName]
|
|
114
|
+
except:
|
|
115
|
+
tom.add_table(
|
|
116
|
+
name=tName,
|
|
117
|
+
description=tDesc,
|
|
118
|
+
data_category=tDC,
|
|
119
|
+
hidden=tHid,
|
|
120
|
+
)
|
|
121
|
+
tom.add_entity_partition(
|
|
122
|
+
table_name=tName, entity_name=tName.replace(" ", "_")
|
|
123
|
+
)
|
|
124
|
+
print(
|
|
125
|
+
f"{icons.green_dot} The '{tName}' table has been added."
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
for i, r in dfC_filt.iterrows():
|
|
129
|
+
tName = r["Table Name"]
|
|
130
|
+
cName = r["Column Name"]
|
|
131
|
+
scName = r["Source"].replace(" ", "_")
|
|
132
|
+
cHid = bool(r["Hidden"])
|
|
133
|
+
cDataType = r["Data Type"]
|
|
134
|
+
|
|
135
|
+
try:
|
|
136
|
+
tom.model.Tables[tName].Columns[cName]
|
|
137
|
+
except:
|
|
138
|
+
tom.add_data_column(
|
|
139
|
+
table_name=tName,
|
|
140
|
+
column_name=cName,
|
|
141
|
+
source_column=scName,
|
|
142
|
+
hidden=cHid,
|
|
143
|
+
data_type=cDataType,
|
|
144
|
+
)
|
|
145
|
+
print(
|
|
146
|
+
f"{icons.green_dot} The '{tName}'[{cName}] column has been added."
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
print(
|
|
150
|
+
f"\n{icons.green_dot} All regular tables and columns have been added to the '{new_dataset}' semantic model."
|
|
151
|
+
)
|
|
152
|
+
except Exception as e:
|
|
153
|
+
if datetime.datetime.now() - start_time > timeout:
|
|
154
|
+
break
|
|
155
|
+
time.sleep(1)
|
|
156
|
+
else:
|
|
157
|
+
print(
|
|
158
|
+
f"{icons.red_dot} Lakehouse not attached to notebook and lakehouse/lakehouse_workspace are not specified. Please add your lakehouse to this notebook or specify the lakehouse/lakehouse_workspace parameters."
|
|
159
|
+
)
|
|
160
|
+
print(
|
|
161
|
+
f"To attach a lakehouse to a notebook, go to the the 'Explorer' window to the left, click 'Lakehouses' to add your lakehouse to this notebook"
|
|
162
|
+
)
|
|
163
|
+
print(
|
|
164
|
+
f"\nLearn more here: https://learn.microsoft.com/fabric/data-engineering/lakehouse-notebook-explore#add-or-remove-a-lakehouse"
|
|
165
|
+
)
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
import sempy
|
|
2
|
+
import sempy.fabric as fabric
|
|
3
|
+
import pandas as pd
|
|
4
|
+
from sempy_labs._helper_functions import create_relationship_name
|
|
5
|
+
from sempy_labs._tom import connect_semantic_model
|
|
6
|
+
from typing import List, Optional, Union
|
|
7
|
+
from sempy._utils._log import log
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def list_semantic_model_objects(dataset: str, workspace: Optional[str] = None):
|
|
11
|
+
"""
|
|
12
|
+
Shows a list of semantic model objects.
|
|
13
|
+
|
|
14
|
+
Parameters
|
|
15
|
+
----------
|
|
16
|
+
dataset : str
|
|
17
|
+
Name of the semantic model.
|
|
18
|
+
workspace : str, default=None
|
|
19
|
+
The Fabric workspace name.
|
|
20
|
+
Defaults to None which resolves to the workspace of the attached lakehouse
|
|
21
|
+
or if no lakehouse attached, resolves to the workspace of the notebook.
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
Returns
|
|
25
|
+
-------
|
|
26
|
+
pandas.DataFrame
|
|
27
|
+
A pandas dataframe showing a list of objects in the semantic model
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
df = pd.DataFrame(columns=["Parent Name", "Object Name", "Object Type"])
|
|
31
|
+
with connect_semantic_model(
|
|
32
|
+
dataset=dataset, workspace=workspace, readonly=True
|
|
33
|
+
) as tom:
|
|
34
|
+
for t in tom.model.Tables:
|
|
35
|
+
if t.CalculationGroup is not None:
|
|
36
|
+
new_data = {
|
|
37
|
+
"Parent Name": t.Parent.Name,
|
|
38
|
+
"Object Name": t.Name,
|
|
39
|
+
"Object Type": "Calculation Group",
|
|
40
|
+
}
|
|
41
|
+
df = pd.concat(
|
|
42
|
+
[df, pd.DataFrame(new_data, index=[0])], ignore_index=True
|
|
43
|
+
)
|
|
44
|
+
for ci in t.CalculationGroup.CalculationItems:
|
|
45
|
+
new_data = {
|
|
46
|
+
"Parent Name": t.Name,
|
|
47
|
+
"Object Name": ci.Name,
|
|
48
|
+
"Object Type": str(ci.ObjectType),
|
|
49
|
+
}
|
|
50
|
+
df = pd.concat(
|
|
51
|
+
[df, pd.DataFrame(new_data, index=[0])], ignore_index=True
|
|
52
|
+
)
|
|
53
|
+
elif any(str(p.SourceType) == "Calculated" for p in t.Partitions):
|
|
54
|
+
new_data = {
|
|
55
|
+
"Parent Name": t.Parent.Name,
|
|
56
|
+
"Object Name": t.Name,
|
|
57
|
+
"Object Type": "Calculated Table",
|
|
58
|
+
}
|
|
59
|
+
df = pd.concat(
|
|
60
|
+
[df, pd.DataFrame(new_data, index=[0])], ignore_index=True
|
|
61
|
+
)
|
|
62
|
+
else:
|
|
63
|
+
new_data = {
|
|
64
|
+
"Parent Name": t.Parent.Name,
|
|
65
|
+
"Object Name": t.Name,
|
|
66
|
+
"Object Type": str(t.ObjectType),
|
|
67
|
+
}
|
|
68
|
+
df = pd.concat(
|
|
69
|
+
[df, pd.DataFrame(new_data, index=[0])], ignore_index=True
|
|
70
|
+
)
|
|
71
|
+
for c in t.Columns:
|
|
72
|
+
if str(c.Type) != "RowNumber":
|
|
73
|
+
if str(c.Type) == "Calculated":
|
|
74
|
+
new_data = {
|
|
75
|
+
"Parent Name": c.Parent.Name,
|
|
76
|
+
"Object Name": c.Name,
|
|
77
|
+
"Object Type": "Calculated Column",
|
|
78
|
+
}
|
|
79
|
+
df = pd.concat(
|
|
80
|
+
[df, pd.DataFrame(new_data, index=[0])], ignore_index=True
|
|
81
|
+
)
|
|
82
|
+
else:
|
|
83
|
+
new_data = {
|
|
84
|
+
"Parent Name": c.Parent.Name,
|
|
85
|
+
"Object Name": c.Name,
|
|
86
|
+
"Object Type": str(c.ObjectType),
|
|
87
|
+
}
|
|
88
|
+
df = pd.concat(
|
|
89
|
+
[df, pd.DataFrame(new_data, index=[0])], ignore_index=True
|
|
90
|
+
)
|
|
91
|
+
for m in t.Measures:
|
|
92
|
+
new_data = {
|
|
93
|
+
"Parent Name": m.Parent.Name,
|
|
94
|
+
"Object Name": m.Name,
|
|
95
|
+
"Object Type": str(m.ObjectType),
|
|
96
|
+
}
|
|
97
|
+
df = pd.concat(
|
|
98
|
+
[df, pd.DataFrame(new_data, index=[0])], ignore_index=True
|
|
99
|
+
)
|
|
100
|
+
for h in t.Hierarchies:
|
|
101
|
+
new_data = {
|
|
102
|
+
"Parent Name": h.Parent.Name,
|
|
103
|
+
"Object Name": h.Name,
|
|
104
|
+
"Object Type": str(h.ObjectType),
|
|
105
|
+
}
|
|
106
|
+
df = pd.concat(
|
|
107
|
+
[df, pd.DataFrame(new_data, index=[0])], ignore_index=True
|
|
108
|
+
)
|
|
109
|
+
for l in h.Levels:
|
|
110
|
+
new_data = {
|
|
111
|
+
"Parent Name": l.Parent.Name,
|
|
112
|
+
"Object Name": l.Name,
|
|
113
|
+
"Object Type": str(l.ObjectType),
|
|
114
|
+
}
|
|
115
|
+
df = pd.concat(
|
|
116
|
+
[df, pd.DataFrame(new_data, index=[0])], ignore_index=True
|
|
117
|
+
)
|
|
118
|
+
for p in t.Partitions:
|
|
119
|
+
new_data = {
|
|
120
|
+
"Parent Name": p.Parent.Name,
|
|
121
|
+
"Object Name": p.Name,
|
|
122
|
+
"Object Type": str(p.ObjectType),
|
|
123
|
+
}
|
|
124
|
+
df = pd.concat(
|
|
125
|
+
[df, pd.DataFrame(new_data, index=[0])], ignore_index=True
|
|
126
|
+
)
|
|
127
|
+
for r in tom.model.Relationships:
|
|
128
|
+
rName = create_relationship_name(
|
|
129
|
+
r.FromTable.Name, r.FromColumn.Name, r.ToTable.Name, r.ToColumn.Name
|
|
130
|
+
)
|
|
131
|
+
new_data = {
|
|
132
|
+
"Parent Name": r.Parent.Name,
|
|
133
|
+
"Object Name": rName,
|
|
134
|
+
"Object Type": str(r.ObjectType),
|
|
135
|
+
}
|
|
136
|
+
df = pd.concat([df, pd.DataFrame(new_data, index=[0])], ignore_index=True)
|
|
137
|
+
for role in tom.model.Roles:
|
|
138
|
+
new_data = {
|
|
139
|
+
"Parent Name": role.Parent.Name,
|
|
140
|
+
"Object Name": role.Name,
|
|
141
|
+
"Object Type": str(role.ObjectType),
|
|
142
|
+
}
|
|
143
|
+
df = pd.concat([df, pd.DataFrame(new_data, index=[0])], ignore_index=True)
|
|
144
|
+
for rls in role.TablePermissions:
|
|
145
|
+
new_data = {
|
|
146
|
+
"Parent Name": role.Name,
|
|
147
|
+
"Object Name": rls.Name,
|
|
148
|
+
"Object Type": str(rls.ObjectType),
|
|
149
|
+
}
|
|
150
|
+
df = pd.concat(
|
|
151
|
+
[df, pd.DataFrame(new_data, index=[0])], ignore_index=True
|
|
152
|
+
)
|
|
153
|
+
for tr in tom.model.Cultures:
|
|
154
|
+
new_data = {
|
|
155
|
+
"Parent Name": tr.Parent.Name,
|
|
156
|
+
"Object Name": tr.Name,
|
|
157
|
+
"Object Type": str(tr.ObjectType),
|
|
158
|
+
}
|
|
159
|
+
df = pd.concat([df, pd.DataFrame(new_data, index=[0])], ignore_index=True)
|
|
160
|
+
for per in tom.model.Perspectives:
|
|
161
|
+
new_data = {
|
|
162
|
+
"Parent Name": per.Parent.Name,
|
|
163
|
+
"Object Name": per.Name,
|
|
164
|
+
"Object Type": str(per.ObjectType),
|
|
165
|
+
}
|
|
166
|
+
df = pd.concat([df, pd.DataFrame(new_data, index=[0])], ignore_index=True)
|
|
167
|
+
|
|
168
|
+
return df
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
@log
|
|
172
|
+
def migration_validation(
|
|
173
|
+
dataset: str,
|
|
174
|
+
new_dataset: str,
|
|
175
|
+
workspace: Optional[str] = None,
|
|
176
|
+
new_dataset_workspace: Optional[str] = None,
|
|
177
|
+
) -> pd.DataFrame:
|
|
178
|
+
"""
|
|
179
|
+
Shows the objects in the original semantic model and whether then were migrated successfully or not.
|
|
180
|
+
|
|
181
|
+
Parameters
|
|
182
|
+
----------
|
|
183
|
+
dataset : str
|
|
184
|
+
Name of the import/DirectQuery semantic model.
|
|
185
|
+
new_dataset : str
|
|
186
|
+
Name of the Direct Lake semantic model.
|
|
187
|
+
workspace : str, default=None
|
|
188
|
+
The Fabric workspace name in which the import/DirectQuery semantic model exists.
|
|
189
|
+
Defaults to None which resolves to the workspace of the attached lakehouse
|
|
190
|
+
or if no lakehouse attached, resolves to the workspace of the notebook.
|
|
191
|
+
new_dataset_workspace : str
|
|
192
|
+
The Fabric workspace name in which the Direct Lake semantic model will be created.
|
|
193
|
+
Defaults to None which resolves to the workspace of the attached lakehouse
|
|
194
|
+
or if no lakehouse attached, resolves to the workspace of the notebook.
|
|
195
|
+
|
|
196
|
+
Returns
|
|
197
|
+
-------
|
|
198
|
+
pandas.DataFrame
|
|
199
|
+
A pandas dataframe showing a list of objects and whether they were successfully migrated. Also shows the % of objects which were migrated successfully.
|
|
200
|
+
"""
|
|
201
|
+
|
|
202
|
+
dfA = list_semantic_model_objects(dataset=dataset, workspace=workspace)
|
|
203
|
+
dfB = list_semantic_model_objects(
|
|
204
|
+
dataset=new_dataset, workspace=new_dataset_workspace
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
def is_migrated(row):
|
|
208
|
+
if row["Object Type"] == "Calculated Table":
|
|
209
|
+
return (
|
|
210
|
+
(dfB["Parent Name"] == row["Parent Name"])
|
|
211
|
+
& (dfB["Object Name"] == row["Object Name"])
|
|
212
|
+
& (dfB["Object Type"].isin(["Calculated Table", "Table"]))
|
|
213
|
+
).any()
|
|
214
|
+
else:
|
|
215
|
+
return (
|
|
216
|
+
(dfB["Parent Name"] == row["Parent Name"])
|
|
217
|
+
& (dfB["Object Name"] == row["Object Name"])
|
|
218
|
+
& (dfB["Object Type"] == row["Object Type"])
|
|
219
|
+
).any()
|
|
220
|
+
|
|
221
|
+
dfA["Migrated"] = dfA.apply(is_migrated, axis=1)
|
|
222
|
+
|
|
223
|
+
denom = len(dfA)
|
|
224
|
+
num = len(dfA[dfA["Migrated"]])
|
|
225
|
+
print(f"{100 * round(num / denom,2)}% migrated")
|
|
226
|
+
|
|
227
|
+
return dfA
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import sempy
|
|
2
|
+
import sempy.fabric as fabric
|
|
3
|
+
import pandas as pd
|
|
4
|
+
import re, datetime, time
|
|
5
|
+
from pyspark.sql import SparkSession
|
|
6
|
+
from sempy_labs._tom import connect_semantic_model
|
|
7
|
+
from typing import List, Optional, Union
|
|
8
|
+
from sempy._utils._log import log
|
|
9
|
+
import sempy_labs._icons as icons
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@log
|
|
13
|
+
def refresh_calc_tables(dataset: str, workspace: Optional[str] = None):
|
|
14
|
+
"""
|
|
15
|
+
Recreates the delta tables in the lakehouse based on the DAX expressions stored as model annotations in the Direct Lake semantic model.
|
|
16
|
+
|
|
17
|
+
Parameters
|
|
18
|
+
----------
|
|
19
|
+
dataset : str
|
|
20
|
+
Name of the semantic model.
|
|
21
|
+
workspace : str, default=None
|
|
22
|
+
The Fabric workspace name.
|
|
23
|
+
Defaults to None which resolves to the workspace of the attached lakehouse
|
|
24
|
+
or if no lakehouse attached, resolves to the workspace of the notebook.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
spark = SparkSession.builder.getOrCreate()
|
|
28
|
+
|
|
29
|
+
start_time = datetime.datetime.now()
|
|
30
|
+
timeout = datetime.timedelta(minutes=1)
|
|
31
|
+
success = False
|
|
32
|
+
|
|
33
|
+
while not success:
|
|
34
|
+
try:
|
|
35
|
+
with connect_semantic_model(
|
|
36
|
+
dataset=dataset, readonly=True, workspace=workspace
|
|
37
|
+
) as tom:
|
|
38
|
+
success = True
|
|
39
|
+
for a in tom.model.Annotations:
|
|
40
|
+
if any(a.Name == t.Name for t in tom.model.Tables):
|
|
41
|
+
tName = a.Name
|
|
42
|
+
query = a.Value
|
|
43
|
+
|
|
44
|
+
if not query.startswith("EVALUATE"):
|
|
45
|
+
daxquery = "EVALUATE \n" + query
|
|
46
|
+
else:
|
|
47
|
+
daxquery = query
|
|
48
|
+
|
|
49
|
+
try:
|
|
50
|
+
df = fabric.evaluate_dax(
|
|
51
|
+
dataset=dataset,
|
|
52
|
+
dax_string=daxquery,
|
|
53
|
+
workspace=workspace,
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
# Update column names for non-field parameters
|
|
57
|
+
if query.find("NAMEOF") == -1:
|
|
58
|
+
for old_column_name in df.columns:
|
|
59
|
+
pattern = r"\[([^\]]+)\]"
|
|
60
|
+
|
|
61
|
+
matches = re.findall(pattern, old_column_name)
|
|
62
|
+
new_column_name = matches[0]
|
|
63
|
+
new_column_name = new_column_name.replace(" ", "")
|
|
64
|
+
|
|
65
|
+
df.rename(
|
|
66
|
+
columns={old_column_name: new_column_name},
|
|
67
|
+
inplace=True,
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
# Update data types for lakehouse columns
|
|
71
|
+
dataType = next(
|
|
72
|
+
str(c.DataType)
|
|
73
|
+
for c in tom.all_columns()
|
|
74
|
+
if c.Parent.Name == tName
|
|
75
|
+
and c.SourceColumn == new_column_name
|
|
76
|
+
)
|
|
77
|
+
# dfC_type = dfC[(dfC['Table Name'] == tName) & (dfC['Source'] == new_column_name)]
|
|
78
|
+
# dataType = dfC_type['Data Type'].iloc[0]
|
|
79
|
+
|
|
80
|
+
if dataType == "Int64":
|
|
81
|
+
df[new_column_name] = df[
|
|
82
|
+
new_column_name
|
|
83
|
+
].astype(int)
|
|
84
|
+
elif dataType in ["Decimal", "Double"]:
|
|
85
|
+
df[new_column_name] = df[
|
|
86
|
+
new_column_name
|
|
87
|
+
].astype(float)
|
|
88
|
+
elif dataType == "Boolean":
|
|
89
|
+
df[new_column_name] = df[
|
|
90
|
+
new_column_name
|
|
91
|
+
].astype(bool)
|
|
92
|
+
elif dataType == "DateTime":
|
|
93
|
+
df[new_column_name] = pd.to_datetime(
|
|
94
|
+
df[new_column_name]
|
|
95
|
+
)
|
|
96
|
+
else:
|
|
97
|
+
df[new_column_name] = df[
|
|
98
|
+
new_column_name
|
|
99
|
+
].astype(str)
|
|
100
|
+
# else:
|
|
101
|
+
# second_column_name = df.columns[1]
|
|
102
|
+
# third_column_name = df.columns[2]
|
|
103
|
+
# df[third_column_name] = df[third_column_name].astype(int)
|
|
104
|
+
|
|
105
|
+
# Remove calc columns from field parameters
|
|
106
|
+
# mask = df[second_column_name].isin(dfC_filt['Full Column Name'])
|
|
107
|
+
# df = df[~mask]
|
|
108
|
+
|
|
109
|
+
delta_table_name = tName.replace(" ", "_")
|
|
110
|
+
print(
|
|
111
|
+
f"{icons.in_progress} Refresh of the '{delta_table_name}' table within the lakehouse is in progress..."
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
spark_df = spark.createDataFrame(df)
|
|
115
|
+
spark_df.write.mode("overwrite").format(
|
|
116
|
+
"delta"
|
|
117
|
+
).saveAsTable(delta_table_name)
|
|
118
|
+
print(
|
|
119
|
+
f"{icons.green_dot} Calculated table '{tName}' has been refreshed as the '{delta_table_name.lower()}' table in the lakehouse."
|
|
120
|
+
)
|
|
121
|
+
except:
|
|
122
|
+
print(
|
|
123
|
+
f"{icons.red_dot} Failed to create calculated table '{tName}' as a delta table in the lakehouse."
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
except Exception as e:
|
|
127
|
+
if datetime.datetime.now() - start_time > timeout:
|
|
128
|
+
break
|
|
129
|
+
time.sleep(1)
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
from sempy_labs.report._generate_report import (
|
|
2
|
+
create_report_from_reportjson,
|
|
3
|
+
update_report_from_reportjson,
|
|
4
|
+
)
|
|
5
|
+
from sempy_labs.report._report_functions import (
|
|
6
|
+
get_report_json,
|
|
7
|
+
# report_dependency_tree,
|
|
8
|
+
export_report,
|
|
9
|
+
clone_report,
|
|
10
|
+
launch_report,
|
|
11
|
+
# list_report_pages,
|
|
12
|
+
# list_report_visuals,
|
|
13
|
+
# list_report_bookmarks,
|
|
14
|
+
# translate_report_titles
|
|
15
|
+
)
|
|
16
|
+
from sempy_labs.report._report_rebind import (
|
|
17
|
+
report_rebind,
|
|
18
|
+
report_rebind_all,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
__all__ = [
|
|
22
|
+
"create_report_from_reportjson",
|
|
23
|
+
"update_report_from_reportjson",
|
|
24
|
+
"get_report_json",
|
|
25
|
+
# report_dependency_tree,
|
|
26
|
+
"export_report",
|
|
27
|
+
"clone_report",
|
|
28
|
+
"launch_report",
|
|
29
|
+
# list_report_pages,
|
|
30
|
+
# list_report_visuals,
|
|
31
|
+
# list_report_bookmarks,
|
|
32
|
+
# translate_report_titles,
|
|
33
|
+
"report_rebind",
|
|
34
|
+
"report_rebind_all",
|
|
35
|
+
]
|