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.

Files changed (87) hide show
  1. {semantic_link_labs-0.9.1.dist-info → semantic_link_labs-0.9.3.dist-info}/METADATA +67 -8
  2. {semantic_link_labs-0.9.1.dist-info → semantic_link_labs-0.9.3.dist-info}/RECORD +87 -80
  3. sempy_labs/__init__.py +14 -12
  4. sempy_labs/_ai.py +8 -5
  5. sempy_labs/_capacities.py +120 -142
  6. sempy_labs/_capacity_migration.py +61 -94
  7. sempy_labs/_clear_cache.py +9 -8
  8. sempy_labs/_connections.py +107 -104
  9. sempy_labs/_data_pipelines.py +47 -49
  10. sempy_labs/_dataflows.py +45 -51
  11. sempy_labs/_dax.py +228 -6
  12. sempy_labs/_delta_analyzer.py +321 -0
  13. sempy_labs/_deployment_pipelines.py +72 -66
  14. sempy_labs/_environments.py +39 -36
  15. sempy_labs/_eventhouses.py +35 -35
  16. sempy_labs/_eventstreams.py +38 -39
  17. sempy_labs/_external_data_shares.py +29 -42
  18. sempy_labs/_gateways.py +103 -99
  19. sempy_labs/_generate_semantic_model.py +22 -30
  20. sempy_labs/_git.py +46 -66
  21. sempy_labs/_graphQL.py +95 -0
  22. sempy_labs/_helper_functions.py +227 -36
  23. sempy_labs/_job_scheduler.py +47 -59
  24. sempy_labs/_kql_databases.py +27 -34
  25. sempy_labs/_kql_querysets.py +23 -30
  26. sempy_labs/_list_functions.py +264 -167
  27. sempy_labs/_managed_private_endpoints.py +52 -47
  28. sempy_labs/_mirrored_databases.py +110 -134
  29. sempy_labs/_mirrored_warehouses.py +13 -13
  30. sempy_labs/_ml_experiments.py +36 -36
  31. sempy_labs/_ml_models.py +37 -38
  32. sempy_labs/_model_bpa.py +2 -2
  33. sempy_labs/_model_bpa_rules.py +8 -6
  34. sempy_labs/_model_dependencies.py +2 -0
  35. sempy_labs/_notebooks.py +28 -29
  36. sempy_labs/_one_lake_integration.py +2 -0
  37. sempy_labs/_query_scale_out.py +63 -81
  38. sempy_labs/_refresh_semantic_model.py +12 -14
  39. sempy_labs/_spark.py +54 -79
  40. sempy_labs/_sql.py +7 -11
  41. sempy_labs/_translations.py +2 -2
  42. sempy_labs/_vertipaq.py +11 -6
  43. sempy_labs/_warehouses.py +30 -33
  44. sempy_labs/_workloads.py +15 -20
  45. sempy_labs/_workspace_identity.py +13 -17
  46. sempy_labs/_workspaces.py +49 -48
  47. sempy_labs/admin/__init__.py +2 -0
  48. sempy_labs/admin/_basic_functions.py +244 -281
  49. sempy_labs/admin/_domains.py +186 -103
  50. sempy_labs/admin/_external_data_share.py +26 -31
  51. sempy_labs/admin/_git.py +17 -22
  52. sempy_labs/admin/_items.py +34 -48
  53. sempy_labs/admin/_scanner.py +61 -49
  54. sempy_labs/directlake/_directlake_schema_compare.py +2 -0
  55. sempy_labs/directlake/_dl_helper.py +10 -11
  56. sempy_labs/directlake/_generate_shared_expression.py +4 -5
  57. sempy_labs/directlake/_get_directlake_lakehouse.py +1 -0
  58. sempy_labs/directlake/_list_directlake_model_calc_tables.py +1 -0
  59. sempy_labs/directlake/_show_unsupported_directlake_objects.py +2 -0
  60. sempy_labs/directlake/_warm_cache.py +2 -0
  61. sempy_labs/graph/__init__.py +33 -0
  62. sempy_labs/graph/_groups.py +402 -0
  63. sempy_labs/graph/_teams.py +113 -0
  64. sempy_labs/graph/_users.py +191 -0
  65. sempy_labs/lakehouse/__init__.py +4 -0
  66. sempy_labs/lakehouse/_get_lakehouse_columns.py +12 -12
  67. sempy_labs/lakehouse/_get_lakehouse_tables.py +16 -22
  68. sempy_labs/lakehouse/_lakehouse.py +104 -7
  69. sempy_labs/lakehouse/_shortcuts.py +42 -20
  70. sempy_labs/migration/__init__.py +4 -0
  71. sempy_labs/migration/_direct_lake_to_import.py +66 -0
  72. sempy_labs/migration/_migrate_calctables_to_lakehouse.py +3 -2
  73. sempy_labs/migration/_migrate_calctables_to_semantic_model.py +1 -0
  74. sempy_labs/migration/_migrate_model_objects_to_semantic_model.py +1 -0
  75. sempy_labs/migration/_migrate_tables_columns_to_semantic_model.py +2 -0
  76. sempy_labs/migration/_refresh_calc_tables.py +2 -2
  77. sempy_labs/report/_download_report.py +8 -13
  78. sempy_labs/report/_generate_report.py +49 -46
  79. sempy_labs/report/_paginated.py +20 -26
  80. sempy_labs/report/_report_functions.py +52 -47
  81. sempy_labs/report/_report_list_functions.py +2 -0
  82. sempy_labs/report/_report_rebind.py +6 -10
  83. sempy_labs/report/_reportwrapper.py +187 -220
  84. sempy_labs/tom/_model.py +12 -6
  85. {semantic_link_labs-0.9.1.dist-info → semantic_link_labs-0.9.3.dist-info}/LICENSE +0 -0
  86. {semantic_link_labs-0.9.1.dist-info → semantic_link_labs-0.9.3.dist-info}/WHEEL +0 -0
  87. {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
- pagination,
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 sempy.fabric.exceptions import FabricHTTPException
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
- df = pd.DataFrame(
23
- columns=["Deployment Pipeline Id", "Deployment Pipeline Name", "Description"]
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
- df = pd.DataFrame(
66
- columns=[
67
- "Deployment Pipeline Stage Id",
68
- "Deployment Pipeline Stage Name",
69
- "Order",
70
- "Description",
71
- "Workspace Id",
72
- "Workspace Name",
73
- "Public",
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 = pagination(client, response)
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["Order"] = df["Order"].astype(int)
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, stage_name: 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
- stage_name : str
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
- df = pd.DataFrame(
131
- columns=[
132
- "Deployment Pipeline Stage Item Id",
133
- "Deployment Pipeline Stage Item Name",
134
- "Item Type",
135
- "Source Item Id",
136
- "Target Item Id",
137
- "Last Deployment Time",
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
- if len(dfPS_filt) == 0:
148
- raise ValueError(
149
- f"{icons.red_dot} The '{stage_name}' stage does not exist within the '{deployment_pipeline}' deployment pipeline."
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
- client = fabric.FabricRestClient()
154
- response = client.get(
155
- f"/v1/deploymentPipelines/{deployment_pipeline_id}/stages/{stage_id}/items"
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
@@ -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
- lro,
8
- pagination,
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
- request_body = {"displayName": environment}
37
+ payload = {"displayName": environment}
39
38
 
40
39
  if description:
41
- request_body["description"] = description
42
-
43
- client = fabric.FabricRestClient()
44
- response = client.post(
45
- f"/v1/workspaces/{workspace_id}/environments", json=request_body
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
- lro(client, response, status_codes=[201, 202])
49
-
50
- print(
51
- f"{icons.green_dot} The '{environment}' environment has been created within the '{workspace_name}' workspace."
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
- df = pd.DataFrame(columns=["Environment Name", "Environment Id", "Description"])
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
- client = fabric.FabricRestClient()
79
- response = client.get(f"/v1/workspaces/{workspace_id}/environments")
80
- if response.status_code != 200:
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
- client = fabric.FabricRestClient()
121
- response = client.delete(
122
- f"/v1/workspaces/{workspace_id}/environments/{environment_id}"
124
+ _base_api(
125
+ request=f"/v1/workspaces/{workspace_id}/environments/{environment_id}",
126
+ method="delete",
123
127
  )
124
-
125
- if response.status_code != 200:
126
- raise FabricHTTPException(response)
127
-
128
- print(
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
- client = fabric.FabricRestClient()
157
- response = client.post(
158
- f"/v1/workspaces/{workspace_id}/environments/{environment_id}/staging/publish"
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
  )