semantic-link-labs 0.9.1__py3-none-any.whl → 0.9.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.9.1.dist-info → semantic_link_labs-0.9.3.dist-info}/METADATA +67 -8
- {semantic_link_labs-0.9.1.dist-info → semantic_link_labs-0.9.3.dist-info}/RECORD +87 -80
- sempy_labs/__init__.py +14 -12
- sempy_labs/_ai.py +8 -5
- sempy_labs/_capacities.py +120 -142
- sempy_labs/_capacity_migration.py +61 -94
- sempy_labs/_clear_cache.py +9 -8
- sempy_labs/_connections.py +107 -104
- sempy_labs/_data_pipelines.py +47 -49
- sempy_labs/_dataflows.py +45 -51
- sempy_labs/_dax.py +228 -6
- sempy_labs/_delta_analyzer.py +321 -0
- sempy_labs/_deployment_pipelines.py +72 -66
- sempy_labs/_environments.py +39 -36
- sempy_labs/_eventhouses.py +35 -35
- sempy_labs/_eventstreams.py +38 -39
- sempy_labs/_external_data_shares.py +29 -42
- sempy_labs/_gateways.py +103 -99
- sempy_labs/_generate_semantic_model.py +22 -30
- sempy_labs/_git.py +46 -66
- sempy_labs/_graphQL.py +95 -0
- sempy_labs/_helper_functions.py +227 -36
- sempy_labs/_job_scheduler.py +47 -59
- sempy_labs/_kql_databases.py +27 -34
- sempy_labs/_kql_querysets.py +23 -30
- sempy_labs/_list_functions.py +264 -167
- sempy_labs/_managed_private_endpoints.py +52 -47
- sempy_labs/_mirrored_databases.py +110 -134
- sempy_labs/_mirrored_warehouses.py +13 -13
- sempy_labs/_ml_experiments.py +36 -36
- sempy_labs/_ml_models.py +37 -38
- sempy_labs/_model_bpa.py +2 -2
- sempy_labs/_model_bpa_rules.py +8 -6
- sempy_labs/_model_dependencies.py +2 -0
- sempy_labs/_notebooks.py +28 -29
- sempy_labs/_one_lake_integration.py +2 -0
- sempy_labs/_query_scale_out.py +63 -81
- sempy_labs/_refresh_semantic_model.py +12 -14
- sempy_labs/_spark.py +54 -79
- sempy_labs/_sql.py +7 -11
- sempy_labs/_translations.py +2 -2
- sempy_labs/_vertipaq.py +11 -6
- sempy_labs/_warehouses.py +30 -33
- sempy_labs/_workloads.py +15 -20
- sempy_labs/_workspace_identity.py +13 -17
- sempy_labs/_workspaces.py +49 -48
- sempy_labs/admin/__init__.py +2 -0
- sempy_labs/admin/_basic_functions.py +244 -281
- sempy_labs/admin/_domains.py +186 -103
- sempy_labs/admin/_external_data_share.py +26 -31
- sempy_labs/admin/_git.py +17 -22
- sempy_labs/admin/_items.py +34 -48
- sempy_labs/admin/_scanner.py +61 -49
- sempy_labs/directlake/_directlake_schema_compare.py +2 -0
- sempy_labs/directlake/_dl_helper.py +10 -11
- sempy_labs/directlake/_generate_shared_expression.py +4 -5
- sempy_labs/directlake/_get_directlake_lakehouse.py +1 -0
- sempy_labs/directlake/_list_directlake_model_calc_tables.py +1 -0
- sempy_labs/directlake/_show_unsupported_directlake_objects.py +2 -0
- sempy_labs/directlake/_warm_cache.py +2 -0
- sempy_labs/graph/__init__.py +33 -0
- sempy_labs/graph/_groups.py +402 -0
- sempy_labs/graph/_teams.py +113 -0
- sempy_labs/graph/_users.py +191 -0
- sempy_labs/lakehouse/__init__.py +4 -0
- sempy_labs/lakehouse/_get_lakehouse_columns.py +12 -12
- sempy_labs/lakehouse/_get_lakehouse_tables.py +16 -22
- sempy_labs/lakehouse/_lakehouse.py +104 -7
- sempy_labs/lakehouse/_shortcuts.py +42 -20
- sempy_labs/migration/__init__.py +4 -0
- sempy_labs/migration/_direct_lake_to_import.py +66 -0
- sempy_labs/migration/_migrate_calctables_to_lakehouse.py +3 -2
- sempy_labs/migration/_migrate_calctables_to_semantic_model.py +1 -0
- sempy_labs/migration/_migrate_model_objects_to_semantic_model.py +1 -0
- sempy_labs/migration/_migrate_tables_columns_to_semantic_model.py +2 -0
- sempy_labs/migration/_refresh_calc_tables.py +2 -2
- sempy_labs/report/_download_report.py +8 -13
- sempy_labs/report/_generate_report.py +49 -46
- sempy_labs/report/_paginated.py +20 -26
- sempy_labs/report/_report_functions.py +52 -47
- sempy_labs/report/_report_list_functions.py +2 -0
- sempy_labs/report/_report_rebind.py +6 -10
- sempy_labs/report/_reportwrapper.py +187 -220
- sempy_labs/tom/_model.py +12 -6
- {semantic_link_labs-0.9.1.dist-info → semantic_link_labs-0.9.3.dist-info}/LICENSE +0 -0
- {semantic_link_labs-0.9.1.dist-info → semantic_link_labs-0.9.3.dist-info}/WHEEL +0 -0
- {semantic_link_labs-0.9.1.dist-info → semantic_link_labs-0.9.3.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
import pandas as pd
|
|
2
|
+
import datetime
|
|
3
|
+
from typing import Dict, Optional
|
|
4
|
+
import pyarrow.dataset as ds
|
|
5
|
+
import pyarrow.parquet as pq
|
|
6
|
+
from sempy_labs._helper_functions import (
|
|
7
|
+
create_abfss_path,
|
|
8
|
+
save_as_delta_table,
|
|
9
|
+
_get_column_aggregate,
|
|
10
|
+
_create_dataframe,
|
|
11
|
+
_update_dataframe_datatypes,
|
|
12
|
+
resolve_workspace_name_and_id,
|
|
13
|
+
resolve_lakehouse_name_and_id,
|
|
14
|
+
_read_delta_table,
|
|
15
|
+
_delta_table_row_count,
|
|
16
|
+
)
|
|
17
|
+
from sempy_labs.lakehouse._get_lakehouse_tables import get_lakehouse_tables
|
|
18
|
+
from sempy_labs.lakehouse._lakehouse import lakehouse_attached
|
|
19
|
+
import sempy_labs._icons as icons
|
|
20
|
+
from uuid import UUID
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def delta_analyzer(
|
|
24
|
+
table_name: str,
|
|
25
|
+
approx_distinct_count: bool = True,
|
|
26
|
+
export: bool = False,
|
|
27
|
+
lakehouse: Optional[str | UUID] = None,
|
|
28
|
+
workspace: Optional[str | UUID] = None,
|
|
29
|
+
) -> Dict[str, pd.DataFrame]:
|
|
30
|
+
"""
|
|
31
|
+
Analyzes a delta table and shows the results in dictionary containing a set of 5 dataframes. If 'export' is set to True, the results will be saved to delta tables in the lakehouse attached to the notebook.
|
|
32
|
+
|
|
33
|
+
The 5 dataframes returned by this function are:
|
|
34
|
+
|
|
35
|
+
* Summary
|
|
36
|
+
* Parquet Files
|
|
37
|
+
* Row Groups
|
|
38
|
+
* Column Chunks
|
|
39
|
+
* Columns
|
|
40
|
+
|
|
41
|
+
Read more about Delta Analyzer `here <https://github.com/microsoft/Analysis-Services/tree/master/DeltaAnalyzer>`_.
|
|
42
|
+
|
|
43
|
+
Parameters
|
|
44
|
+
----------
|
|
45
|
+
table_name : str
|
|
46
|
+
The delta table name.
|
|
47
|
+
approx_distinct_count: bool, default=True
|
|
48
|
+
If True, uses approx_count_distinct to calculate the cardinality of each column. If False, uses COUNT(DISTINCT) instead.
|
|
49
|
+
export : bool, default=False
|
|
50
|
+
If True, exports the resulting dataframes to delta tables in the lakehouse attached to the notebook.
|
|
51
|
+
lakehouse : str | uuid.UUID, default=None
|
|
52
|
+
The Fabric lakehouse name or ID.
|
|
53
|
+
Defaults to None which resolves to the lakehouse attached to the notebook.
|
|
54
|
+
workspace : str | uuid.UUID, default=None
|
|
55
|
+
The Fabric workspace name or ID used by the lakehouse.
|
|
56
|
+
Defaults to None which resolves to the workspace of the attached lakehouse
|
|
57
|
+
or if no lakehouse attached, resolves to the workspace of the notebook.
|
|
58
|
+
|
|
59
|
+
Returns
|
|
60
|
+
-------
|
|
61
|
+
Dict[str, pandas.DataFrame]
|
|
62
|
+
A dictionary of pandas dataframes showing semantic model objects which violated the best practice analyzer rules.
|
|
63
|
+
"""
|
|
64
|
+
import notebookutils
|
|
65
|
+
|
|
66
|
+
# display_toggle = notebookutils.common.configs.pandas_display
|
|
67
|
+
|
|
68
|
+
# Turn off notebookutils display
|
|
69
|
+
# if display_toggle is True:
|
|
70
|
+
# notebookutils.common.configs.pandas_display = False
|
|
71
|
+
|
|
72
|
+
prefix = "SLL_DeltaAnalyzer_"
|
|
73
|
+
now = datetime.datetime.now()
|
|
74
|
+
(workspace_name, workspace_id) = resolve_workspace_name_and_id(workspace=workspace)
|
|
75
|
+
(lakehouse_name, lakehouse_id) = resolve_lakehouse_name_and_id(
|
|
76
|
+
lakehouse=lakehouse, workspace=workspace
|
|
77
|
+
)
|
|
78
|
+
path = create_abfss_path(lakehouse_id, workspace_id, table_name)
|
|
79
|
+
lake_path = create_abfss_path(lakehouse_id, workspace_id)
|
|
80
|
+
mounts = notebookutils.fs.mounts()
|
|
81
|
+
mount_point = f"/{workspace_name.replace(' ', '')}{lakehouse_name.replace(' ', '')}"
|
|
82
|
+
if not any(i.get("source") == lake_path for i in mounts):
|
|
83
|
+
# Mount lakehouse if not mounted
|
|
84
|
+
notebookutils.fs.mount(lake_path, mount_point)
|
|
85
|
+
print(
|
|
86
|
+
f"{icons.green_dot} Mounted the '{lakehouse_name}' lakehouse within the '{workspace_name}' to the notebook."
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
mounts = notebookutils.fs.mounts()
|
|
90
|
+
local_path = next(
|
|
91
|
+
i.get("localPath") for i in mounts if i.get("source") == lake_path
|
|
92
|
+
)
|
|
93
|
+
table_path = f"{local_path}/Tables/{table_name}"
|
|
94
|
+
|
|
95
|
+
# Set back to original value
|
|
96
|
+
# notebookutils.common.configs.pandas_display = display_toggle
|
|
97
|
+
|
|
98
|
+
parquet_file_df_columns = {
|
|
99
|
+
"ParquetFile": "string",
|
|
100
|
+
"RowCount": "int",
|
|
101
|
+
"RowGroups": "int",
|
|
102
|
+
}
|
|
103
|
+
row_group_df_columns = {
|
|
104
|
+
"ParquetFile": "string",
|
|
105
|
+
"RowGroupID": "int",
|
|
106
|
+
"RowCount": "int",
|
|
107
|
+
"CompressedSize": "int",
|
|
108
|
+
"UncompressedSize": "int",
|
|
109
|
+
"CompressionRatio": "float",
|
|
110
|
+
}
|
|
111
|
+
column_chunk_df_columns = {
|
|
112
|
+
"ParquetFile": "string",
|
|
113
|
+
"ColumnID": "int",
|
|
114
|
+
"ColumnName": "string",
|
|
115
|
+
"ColumnType": "string",
|
|
116
|
+
"CompressedSize": "int",
|
|
117
|
+
"UncompressedSize": "int",
|
|
118
|
+
"HasDict": "bool",
|
|
119
|
+
"DictOffset": "int_fillna",
|
|
120
|
+
"ValueCount": "int",
|
|
121
|
+
"Encodings": "string",
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
parquet_file_df = _create_dataframe(columns=parquet_file_df_columns)
|
|
125
|
+
row_group_df = _create_dataframe(columns=row_group_df_columns)
|
|
126
|
+
column_chunk_df = _create_dataframe(columns=column_chunk_df_columns)
|
|
127
|
+
|
|
128
|
+
# delta_table = DeltaTable.forPath(spark, path)
|
|
129
|
+
# detail_df = spark.sql(f"DESCRIBE DETAIL `{table_name}`").collect()[0]
|
|
130
|
+
|
|
131
|
+
# num_files = detail_df.numFiles
|
|
132
|
+
# size_in_bytes = detail_df.sizeInBytes
|
|
133
|
+
|
|
134
|
+
latest_files = _read_delta_table(path).inputFiles()
|
|
135
|
+
file_paths = [f.split("/")[-1] for f in latest_files]
|
|
136
|
+
row_count = _delta_table_row_count(table_name)
|
|
137
|
+
row_groups = 0
|
|
138
|
+
max_rows_per_row_group = 0
|
|
139
|
+
min_rows_per_row_group = float("inf")
|
|
140
|
+
|
|
141
|
+
schema = ds.dataset(table_path).schema.metadata
|
|
142
|
+
is_vorder = any(b"vorder" in key for key in schema.keys())
|
|
143
|
+
|
|
144
|
+
for file_name in file_paths:
|
|
145
|
+
parquet_file = pq.ParquetFile(f"{table_path}/{file_name}")
|
|
146
|
+
row_groups += parquet_file.num_row_groups
|
|
147
|
+
|
|
148
|
+
# Generate rowgroup dataframe
|
|
149
|
+
new_data = {
|
|
150
|
+
"ParquetFile": file_name,
|
|
151
|
+
"RowCount": parquet_file.metadata.num_rows,
|
|
152
|
+
"RowGroups": parquet_file.num_row_groups,
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
parquet_file_df = pd.concat(
|
|
156
|
+
[parquet_file_df, pd.DataFrame(new_data, index=[0])], ignore_index=True
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
for i in range(parquet_file.num_row_groups):
|
|
160
|
+
row_group = parquet_file.metadata.row_group(i)
|
|
161
|
+
num_rows = row_group.num_rows
|
|
162
|
+
|
|
163
|
+
max_rows_per_row_group = max(max_rows_per_row_group, num_rows)
|
|
164
|
+
min_rows_per_row_group = min(min_rows_per_row_group, num_rows)
|
|
165
|
+
|
|
166
|
+
total_compressed_size = 0
|
|
167
|
+
total_uncompressed_size = 0
|
|
168
|
+
|
|
169
|
+
for j in range(row_group.num_columns):
|
|
170
|
+
column_chunk = row_group.column(j)
|
|
171
|
+
total_compressed_size += column_chunk.total_compressed_size
|
|
172
|
+
total_uncompressed_size += column_chunk.total_uncompressed_size
|
|
173
|
+
|
|
174
|
+
# Generate Column Chunk Dataframe
|
|
175
|
+
new_data = {
|
|
176
|
+
"ParquetFile": file_name,
|
|
177
|
+
"ColumnID": j,
|
|
178
|
+
"ColumnName": column_chunk.path_in_schema,
|
|
179
|
+
"ColumnType": column_chunk.physical_type,
|
|
180
|
+
"CompressedSize": column_chunk.total_compressed_size,
|
|
181
|
+
"UncompressedSize": column_chunk.total_uncompressed_size,
|
|
182
|
+
"HasDict": column_chunk.has_dictionary_page,
|
|
183
|
+
"DictOffset": column_chunk.dictionary_page_offset,
|
|
184
|
+
"ValueCount": column_chunk.num_values,
|
|
185
|
+
"Encodings": str(column_chunk.encodings),
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
column_chunk_df = pd.concat(
|
|
189
|
+
[column_chunk_df, pd.DataFrame(new_data, index=[0])],
|
|
190
|
+
ignore_index=True,
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
# Generate rowgroup dataframe
|
|
194
|
+
new_data = {
|
|
195
|
+
"ParquetFile": file_name,
|
|
196
|
+
"RowGroupID": i + 1,
|
|
197
|
+
"RowCount": num_rows,
|
|
198
|
+
"CompressedSize": total_compressed_size,
|
|
199
|
+
"UncompressedSize": total_uncompressed_size,
|
|
200
|
+
"CompressionRatio": total_compressed_size / total_uncompressed_size,
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if not row_group_df.empty:
|
|
204
|
+
row_group_df = pd.concat(
|
|
205
|
+
[row_group_df, pd.DataFrame(new_data, index=[0])], ignore_index=True
|
|
206
|
+
)
|
|
207
|
+
else:
|
|
208
|
+
row_group_df = pd.DataFrame(new_data, index=[0])
|
|
209
|
+
|
|
210
|
+
avg_rows_per_row_group = row_count / row_groups
|
|
211
|
+
|
|
212
|
+
# Generate summary dataframe
|
|
213
|
+
summary_df = pd.DataFrame(
|
|
214
|
+
[
|
|
215
|
+
{
|
|
216
|
+
"RowCount": row_count,
|
|
217
|
+
"RowGroups": row_groups,
|
|
218
|
+
"ParquetFiles": len(file_paths),
|
|
219
|
+
"MaxRowsPerRowGroup": max_rows_per_row_group,
|
|
220
|
+
"MinRowsPerRowGroup": min_rows_per_row_group,
|
|
221
|
+
"AvgRowsPerRowGroup": avg_rows_per_row_group,
|
|
222
|
+
"VOrderEnabled": is_vorder,
|
|
223
|
+
# "VOrderLevel": v_order_level,
|
|
224
|
+
}
|
|
225
|
+
]
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
# Clean up data types
|
|
229
|
+
_update_dataframe_datatypes(
|
|
230
|
+
dataframe=column_chunk_df, column_map=column_chunk_df_columns
|
|
231
|
+
)
|
|
232
|
+
_update_dataframe_datatypes(dataframe=row_group_df, column_map=row_group_df_columns)
|
|
233
|
+
_update_dataframe_datatypes(
|
|
234
|
+
dataframe=parquet_file_df, column_map=parquet_file_df_columns
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
# Generate column dataframe
|
|
238
|
+
column_df = column_chunk_df.groupby(
|
|
239
|
+
["ColumnName", "ColumnType"], as_index=False
|
|
240
|
+
).agg({"CompressedSize": "sum", "UncompressedSize": "sum"})
|
|
241
|
+
|
|
242
|
+
# Add distinct count to column_df
|
|
243
|
+
for ind, r in column_df.iterrows():
|
|
244
|
+
col_name = r["ColumnName"]
|
|
245
|
+
if approx_distinct_count:
|
|
246
|
+
dc = _get_column_aggregate(
|
|
247
|
+
table_name=table_name,
|
|
248
|
+
column_name=col_name,
|
|
249
|
+
function="approx",
|
|
250
|
+
lakehouse=lakehouse,
|
|
251
|
+
workspace=workspace,
|
|
252
|
+
)
|
|
253
|
+
else:
|
|
254
|
+
dc = _get_column_aggregate(
|
|
255
|
+
table_name=table_name,
|
|
256
|
+
column_name=col_name,
|
|
257
|
+
function="distinctcount",
|
|
258
|
+
lakehouse=lakehouse,
|
|
259
|
+
workspace=workspace,
|
|
260
|
+
)
|
|
261
|
+
|
|
262
|
+
if "Cardinality" not in column_df.columns:
|
|
263
|
+
column_df["Cardinality"] = None
|
|
264
|
+
|
|
265
|
+
column_df.at[ind, "Cardinality"] = dc
|
|
266
|
+
|
|
267
|
+
column_df["Cardinality"] = column_df["Cardinality"].astype(int)
|
|
268
|
+
summary_df["TotalSize"] = column_df["CompressedSize"].sum()
|
|
269
|
+
|
|
270
|
+
dataframes = {
|
|
271
|
+
"Summary": summary_df,
|
|
272
|
+
"Parquet Files": parquet_file_df,
|
|
273
|
+
"Row Groups": row_group_df,
|
|
274
|
+
"Column Chunks": column_chunk_df,
|
|
275
|
+
"Columns": column_df,
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
save_table = f"{prefix}Summary"
|
|
279
|
+
|
|
280
|
+
if export:
|
|
281
|
+
if not lakehouse_attached():
|
|
282
|
+
raise ValueError(
|
|
283
|
+
f"{icons.red_dot} No lakehouse is attached to this notebook. Please attach a lakehouse to the notebook before running the Delta Analyzer."
|
|
284
|
+
)
|
|
285
|
+
dfL = get_lakehouse_tables()
|
|
286
|
+
dfL_filt = dfL[dfL["Table Name"] == save_table]
|
|
287
|
+
if dfL_filt.empty:
|
|
288
|
+
runId = 1
|
|
289
|
+
else:
|
|
290
|
+
max_run_id = _get_column_aggregate(
|
|
291
|
+
table_name=save_table,
|
|
292
|
+
)
|
|
293
|
+
runId = max_run_id + 1
|
|
294
|
+
|
|
295
|
+
for name, df in dataframes.items():
|
|
296
|
+
name = name.replace(" ", "")
|
|
297
|
+
cols = {
|
|
298
|
+
"WorkspaceName": workspace_name,
|
|
299
|
+
"WorkspaceId": workspace_id,
|
|
300
|
+
"LakehouseName": lakehouse_name,
|
|
301
|
+
"LakehouseId": lakehouse_id,
|
|
302
|
+
"TableName": table_name,
|
|
303
|
+
}
|
|
304
|
+
for i, (col, param) in enumerate(cols.items()):
|
|
305
|
+
df[col] = param
|
|
306
|
+
df.insert(i, col, df.pop(col))
|
|
307
|
+
|
|
308
|
+
df["Timestamp"] = now
|
|
309
|
+
df["Timestamp"] = pd.to_datetime(df["Timestamp"])
|
|
310
|
+
|
|
311
|
+
if export:
|
|
312
|
+
df["RunId"] = runId
|
|
313
|
+
df["RunId"] = df["RunId"].astype(int)
|
|
314
|
+
save_as_delta_table(
|
|
315
|
+
dataframe=df,
|
|
316
|
+
delta_table_name=f"{prefix}{name}",
|
|
317
|
+
write_mode="append",
|
|
318
|
+
merge_schema=True,
|
|
319
|
+
)
|
|
320
|
+
|
|
321
|
+
return dataframes
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import sempy.fabric as fabric
|
|
2
1
|
import pandas as pd
|
|
3
2
|
from sempy_labs._helper_functions import (
|
|
4
|
-
|
|
3
|
+
_is_valid_uuid,
|
|
4
|
+
_base_api,
|
|
5
|
+
_update_dataframe_datatypes,
|
|
6
|
+
_create_dataframe,
|
|
5
7
|
)
|
|
6
8
|
import sempy_labs._icons as icons
|
|
7
|
-
from
|
|
9
|
+
from uuid import UUID
|
|
8
10
|
|
|
9
11
|
|
|
10
12
|
def list_deployment_pipelines() -> pd.DataFrame:
|
|
@@ -19,18 +21,19 @@ def list_deployment_pipelines() -> pd.DataFrame:
|
|
|
19
21
|
A pandas dataframe showing a list of deployment pipelines the user can access.
|
|
20
22
|
"""
|
|
21
23
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
+
columns = {
|
|
25
|
+
"Deployment Pipeline Id": "string",
|
|
26
|
+
"Deployment Pipeline Name": "string",
|
|
27
|
+
"Description": "string",
|
|
28
|
+
}
|
|
29
|
+
df = _create_dataframe(columns=columns)
|
|
30
|
+
|
|
31
|
+
responses = _base_api(
|
|
32
|
+
request="/v1/deploymentPipelines",
|
|
33
|
+
status_codes=200,
|
|
34
|
+
uses_pagination=True,
|
|
24
35
|
)
|
|
25
36
|
|
|
26
|
-
client = fabric.FabricRestClient()
|
|
27
|
-
response = client.get("/v1/deploymentPipelines")
|
|
28
|
-
|
|
29
|
-
if response.status_code != 200:
|
|
30
|
-
raise FabricHTTPException(response)
|
|
31
|
-
|
|
32
|
-
responses = pagination(client, response)
|
|
33
|
-
|
|
34
37
|
for r in responses:
|
|
35
38
|
for v in r.get("value", []):
|
|
36
39
|
new_data = {
|
|
@@ -43,7 +46,7 @@ def list_deployment_pipelines() -> pd.DataFrame:
|
|
|
43
46
|
return df
|
|
44
47
|
|
|
45
48
|
|
|
46
|
-
def list_deployment_pipeline_stages(deployment_pipeline: str) -> pd.DataFrame:
|
|
49
|
+
def list_deployment_pipeline_stages(deployment_pipeline: str | UUID) -> pd.DataFrame:
|
|
47
50
|
"""
|
|
48
51
|
Shows the specified deployment pipeline stages.
|
|
49
52
|
|
|
@@ -51,8 +54,8 @@ def list_deployment_pipeline_stages(deployment_pipeline: str) -> pd.DataFrame:
|
|
|
51
54
|
|
|
52
55
|
Parameters
|
|
53
56
|
----------
|
|
54
|
-
deployment_pipeline : str
|
|
55
|
-
The deployment pipeline name.
|
|
57
|
+
deployment_pipeline : str | uuid.UUID
|
|
58
|
+
The deployment pipeline name or ID.
|
|
56
59
|
|
|
57
60
|
Returns
|
|
58
61
|
-------
|
|
@@ -62,28 +65,26 @@ def list_deployment_pipeline_stages(deployment_pipeline: str) -> pd.DataFrame:
|
|
|
62
65
|
|
|
63
66
|
from sempy_labs._helper_functions import resolve_deployment_pipeline_id
|
|
64
67
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
)
|
|
68
|
+
columns = {
|
|
69
|
+
"Deployment Pipeline Stage Id": "string",
|
|
70
|
+
"Deployment Pipeline Stage Name": "string",
|
|
71
|
+
"Order": "int",
|
|
72
|
+
"Description": "string",
|
|
73
|
+
"Workspace Id": "string",
|
|
74
|
+
"Workspace Name": "string",
|
|
75
|
+
"Public": "bool",
|
|
76
|
+
}
|
|
77
|
+
df = _create_dataframe(columns=columns)
|
|
76
78
|
|
|
77
79
|
deployment_pipeline_id = resolve_deployment_pipeline_id(
|
|
78
80
|
deployment_pipeline=deployment_pipeline
|
|
79
81
|
)
|
|
80
|
-
client = fabric.FabricRestClient()
|
|
81
|
-
response = client.get(f"/v1/deploymentPipelines/{deployment_pipeline_id}/stages")
|
|
82
|
-
|
|
83
|
-
if response.status_code != 200:
|
|
84
|
-
raise FabricHTTPException(response)
|
|
85
82
|
|
|
86
|
-
responses =
|
|
83
|
+
responses = _base_api(
|
|
84
|
+
request=f"/v1/deploymentPipelines/{deployment_pipeline_id}/stages",
|
|
85
|
+
status_codes=200,
|
|
86
|
+
uses_pagination=True,
|
|
87
|
+
)
|
|
87
88
|
|
|
88
89
|
for r in responses:
|
|
89
90
|
for v in r.get("value", []):
|
|
@@ -98,14 +99,14 @@ def list_deployment_pipeline_stages(deployment_pipeline: str) -> pd.DataFrame:
|
|
|
98
99
|
}
|
|
99
100
|
df = pd.concat([df, pd.DataFrame(new_data, index=[0])], ignore_index=True)
|
|
100
101
|
|
|
101
|
-
df
|
|
102
|
-
df["Public"] = df["Public"].astype(bool)
|
|
102
|
+
_update_dataframe_datatypes(dataframe=df, column_map=columns)
|
|
103
103
|
|
|
104
104
|
return df
|
|
105
105
|
|
|
106
106
|
|
|
107
107
|
def list_deployment_pipeline_stage_items(
|
|
108
|
-
deployment_pipeline: str
|
|
108
|
+
deployment_pipeline: str | UUID,
|
|
109
|
+
stage: str | UUID,
|
|
109
110
|
) -> pd.DataFrame:
|
|
110
111
|
"""
|
|
111
112
|
Shows the supported items from the workspace assigned to the specified stage of the specified deployment pipeline.
|
|
@@ -114,10 +115,10 @@ def list_deployment_pipeline_stage_items(
|
|
|
114
115
|
|
|
115
116
|
Parameters
|
|
116
117
|
----------
|
|
117
|
-
deployment_pipeline : str
|
|
118
|
-
The deployment pipeline name.
|
|
119
|
-
|
|
120
|
-
The deployment pipeline stage name.
|
|
118
|
+
deployment_pipeline : str | uuid.UUID
|
|
119
|
+
The deployment pipeline name or ID.
|
|
120
|
+
stage : str | uuid.UUID
|
|
121
|
+
The deployment pipeline stage name or ID.
|
|
121
122
|
|
|
122
123
|
Returns
|
|
123
124
|
-------
|
|
@@ -127,39 +128,46 @@ def list_deployment_pipeline_stage_items(
|
|
|
127
128
|
|
|
128
129
|
from sempy_labs._helper_functions import resolve_deployment_pipeline_id
|
|
129
130
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
)
|
|
131
|
+
columns = {
|
|
132
|
+
"Deployment Pipeline Stage Item Id": "string",
|
|
133
|
+
"Deployment Pipeline Stage Item Name": "string",
|
|
134
|
+
"Item Type": "string",
|
|
135
|
+
"Source Item Id": "string",
|
|
136
|
+
"Target Item Id": "string",
|
|
137
|
+
"Last Deployment Time": "string",
|
|
138
|
+
}
|
|
139
|
+
df = _create_dataframe(columns=columns)
|
|
140
140
|
|
|
141
141
|
deployment_pipeline_id = resolve_deployment_pipeline_id(
|
|
142
142
|
deployment_pipeline=deployment_pipeline
|
|
143
143
|
)
|
|
144
|
-
dfPS = list_deployment_pipeline_stages(deployment_pipeline=deployment_pipeline)
|
|
145
|
-
dfPS_filt = dfPS[dfPS["Deployment Pipeline Stage Name"] == stage_name]
|
|
146
144
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
145
|
+
def resolve_deployment_pipeline_stage_id(
|
|
146
|
+
deployment_pipeline_id: UUID, stage: str | UUID
|
|
147
|
+
):
|
|
148
|
+
|
|
149
|
+
dfPS = list_deployment_pipeline_stages(
|
|
150
|
+
deployment_pipeline=deployment_pipeline_id
|
|
150
151
|
)
|
|
151
|
-
stage_id = dfPS_filt["Deployment Pipeline Stage Id"].iloc[0]
|
|
152
152
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
153
|
+
if _is_valid_uuid(stage):
|
|
154
|
+
dfPS_filt = dfPS[dfPS["Deployment Pipeline Stage Id"] == stage]
|
|
155
|
+
else:
|
|
156
|
+
dfPS_filt = dfPS[dfPS["Deployment Pipeline Stage Name"] == stage]
|
|
157
|
+
if dfPS.empty:
|
|
158
|
+
raise ValueError(
|
|
159
|
+
f"{icons.red_dot} The '{stage}' stage does not exist within the '{deployment_pipeline}' deployment pipeline."
|
|
160
|
+
)
|
|
161
|
+
return dfPS_filt["Deployment Pipeline Stage Id"].iloc[0]
|
|
162
|
+
|
|
163
|
+
stage_id = resolve_deployment_pipeline_stage_id(deployment_pipeline_id, stage)
|
|
164
|
+
|
|
165
|
+
responses = _base_api(
|
|
166
|
+
request=f"/v1/deploymentPipelines/{deployment_pipeline_id}/stages/{stage_id}/items",
|
|
167
|
+
status_codes=200,
|
|
168
|
+
uses_pagination=True,
|
|
156
169
|
)
|
|
157
170
|
|
|
158
|
-
if response.status_code != 200:
|
|
159
|
-
raise FabricHTTPException(response)
|
|
160
|
-
|
|
161
|
-
responses = pagination(client, response)
|
|
162
|
-
|
|
163
171
|
for r in responses:
|
|
164
172
|
for v in r.get("value", []):
|
|
165
173
|
new_data = {
|
|
@@ -172,6 +180,4 @@ def list_deployment_pipeline_stage_items(
|
|
|
172
180
|
}
|
|
173
181
|
df = pd.concat([df, pd.DataFrame(new_data, index=[0])], ignore_index=True)
|
|
174
182
|
|
|
175
|
-
df["Last Deployment Time"] = pd.to_datetime(df["Last Deployment Time"])
|
|
176
|
-
|
|
177
183
|
return df
|
sempy_labs/_environments.py
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
import sempy.fabric as fabric
|
|
2
1
|
import pandas as pd
|
|
3
2
|
import sempy_labs._icons as icons
|
|
4
3
|
from typing import Optional
|
|
5
4
|
from sempy_labs._helper_functions import (
|
|
6
5
|
resolve_workspace_name_and_id,
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
_base_api,
|
|
7
|
+
_print_success,
|
|
8
|
+
_create_dataframe,
|
|
9
9
|
)
|
|
10
|
-
from sempy.fabric.exceptions import FabricHTTPException
|
|
11
10
|
from uuid import UUID
|
|
12
11
|
|
|
13
12
|
|
|
@@ -35,20 +34,23 @@ def create_environment(
|
|
|
35
34
|
|
|
36
35
|
(workspace_name, workspace_id) = resolve_workspace_name_and_id(workspace)
|
|
37
36
|
|
|
38
|
-
|
|
37
|
+
payload = {"displayName": environment}
|
|
39
38
|
|
|
40
39
|
if description:
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
40
|
+
payload["description"] = description
|
|
41
|
+
|
|
42
|
+
_base_api(
|
|
43
|
+
request="/v1/workspaces/{workspace_id}/environments",
|
|
44
|
+
method="post",
|
|
45
|
+
payload=payload,
|
|
46
|
+
status_codes=[201, 202],
|
|
47
|
+
lro_return_status_code=True,
|
|
46
48
|
)
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
49
|
+
_print_success(
|
|
50
|
+
item_name=environment,
|
|
51
|
+
item_type="environment",
|
|
52
|
+
workspace_name=workspace_name,
|
|
53
|
+
action="created",
|
|
52
54
|
)
|
|
53
55
|
|
|
54
56
|
|
|
@@ -71,16 +73,18 @@ def list_environments(workspace: Optional[str | UUID] = None) -> pd.DataFrame:
|
|
|
71
73
|
A pandas dataframe showing the environments within a workspace.
|
|
72
74
|
"""
|
|
73
75
|
|
|
74
|
-
|
|
76
|
+
columns = {
|
|
77
|
+
"Environment Name": "string",
|
|
78
|
+
"Environment Id": "string",
|
|
79
|
+
"Description": "string",
|
|
80
|
+
}
|
|
81
|
+
df = _create_dataframe(columns=columns)
|
|
75
82
|
|
|
76
83
|
(workspace_name, workspace_id) = resolve_workspace_name_and_id(workspace)
|
|
77
84
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
raise FabricHTTPException(response)
|
|
82
|
-
|
|
83
|
-
responses = pagination(client, response)
|
|
85
|
+
responses = _base_api(
|
|
86
|
+
request=f"/v1/workspaces/{workspace_id}/environments", uses_pagination=True
|
|
87
|
+
)
|
|
84
88
|
|
|
85
89
|
for r in responses:
|
|
86
90
|
for v in r.get("value", []):
|
|
@@ -117,16 +121,15 @@ def delete_environment(environment: str, workspace: Optional[str | UUID] = None)
|
|
|
117
121
|
environment=environment, workspace=workspace_id
|
|
118
122
|
)
|
|
119
123
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
124
|
+
_base_api(
|
|
125
|
+
request=f"/v1/workspaces/{workspace_id}/environments/{environment_id}",
|
|
126
|
+
method="delete",
|
|
123
127
|
)
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
f"{icons.green_dot} The '{environment}' environment within the '{workspace_name}' workspace has been deleted."
|
|
128
|
+
_print_success(
|
|
129
|
+
item_name=environment,
|
|
130
|
+
item_type="environment",
|
|
131
|
+
workspace_name=workspace_name,
|
|
132
|
+
action="deleted",
|
|
130
133
|
)
|
|
131
134
|
|
|
132
135
|
|
|
@@ -153,13 +156,13 @@ def publish_environment(environment: str, workspace: Optional[str | UUID] = None
|
|
|
153
156
|
environment=environment, workspace=workspace_id
|
|
154
157
|
)
|
|
155
158
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
+
_base_api(
|
|
160
|
+
request=f"/v1/workspaces/{workspace_id}/environments/{environment_id}/staging/publish",
|
|
161
|
+
method="post",
|
|
162
|
+
lro_return_status_code=True,
|
|
163
|
+
status_codes=None,
|
|
159
164
|
)
|
|
160
165
|
|
|
161
|
-
lro(client, response)
|
|
162
|
-
|
|
163
166
|
print(
|
|
164
167
|
f"{icons.green_dot} The '{environment}' environment within the '{workspace_name}' workspace has been published."
|
|
165
168
|
)
|