semantic-link-labs 0.7.1__py3-none-any.whl → 0.7.3__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.7.1.dist-info → semantic_link_labs-0.7.3.dist-info}/METADATA +3 -2
- {semantic_link_labs-0.7.1.dist-info → semantic_link_labs-0.7.3.dist-info}/RECORD +35 -28
- {semantic_link_labs-0.7.1.dist-info → semantic_link_labs-0.7.3.dist-info}/WHEEL +1 -1
- sempy_labs/__init__.py +60 -3
- sempy_labs/_bpa_translation/_translations_sv-SE.po +914 -0
- sempy_labs/_clear_cache.py +298 -3
- sempy_labs/_dataflows.py +130 -0
- sempy_labs/_deployment_pipelines.py +171 -0
- sempy_labs/_generate_semantic_model.py +148 -27
- sempy_labs/_git.py +380 -0
- sempy_labs/_helper_functions.py +57 -0
- sempy_labs/_list_functions.py +144 -121
- sempy_labs/_model_bpa.py +85 -83
- sempy_labs/_model_bpa_bulk.py +3 -1
- sempy_labs/_model_bpa_rules.py +788 -800
- sempy_labs/_query_scale_out.py +15 -3
- sempy_labs/_sql.py +96 -0
- sempy_labs/_translations.py +0 -1
- sempy_labs/_workspace_identity.py +66 -0
- sempy_labs/directlake/__init__.py +2 -0
- sempy_labs/directlake/_directlake_schema_compare.py +1 -2
- sempy_labs/directlake/_dl_helper.py +4 -7
- sempy_labs/directlake/_generate_shared_expression.py +85 -0
- sempy_labs/directlake/_show_unsupported_directlake_objects.py +1 -2
- sempy_labs/lakehouse/_get_lakehouse_tables.py +7 -3
- sempy_labs/migration/_migrate_calctables_to_lakehouse.py +5 -0
- sempy_labs/migration/_migrate_calctables_to_semantic_model.py +5 -0
- sempy_labs/migration/_migrate_model_objects_to_semantic_model.py +6 -2
- sempy_labs/migration/_migrate_tables_columns_to_semantic_model.py +6 -5
- sempy_labs/migration/_migration_validation.py +6 -0
- sempy_labs/report/_report_functions.py +21 -42
- sempy_labs/report/_report_rebind.py +5 -0
- sempy_labs/tom/_model.py +91 -52
- {semantic_link_labs-0.7.1.dist-info → semantic_link_labs-0.7.3.dist-info}/LICENSE +0 -0
- {semantic_link_labs-0.7.1.dist-info → semantic_link_labs-0.7.3.dist-info}/top_level.txt +0 -0
|
@@ -13,12 +13,14 @@ from sempy_labs._helper_functions import (
|
|
|
13
13
|
)
|
|
14
14
|
from sempy_labs.lakehouse._lakehouse import lakehouse_attached
|
|
15
15
|
import sempy_labs._icons as icons
|
|
16
|
+
from sempy_labs._refresh_semantic_model import refresh_semantic_model
|
|
16
17
|
|
|
17
18
|
|
|
18
19
|
def create_blank_semantic_model(
|
|
19
20
|
dataset: str,
|
|
20
21
|
compatibility_level: int = 1605,
|
|
21
22
|
workspace: Optional[str] = None,
|
|
23
|
+
overwrite: Optional[bool] = True,
|
|
22
24
|
):
|
|
23
25
|
"""
|
|
24
26
|
Creates a new blank semantic model (no tables/columns etc.).
|
|
@@ -33,34 +35,42 @@ def create_blank_semantic_model(
|
|
|
33
35
|
The Fabric workspace name.
|
|
34
36
|
Defaults to None which resolves to the workspace of the attached lakehouse
|
|
35
37
|
or if no lakehouse attached, resolves to the workspace of the notebook.
|
|
38
|
+
overwrite : bool, default=False
|
|
39
|
+
If set to True, overwrites the existing semantic model in the workspace if it exists.
|
|
36
40
|
"""
|
|
37
41
|
|
|
38
42
|
workspace = fabric.resolve_workspace_name(workspace)
|
|
43
|
+
dfD = fabric.list_datasets(workspace=workspace, mode="rest")
|
|
44
|
+
dfD_filt = dfD[dfD["Dataset Name"] == dataset]
|
|
39
45
|
|
|
40
|
-
|
|
46
|
+
if len(dfD_filt) > 0 and not overwrite:
|
|
47
|
+
raise ValueError(
|
|
48
|
+
f"{icons.warning} The '{dataset}' semantic model already exists within the '{workspace}' workspace. The 'overwrite' parameter is set to False so the blank new semantic model was not created."
|
|
49
|
+
)
|
|
41
50
|
|
|
51
|
+
min_compat = 1500
|
|
42
52
|
if compatibility_level < min_compat:
|
|
43
53
|
raise ValueError(
|
|
44
54
|
f"{icons.red_dot} Compatiblity level must be at least {min_compat}."
|
|
45
55
|
)
|
|
46
56
|
|
|
47
57
|
tmsl = f"""
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
58
|
+
{{
|
|
59
|
+
"createOrReplace": {{
|
|
60
|
+
"object": {{
|
|
61
|
+
"database": '{dataset}'
|
|
62
|
+
}},
|
|
63
|
+
"database": {{
|
|
64
|
+
"name": '{dataset}',
|
|
65
|
+
"compatibilityLevel": {compatibility_level},
|
|
66
|
+
"model": {{
|
|
67
|
+
"culture": "en-US",
|
|
68
|
+
"defaultPowerBIDataSourceVersion": "powerBI_V3"
|
|
69
|
+
}}
|
|
70
|
+
}}
|
|
59
71
|
}}
|
|
60
|
-
}}
|
|
61
72
|
}}
|
|
62
|
-
|
|
63
|
-
"""
|
|
73
|
+
"""
|
|
64
74
|
|
|
65
75
|
fabric.execute_tmsl(script=tmsl, workspace=workspace)
|
|
66
76
|
|
|
@@ -89,12 +99,12 @@ def create_semantic_model_from_bim(
|
|
|
89
99
|
|
|
90
100
|
(workspace, workspace_id) = resolve_workspace_name_and_id(workspace)
|
|
91
101
|
|
|
92
|
-
dfI = fabric.
|
|
93
|
-
dfI_filt = dfI[(dfI["
|
|
102
|
+
dfI = fabric.list_datasets(workspace=workspace, mode="rest")
|
|
103
|
+
dfI_filt = dfI[(dfI["Dataset Name"] == dataset)]
|
|
94
104
|
|
|
95
105
|
if len(dfI_filt) > 0:
|
|
96
106
|
raise ValueError(
|
|
97
|
-
f"{icons.red_dot} '{dataset}' already exists as a semantic model in the '{workspace}' workspace."
|
|
107
|
+
f"{icons.red_dot} The '{dataset}' semantic model already exists as a semantic model in the '{workspace}' workspace."
|
|
98
108
|
)
|
|
99
109
|
|
|
100
110
|
client = fabric.FabricRestClient()
|
|
@@ -133,12 +143,77 @@ def create_semantic_model_from_bim(
|
|
|
133
143
|
)
|
|
134
144
|
|
|
135
145
|
|
|
146
|
+
def update_semantic_model_from_bim(
|
|
147
|
+
dataset: str, bim_file: dict, workspace: Optional[str] = None
|
|
148
|
+
):
|
|
149
|
+
"""
|
|
150
|
+
Updates a semantic model definition based on a Model.bim file.
|
|
151
|
+
|
|
152
|
+
Parameters
|
|
153
|
+
----------
|
|
154
|
+
dataset : str
|
|
155
|
+
Name of the semantic model.
|
|
156
|
+
bim_file : dict
|
|
157
|
+
The model.bim file.
|
|
158
|
+
workspace : str, default=None
|
|
159
|
+
The Fabric workspace name.
|
|
160
|
+
Defaults to None which resolves to the workspace of the attached lakehouse
|
|
161
|
+
or if no lakehouse attached, resolves to the workspace of the notebook.
|
|
162
|
+
"""
|
|
163
|
+
|
|
164
|
+
(workspace, workspace_id) = resolve_workspace_name_and_id(workspace)
|
|
165
|
+
|
|
166
|
+
dfD = fabric.list_datasets(workspace=workspace, mode="rest")
|
|
167
|
+
dfD_filt = dfD[dfD["Dataset Name"] == dataset]
|
|
168
|
+
if len(dfD_filt) == 0:
|
|
169
|
+
raise ValueError(
|
|
170
|
+
f"{icons.red_dot} The '{dataset}' semantic model within the '{workspace}' workspace does not exist."
|
|
171
|
+
)
|
|
172
|
+
dataset_id = dfD_filt["Dataset Id"].iloc[0]
|
|
173
|
+
|
|
174
|
+
client = fabric.FabricRestClient()
|
|
175
|
+
defPBIDataset = {"version": "1.0", "settings": {}}
|
|
176
|
+
|
|
177
|
+
payloadPBIDefinition = _conv_b64(defPBIDataset)
|
|
178
|
+
payloadBim = _conv_b64(bim_file)
|
|
179
|
+
|
|
180
|
+
request_body = {
|
|
181
|
+
"displayName": dataset,
|
|
182
|
+
"definition": {
|
|
183
|
+
"parts": [
|
|
184
|
+
{
|
|
185
|
+
"path": "model.bim",
|
|
186
|
+
"payload": payloadBim,
|
|
187
|
+
"payloadType": "InlineBase64",
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
"path": "definition.pbidataset",
|
|
191
|
+
"payload": payloadPBIDefinition,
|
|
192
|
+
"payloadType": "InlineBase64",
|
|
193
|
+
},
|
|
194
|
+
]
|
|
195
|
+
},
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
response = client.post(
|
|
199
|
+
f"/v1/workspaces/{workspace_id}/semanticModels/{dataset_id}/updateDefinition",
|
|
200
|
+
json=request_body,
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
lro(client, response, status_codes=[200, 202])
|
|
204
|
+
|
|
205
|
+
print(
|
|
206
|
+
f"{icons.green_dot} The '{dataset}' semantic model has been updated within the '{workspace}' workspace."
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
|
|
136
210
|
def deploy_semantic_model(
|
|
137
211
|
source_dataset: str,
|
|
138
212
|
source_workspace: Optional[str] = None,
|
|
139
213
|
target_dataset: Optional[str] = None,
|
|
140
214
|
target_workspace: Optional[str] = None,
|
|
141
215
|
refresh_target_dataset: Optional[bool] = True,
|
|
216
|
+
overwrite: Optional[bool] = False,
|
|
142
217
|
):
|
|
143
218
|
"""
|
|
144
219
|
Deploys a semantic model based on an existing semantic model.
|
|
@@ -159,14 +234,10 @@ def deploy_semantic_model(
|
|
|
159
234
|
or if no lakehouse attached, resolves to the workspace of the notebook.
|
|
160
235
|
refresh_target_dataset : bool, default=True
|
|
161
236
|
If set to True, this will initiate a full refresh of the target semantic model in the target workspace.
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
-------
|
|
165
|
-
|
|
237
|
+
overwrite : bool, default=False
|
|
238
|
+
If set to True, overwrites the existing semantic model in the workspace if it exists.
|
|
166
239
|
"""
|
|
167
240
|
|
|
168
|
-
from sempy_labs import refresh_semantic_model
|
|
169
|
-
|
|
170
241
|
source_workspace = fabric.resolve_workspace_name(source_workspace)
|
|
171
242
|
|
|
172
243
|
if target_workspace is None:
|
|
@@ -181,11 +252,28 @@ def deploy_semantic_model(
|
|
|
181
252
|
f"parameters have the same value. At least one of these must be different. Please update the parameters."
|
|
182
253
|
)
|
|
183
254
|
|
|
255
|
+
dfD = fabric.list_datasets(workspace=target_workspace, mode="rest")
|
|
256
|
+
dfD_filt = dfD[dfD["Dataset Name"] == target_dataset]
|
|
257
|
+
if len(dfD_filt) > 0 and not overwrite:
|
|
258
|
+
raise ValueError(
|
|
259
|
+
f"{icons.warning} The '{target_dataset}' semantic model already exists within the '{target_workspace}' workspace. The 'overwrite' parameter is set to False so the source semantic model was not deployed to the target destination."
|
|
260
|
+
)
|
|
261
|
+
|
|
184
262
|
bim = get_semantic_model_bim(dataset=source_dataset, workspace=source_workspace)
|
|
185
263
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
264
|
+
# Create the semantic model if the model does not exist
|
|
265
|
+
if len(dfD_filt) == 0:
|
|
266
|
+
create_semantic_model_from_bim(
|
|
267
|
+
dataset=target_dataset,
|
|
268
|
+
bim_file=bim,
|
|
269
|
+
workspace=target_workspace,
|
|
270
|
+
overwrite=overwrite,
|
|
271
|
+
)
|
|
272
|
+
# Update the semantic model if the model exists
|
|
273
|
+
else:
|
|
274
|
+
update_semantic_model_from_bim(
|
|
275
|
+
dataset=target_dataset, bim_file=bim, workspace=target_workspace
|
|
276
|
+
)
|
|
189
277
|
|
|
190
278
|
if refresh_target_dataset:
|
|
191
279
|
refresh_semantic_model(dataset=target_dataset, workspace=target_workspace)
|
|
@@ -257,3 +345,36 @@ def get_semantic_model_bim(
|
|
|
257
345
|
)
|
|
258
346
|
|
|
259
347
|
return bimJson
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
def get_semantic_model_size(dataset: str, workspace: Optional[str] = None):
|
|
351
|
+
|
|
352
|
+
workspace = fabric.resolve_workspace_name(workspace)
|
|
353
|
+
|
|
354
|
+
dict = fabric.evaluate_dax(
|
|
355
|
+
dataset=dataset,
|
|
356
|
+
workspace=workspace,
|
|
357
|
+
dax_string="""
|
|
358
|
+
EVALUATE SELECTCOLUMNS(FILTER(INFO.STORAGETABLECOLUMNS(), [COLUMN_TYPE] = "BASIC_DATA"),[DICTIONARY_SIZE])
|
|
359
|
+
""",
|
|
360
|
+
)
|
|
361
|
+
|
|
362
|
+
used_size = fabric.evaluate_dax(
|
|
363
|
+
dataset=dataset,
|
|
364
|
+
workspace=workspace,
|
|
365
|
+
dax_string="""
|
|
366
|
+
EVALUATE SELECTCOLUMNS(INFO.STORAGETABLECOLUMNSEGMENTS(),[USED_SIZE])
|
|
367
|
+
""",
|
|
368
|
+
)
|
|
369
|
+
dict_size = dict["[DICTIONARY_SIZE]"].sum()
|
|
370
|
+
used_size = used_size["[USED_SIZE]"].sum()
|
|
371
|
+
model_size = dict_size + used_size
|
|
372
|
+
# Calculate proper bytes size by dividing by 1024 and multiplying by 1000 - per 1000
|
|
373
|
+
if model_size >= 10**9:
|
|
374
|
+
result = model_size / (1024**3) * 10**9
|
|
375
|
+
elif model_size >= 10**6:
|
|
376
|
+
result = model_size / (1024**2) * 10**6
|
|
377
|
+
elif model_size >= 10**3:
|
|
378
|
+
result = model_size / (1024) * 10**3
|
|
379
|
+
|
|
380
|
+
return result
|
sempy_labs/_git.py
ADDED
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
import sempy.fabric as fabric
|
|
2
|
+
import pandas as pd
|
|
3
|
+
import sempy_labs._icons as icons
|
|
4
|
+
from typing import Optional, List
|
|
5
|
+
from sempy_labs._helper_functions import (
|
|
6
|
+
resolve_workspace_name_and_id,
|
|
7
|
+
lro,
|
|
8
|
+
)
|
|
9
|
+
from sempy.fabric.exceptions import FabricHTTPException
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def connect_workspace_to_git(
|
|
13
|
+
organization_name: str,
|
|
14
|
+
project_name: str,
|
|
15
|
+
repository_name: str,
|
|
16
|
+
branch_name: str,
|
|
17
|
+
directory_name: str,
|
|
18
|
+
git_provider_type: str = "AzureDevOps",
|
|
19
|
+
workspace: Optional[str] = None,
|
|
20
|
+
):
|
|
21
|
+
"""
|
|
22
|
+
Connects a workspace to a git repository.
|
|
23
|
+
|
|
24
|
+
Parameters
|
|
25
|
+
----------
|
|
26
|
+
organization_name : str
|
|
27
|
+
The organization name.
|
|
28
|
+
project_name : str
|
|
29
|
+
The project name.
|
|
30
|
+
repository_name : str
|
|
31
|
+
The repository name.
|
|
32
|
+
branch_name : str
|
|
33
|
+
The branch name.
|
|
34
|
+
directory_name : str
|
|
35
|
+
The directory name.
|
|
36
|
+
git_provider_type : str, default="AzureDevOps"
|
|
37
|
+
A `Git provider type <https://learn.microsoft.com/rest/api/fabric/core/git/connect?tabs=HTTP#gitprovidertype>`_.
|
|
38
|
+
workspace : str, default=None
|
|
39
|
+
The Fabric workspace name.
|
|
40
|
+
Defaults to None which resolves to the workspace of the attached lakehouse
|
|
41
|
+
or if no lakehouse attached, resolves to the workspace of the notebook.
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
# https://learn.microsoft.com/en-us/rest/api/fabric/core/git/connect?tabs=HTTP
|
|
45
|
+
|
|
46
|
+
workspace, workspace_id = resolve_workspace_name_and_id(workspace)
|
|
47
|
+
|
|
48
|
+
request_body = {
|
|
49
|
+
"gitProviderDetails": {
|
|
50
|
+
"organizationName": organization_name,
|
|
51
|
+
"projectName": project_name,
|
|
52
|
+
"gitProviderType": git_provider_type,
|
|
53
|
+
"repositoryName": repository_name,
|
|
54
|
+
"branchName": branch_name,
|
|
55
|
+
"directoryName": directory_name,
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
client = fabric.FabricRestClient()
|
|
60
|
+
response = client.post(
|
|
61
|
+
f"/v1/workspaces/{workspace_id}/git/connect", json=request_body
|
|
62
|
+
)
|
|
63
|
+
if response.status_code != 200:
|
|
64
|
+
raise FabricHTTPException(response)
|
|
65
|
+
|
|
66
|
+
print(
|
|
67
|
+
f"{icons.green_dot} The '{workspace}' workspace has been connected to the '{project_name}' Git project within the '{repository_name}' repository."
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def disconnect_workspace_from_git(workspace: Optional[str] = None):
|
|
72
|
+
"""
|
|
73
|
+
Disconnects a workpsace from a git repository.
|
|
74
|
+
|
|
75
|
+
Parameters
|
|
76
|
+
----------
|
|
77
|
+
workspace : str, default=None
|
|
78
|
+
The Fabric workspace name.
|
|
79
|
+
Defaults to None which resolves to the workspace of the attached lakehouse
|
|
80
|
+
or if no lakehouse attached, resolves to the workspace of the notebook.
|
|
81
|
+
"""
|
|
82
|
+
|
|
83
|
+
# https://learn.microsoft.com/en-us/rest/api/fabric/core/git/disconnect?tabs=HTTP
|
|
84
|
+
|
|
85
|
+
workspace, workspace_id = resolve_workspace_name_and_id(workspace)
|
|
86
|
+
|
|
87
|
+
client = fabric.FabricRestClient()
|
|
88
|
+
response = client.post(f"/v1/workspaces/{workspace_id}/git/disconnect")
|
|
89
|
+
if response.status_code != 200:
|
|
90
|
+
raise FabricHTTPException(response)
|
|
91
|
+
|
|
92
|
+
print(
|
|
93
|
+
f"{icons.green_dot} The '{workspace}' workspace has been disconnected from Git."
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def get_git_status(workspace: Optional[str] = None) -> pd.DataFrame:
|
|
98
|
+
"""
|
|
99
|
+
Obtains the Git status of items in the workspace, that can be committed to Git.
|
|
100
|
+
|
|
101
|
+
Parameters
|
|
102
|
+
----------
|
|
103
|
+
workspace : str, default=None
|
|
104
|
+
The Fabric workspace name.
|
|
105
|
+
Defaults to None which resolves to the workspace of the attached lakehouse
|
|
106
|
+
or if no lakehouse attached, resolves to the workspace of the notebook.
|
|
107
|
+
|
|
108
|
+
Returns
|
|
109
|
+
-------
|
|
110
|
+
pandas.DataFrame
|
|
111
|
+
A pandas dataframe showing the Git status of items in the workspace.
|
|
112
|
+
"""
|
|
113
|
+
|
|
114
|
+
# https://learn.microsoft.com/en-us/rest/api/fabric/core/git/get-status?tabs=HTTP
|
|
115
|
+
|
|
116
|
+
workspace, workspace_id = resolve_workspace_name_and_id(workspace)
|
|
117
|
+
|
|
118
|
+
df = pd.DataFrame(
|
|
119
|
+
columns=[
|
|
120
|
+
"Workspace Head",
|
|
121
|
+
"Remote Commit Hash",
|
|
122
|
+
"Object ID",
|
|
123
|
+
"Logical ID",
|
|
124
|
+
"Item Type",
|
|
125
|
+
"Item Name",
|
|
126
|
+
"Workspace Change",
|
|
127
|
+
"Remote Change",
|
|
128
|
+
"Conflict Type",
|
|
129
|
+
]
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
client = fabric.FabricRestClient()
|
|
133
|
+
response = client.get(f"/v1/workspaces/{workspace_id}/git/status")
|
|
134
|
+
|
|
135
|
+
if response not in [200, 202]:
|
|
136
|
+
raise FabricHTTPException(response)
|
|
137
|
+
|
|
138
|
+
result = lro(client, response).json()
|
|
139
|
+
|
|
140
|
+
for v in result.get("value", []):
|
|
141
|
+
changes = v.get("changes", [])
|
|
142
|
+
item_metadata = changes.get("itemMetadata", {})
|
|
143
|
+
item_identifier = item_metadata.get("itemIdentifier", {})
|
|
144
|
+
|
|
145
|
+
new_data = {
|
|
146
|
+
"Workspace Head": v.get("workspaceHead"),
|
|
147
|
+
"Remote Commit Hash": v.get("remoteCommitHash"),
|
|
148
|
+
"Object ID": item_identifier.get("objectId"),
|
|
149
|
+
"Logical ID": item_identifier.get("logicalId"),
|
|
150
|
+
"Item Type": item_metadata.get("itemType"),
|
|
151
|
+
"Item Name": item_metadata.get("displayName"),
|
|
152
|
+
"Remote Change": changes.get("remoteChange"),
|
|
153
|
+
"Workspace Change": changes.get("workspaceChange"),
|
|
154
|
+
"Conflict Type": changes.get("conflictType"),
|
|
155
|
+
}
|
|
156
|
+
df = pd.concat([df, pd.DataFrame(new_data, index=[0])], ignore_index=True)
|
|
157
|
+
|
|
158
|
+
return df
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def get_git_connection(workspace: Optional[str] = None) -> pd.DataFrame:
|
|
162
|
+
"""
|
|
163
|
+
Obtains the Git status of items in the workspace, that can be committed to Git.
|
|
164
|
+
|
|
165
|
+
Parameters
|
|
166
|
+
----------
|
|
167
|
+
workspace : str, default=None
|
|
168
|
+
The Fabric workspace name.
|
|
169
|
+
Defaults to None which resolves to the workspace of the attached lakehouse
|
|
170
|
+
or if no lakehouse attached, resolves to the workspace of the notebook.
|
|
171
|
+
|
|
172
|
+
Returns
|
|
173
|
+
-------
|
|
174
|
+
pandas.DataFrame
|
|
175
|
+
A pandas dataframe showing the Git status of items in the workspace.
|
|
176
|
+
"""
|
|
177
|
+
|
|
178
|
+
# https://learn.microsoft.com/en-us/rest/api/fabric/core/git/get-status?tabs=HTTP
|
|
179
|
+
|
|
180
|
+
workspace, workspace_id = resolve_workspace_name_and_id(workspace)
|
|
181
|
+
|
|
182
|
+
df = pd.DataFrame(
|
|
183
|
+
columns=[
|
|
184
|
+
"Organization Name",
|
|
185
|
+
"Project Name",
|
|
186
|
+
"Git Provider Type",
|
|
187
|
+
"Repository Name",
|
|
188
|
+
"Branch Name",
|
|
189
|
+
"Directory Name",
|
|
190
|
+
"Workspace Head",
|
|
191
|
+
"Last Sync Time",
|
|
192
|
+
"Git Connection State",
|
|
193
|
+
]
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
client = fabric.FabricRestClient()
|
|
197
|
+
response = client.get(f"/v1/workspaces/{workspace_id}/git/connection")
|
|
198
|
+
|
|
199
|
+
if response.status_code != 200:
|
|
200
|
+
raise FabricHTTPException(response)
|
|
201
|
+
|
|
202
|
+
for v in response.json().get("value", []):
|
|
203
|
+
provider_details = v.get("gitProviderDetails", {})
|
|
204
|
+
sync_details = v.get("gitSyncDetails", {})
|
|
205
|
+
new_data = {
|
|
206
|
+
"Organization Name": provider_details.get("organizationName"),
|
|
207
|
+
"Project Name": provider_details.get("projectName"),
|
|
208
|
+
"Git Provider Type": provider_details.get("gitProviderType"),
|
|
209
|
+
"Repository Name": provider_details.get("repositoryName"),
|
|
210
|
+
"Branch Name": provider_details.get("branchName"),
|
|
211
|
+
"Directory Name": provider_details.get("directoryName"),
|
|
212
|
+
"Workspace Head": sync_details.get("head"),
|
|
213
|
+
"Last Sync Time": sync_details.get("lastSyncTime"),
|
|
214
|
+
"Git Conneciton State": v.get("gitConnectionState"),
|
|
215
|
+
}
|
|
216
|
+
df = pd.concat([df, pd.DataFrame(new_data, index=[0])], ignore_index=True)
|
|
217
|
+
|
|
218
|
+
return df
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def initialize_git_connection(workspace: Optional[str] = None):
|
|
222
|
+
"""
|
|
223
|
+
Initializes a connection for a workspace that is connected to Git.
|
|
224
|
+
|
|
225
|
+
Parameters
|
|
226
|
+
----------
|
|
227
|
+
workspace : str, default=None
|
|
228
|
+
The Fabric workspace name.
|
|
229
|
+
Defaults to None which resolves to the workspace of the attached lakehouse
|
|
230
|
+
or if no lakehouse attached, resolves to the workspace of the notebook.
|
|
231
|
+
"""
|
|
232
|
+
|
|
233
|
+
# https://learn.microsoft.com/en-us/rest/api/fabric/core/git/initialize-connection?tabs=HTTP
|
|
234
|
+
|
|
235
|
+
workspace, workspace_id = resolve_workspace_name_and_id(workspace)
|
|
236
|
+
|
|
237
|
+
client = fabric.FabricRestClient()
|
|
238
|
+
response = client.post(f"/v1/workspaces/{workspace_id}/git/initializeConnection")
|
|
239
|
+
|
|
240
|
+
if response not in [200, 202]:
|
|
241
|
+
raise FabricHTTPException(response)
|
|
242
|
+
|
|
243
|
+
lro(client, response)
|
|
244
|
+
|
|
245
|
+
print(
|
|
246
|
+
f"{icons.green_dot} The '{workspace}' workspace git connection has been initialized."
|
|
247
|
+
)
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
def commit_to_git(
|
|
251
|
+
comment: str, item_ids: str | List[str] = None, workspace: Optional[str] = None
|
|
252
|
+
):
|
|
253
|
+
"""
|
|
254
|
+
Commits all or a selection of items within a workspace to Git.
|
|
255
|
+
|
|
256
|
+
Parameters
|
|
257
|
+
----------
|
|
258
|
+
comment : str
|
|
259
|
+
The Git commit comment.
|
|
260
|
+
item_ids : str | List[str], default=None
|
|
261
|
+
A list of item Ids to commit to Git.
|
|
262
|
+
Defaults to None which commits all items to Git.
|
|
263
|
+
workspace : str, default=None
|
|
264
|
+
The Fabric workspace name.
|
|
265
|
+
Defaults to None which resolves to the workspace of the attached lakehouse
|
|
266
|
+
or if no lakehouse attached, resolves to the workspace of the notebook.
|
|
267
|
+
"""
|
|
268
|
+
|
|
269
|
+
# https://learn.microsoft.com/en-us/rest/api/fabric/core/git/commit-to-git?tabs=HTTP
|
|
270
|
+
|
|
271
|
+
workspace, workspace_id = resolve_workspace_name_and_id(workspace)
|
|
272
|
+
|
|
273
|
+
gs = get_git_status(workspace=workspace)
|
|
274
|
+
workspace_head = gs["Workspace Head"].iloc[0]
|
|
275
|
+
|
|
276
|
+
if item_ids is None:
|
|
277
|
+
commit_mode = "All"
|
|
278
|
+
else:
|
|
279
|
+
commit_mode = "Selective"
|
|
280
|
+
|
|
281
|
+
if isinstance(item_ids, str):
|
|
282
|
+
item_ids = [item_ids]
|
|
283
|
+
|
|
284
|
+
request_body = {
|
|
285
|
+
"mode": commit_mode,
|
|
286
|
+
"workspaceHead": workspace_head,
|
|
287
|
+
"comment": comment,
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
if item_ids is not None:
|
|
291
|
+
request_body["items"] = [{"objectId": item_id} for item_id in item_ids]
|
|
292
|
+
|
|
293
|
+
client = fabric.FabricRestClient()
|
|
294
|
+
response = client.post(
|
|
295
|
+
f"/v1/workspaces/{workspace_id}/git/commitToGit",
|
|
296
|
+
json=request_body,
|
|
297
|
+
)
|
|
298
|
+
|
|
299
|
+
if response.status_code not in [200, 202]:
|
|
300
|
+
raise FabricHTTPException(response)
|
|
301
|
+
|
|
302
|
+
lro(client, response)
|
|
303
|
+
|
|
304
|
+
if commit_mode == "All":
|
|
305
|
+
print(
|
|
306
|
+
f"{icons.green_dot} All items within the '{workspace}' workspace have been committed to Git."
|
|
307
|
+
)
|
|
308
|
+
else:
|
|
309
|
+
print(
|
|
310
|
+
f"{icons.green_dot} The {item_ids} items ithin the '{workspace}' workspace have been committed to Git."
|
|
311
|
+
)
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
def update_from_git(
|
|
315
|
+
remote_commit_hash: str,
|
|
316
|
+
conflict_resolution_policy: str,
|
|
317
|
+
workspace_head: Optional[str] = None,
|
|
318
|
+
allow_override: Optional[bool] = False,
|
|
319
|
+
workspace: Optional[str] = None,
|
|
320
|
+
):
|
|
321
|
+
"""
|
|
322
|
+
Updates the workspace with commits pushed to the connected branch.
|
|
323
|
+
|
|
324
|
+
Parameters
|
|
325
|
+
----------
|
|
326
|
+
workspace_head : str
|
|
327
|
+
Full SHA hash that the workspace is synced to. This value may be null only after Initialize Connection.
|
|
328
|
+
In other cases, the system will validate that the given value is aligned with the head known to the system.
|
|
329
|
+
remove_commit_hash : str
|
|
330
|
+
Remote full SHA commit hash.
|
|
331
|
+
confilict_resolution_policy : str
|
|
332
|
+
The `conflict resolution policy <https://learn.microsoft.com/rest/api/fabric/core/git/update-from-git?tabs=HTTP#conflictresolutionpolicy>`_.
|
|
333
|
+
allow_override : bool, default=False
|
|
334
|
+
workspace : str, default=None
|
|
335
|
+
The Fabric workspace name.
|
|
336
|
+
Defaults to None which resolves to the workspace of the attached lakehouse
|
|
337
|
+
or if no lakehouse attached, resolves to the workspace of the notebook.
|
|
338
|
+
"""
|
|
339
|
+
|
|
340
|
+
# https://learn.microsoft.com/en-us/rest/api/fabric/core/git/update-from-git?tabs=HTTP
|
|
341
|
+
|
|
342
|
+
workspace, workspace_id = resolve_workspace_name_and_id(workspace)
|
|
343
|
+
|
|
344
|
+
conflict_resolution_policies = ["PreferWorkspace", "PreferRemote"]
|
|
345
|
+
if "remote" in conflict_resolution_policies.lower():
|
|
346
|
+
conflict_resolution_policies = "PreferRemote"
|
|
347
|
+
elif "workspace" in conflict_resolution_policies.lower():
|
|
348
|
+
conflict_resolution_policies = "PreferWorkspace"
|
|
349
|
+
|
|
350
|
+
if conflict_resolution_policy not in conflict_resolution_policies:
|
|
351
|
+
raise ValueError(
|
|
352
|
+
f"{icons.red_dot} Invalid conflict resolution policy. Valid options: {conflict_resolution_policies}."
|
|
353
|
+
)
|
|
354
|
+
|
|
355
|
+
request_body = {}
|
|
356
|
+
request_body["remoteCommitHash"] = remote_commit_hash
|
|
357
|
+
request_body["conflictResolution"] = {
|
|
358
|
+
"conflictResolutionType": "Workspace",
|
|
359
|
+
"conflictResolutionPolicy": conflict_resolution_policy,
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
if workspace_head is not None:
|
|
363
|
+
request_body["workspaceHead"] = workspace_head
|
|
364
|
+
if allow_override is not None:
|
|
365
|
+
request_body["options"] = {"allowOverrideItems": allow_override}
|
|
366
|
+
|
|
367
|
+
client = fabric.FabricRestClient()
|
|
368
|
+
response = client.post(
|
|
369
|
+
f"/v1/workspaces/{workspace_id}/git/updateFromGit",
|
|
370
|
+
json=request_body,
|
|
371
|
+
)
|
|
372
|
+
|
|
373
|
+
if response.status_code not in [200, 202]:
|
|
374
|
+
raise FabricHTTPException(response)
|
|
375
|
+
|
|
376
|
+
lro(client, response)
|
|
377
|
+
|
|
378
|
+
print(
|
|
379
|
+
f"{icons.green_dot} The '{workspace}' workspace has been updated with commits pushed to the connected branch."
|
|
380
|
+
)
|
sempy_labs/_helper_functions.py
CHANGED
|
@@ -12,6 +12,7 @@ from uuid import UUID
|
|
|
12
12
|
import sempy_labs._icons as icons
|
|
13
13
|
from sempy.fabric.exceptions import FabricHTTPException
|
|
14
14
|
import urllib.parse
|
|
15
|
+
from azure.core.credentials import TokenCredential, AccessToken
|
|
15
16
|
|
|
16
17
|
|
|
17
18
|
def create_abfss_path(
|
|
@@ -853,3 +854,59 @@ def pagination(client, response):
|
|
|
853
854
|
continuation_uri = response_json.get("continuationUri")
|
|
854
855
|
|
|
855
856
|
return responses
|
|
857
|
+
|
|
858
|
+
|
|
859
|
+
def resolve_deployment_pipeline_id(deployment_pipeline: str) -> UUID:
|
|
860
|
+
|
|
861
|
+
from sempy_labs._deployment_pipelines import list_deployment_pipelines
|
|
862
|
+
|
|
863
|
+
dfP = list_deployment_pipelines()
|
|
864
|
+
dfP_filt = dfP[dfP["Deployment Pipeline Name"] == deployment_pipeline]
|
|
865
|
+
if len(dfP_filt) == 0:
|
|
866
|
+
raise ValueError(
|
|
867
|
+
f"{icons.red_dot} The '{deployment_pipeline}' deployment pipeline is not valid."
|
|
868
|
+
)
|
|
869
|
+
deployment_pipeline_id = dfP_filt["Deployment Pipeline Id"].iloc[0]
|
|
870
|
+
|
|
871
|
+
return deployment_pipeline_id
|
|
872
|
+
|
|
873
|
+
|
|
874
|
+
class FabricTokenCredential(TokenCredential):
|
|
875
|
+
|
|
876
|
+
def get_token(
|
|
877
|
+
self,
|
|
878
|
+
scopes: str,
|
|
879
|
+
claims: Optional[str] = None,
|
|
880
|
+
tenant_id: Optional[str] = None,
|
|
881
|
+
enable_cae: Optional[bool] = False,
|
|
882
|
+
**kwargs: any,
|
|
883
|
+
) -> AccessToken:
|
|
884
|
+
|
|
885
|
+
from notebookutils import mssparkutils
|
|
886
|
+
token = mssparkutils.credentials.getToken(scopes)
|
|
887
|
+
access_token = AccessToken(token, 0)
|
|
888
|
+
|
|
889
|
+
return access_token
|
|
890
|
+
|
|
891
|
+
|
|
892
|
+
def get_adls_client(account_name):
|
|
893
|
+
|
|
894
|
+
from azure.storage.filedatalake import DataLakeServiceClient
|
|
895
|
+
|
|
896
|
+
account_url = f"https://{account_name}.dfs.core.windows.net"
|
|
897
|
+
|
|
898
|
+
service_client = DataLakeServiceClient(
|
|
899
|
+
account_url, credential=FabricTokenCredential()
|
|
900
|
+
)
|
|
901
|
+
|
|
902
|
+
return service_client
|
|
903
|
+
|
|
904
|
+
|
|
905
|
+
def resolve_warehouse_id(warehouse: str, workspace: Optional[str]):
|
|
906
|
+
|
|
907
|
+
workspace = fabric.resolve_workspace_name(workspace)
|
|
908
|
+
warehouse_id = fabric.resolve_item_id(
|
|
909
|
+
item_name=warehouse, type="Warehouse", workspace=workspace
|
|
910
|
+
)
|
|
911
|
+
|
|
912
|
+
return warehouse_id
|