semantic-link-labs 0.10.0__py3-none-any.whl → 0.11.0__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 (95) hide show
  1. {semantic_link_labs-0.10.0.dist-info → semantic_link_labs-0.11.0.dist-info}/METADATA +9 -6
  2. {semantic_link_labs-0.10.0.dist-info → semantic_link_labs-0.11.0.dist-info}/RECORD +95 -87
  3. sempy_labs/__init__.py +11 -1
  4. sempy_labs/_a_lib_info.py +2 -0
  5. sempy_labs/_capacities.py +2 -0
  6. sempy_labs/_connections.py +11 -0
  7. sempy_labs/_dashboards.py +9 -4
  8. sempy_labs/_data_pipelines.py +5 -0
  9. sempy_labs/_dataflows.py +284 -17
  10. sempy_labs/_daxformatter.py +80 -0
  11. sempy_labs/_delta_analyzer_history.py +4 -1
  12. sempy_labs/_deployment_pipelines.py +4 -0
  13. sempy_labs/_documentation.py +3 -0
  14. sempy_labs/_environments.py +10 -1
  15. sempy_labs/_eventhouses.py +12 -5
  16. sempy_labs/_eventstreams.py +11 -3
  17. sempy_labs/_external_data_shares.py +8 -2
  18. sempy_labs/_gateways.py +26 -5
  19. sempy_labs/_git.py +11 -0
  20. sempy_labs/_graphQL.py +10 -3
  21. sempy_labs/_helper_functions.py +62 -10
  22. sempy_labs/_job_scheduler.py +54 -7
  23. sempy_labs/_kql_databases.py +11 -2
  24. sempy_labs/_kql_querysets.py +11 -3
  25. sempy_labs/_list_functions.py +17 -45
  26. sempy_labs/_managed_private_endpoints.py +11 -2
  27. sempy_labs/_mirrored_databases.py +17 -3
  28. sempy_labs/_mirrored_warehouses.py +9 -3
  29. sempy_labs/_ml_experiments.py +11 -3
  30. sempy_labs/_ml_models.py +11 -3
  31. sempy_labs/_model_bpa_rules.py +2 -0
  32. sempy_labs/_mounted_data_factories.py +12 -8
  33. sempy_labs/_notebooks.py +6 -3
  34. sempy_labs/_refresh_semantic_model.py +1 -0
  35. sempy_labs/_semantic_models.py +107 -0
  36. sempy_labs/_spark.py +7 -0
  37. sempy_labs/_sql_endpoints.py +208 -0
  38. sempy_labs/_sqldatabase.py +13 -4
  39. sempy_labs/_tags.py +5 -1
  40. sempy_labs/_user_delegation_key.py +2 -0
  41. sempy_labs/_variable_libraries.py +3 -1
  42. sempy_labs/_warehouses.py +13 -3
  43. sempy_labs/_workloads.py +3 -0
  44. sempy_labs/_workspace_identity.py +3 -0
  45. sempy_labs/_workspaces.py +14 -1
  46. sempy_labs/admin/__init__.py +2 -0
  47. sempy_labs/admin/_activities.py +6 -5
  48. sempy_labs/admin/_apps.py +31 -31
  49. sempy_labs/admin/_artifacts.py +8 -3
  50. sempy_labs/admin/_basic_functions.py +5 -0
  51. sempy_labs/admin/_capacities.py +39 -28
  52. sempy_labs/admin/_datasets.py +51 -51
  53. sempy_labs/admin/_domains.py +17 -1
  54. sempy_labs/admin/_external_data_share.py +8 -2
  55. sempy_labs/admin/_git.py +14 -9
  56. sempy_labs/admin/_items.py +15 -2
  57. sempy_labs/admin/_reports.py +64 -65
  58. sempy_labs/admin/_shared.py +7 -1
  59. sempy_labs/admin/_tags.py +5 -0
  60. sempy_labs/admin/_tenant.py +5 -2
  61. sempy_labs/admin/_users.py +9 -3
  62. sempy_labs/admin/_workspaces.py +88 -0
  63. sempy_labs/directlake/_dl_helper.py +2 -0
  64. sempy_labs/directlake/_generate_shared_expression.py +2 -0
  65. sempy_labs/directlake/_get_directlake_lakehouse.py +2 -4
  66. sempy_labs/directlake/_get_shared_expression.py +2 -0
  67. sempy_labs/directlake/_guardrails.py +2 -0
  68. sempy_labs/directlake/_update_directlake_model_lakehouse_connection.py +5 -3
  69. sempy_labs/directlake/_warm_cache.py +1 -0
  70. sempy_labs/graph/_groups.py +22 -7
  71. sempy_labs/graph/_teams.py +7 -2
  72. sempy_labs/graph/_users.py +1 -0
  73. sempy_labs/lakehouse/_blobs.py +1 -0
  74. sempy_labs/lakehouse/_get_lakehouse_tables.py +88 -27
  75. sempy_labs/lakehouse/_helper.py +2 -0
  76. sempy_labs/lakehouse/_lakehouse.py +38 -5
  77. sempy_labs/lakehouse/_livy_sessions.py +2 -1
  78. sempy_labs/lakehouse/_shortcuts.py +7 -1
  79. sempy_labs/migration/_direct_lake_to_import.py +2 -0
  80. sempy_labs/mirrored_azure_databricks_catalog/__init__.py +15 -0
  81. sempy_labs/mirrored_azure_databricks_catalog/_discover.py +213 -0
  82. sempy_labs/mirrored_azure_databricks_catalog/_refresh_catalog_metadata.py +45 -0
  83. sempy_labs/report/_download_report.py +2 -1
  84. sempy_labs/report/_generate_report.py +2 -0
  85. sempy_labs/report/_paginated.py +2 -0
  86. sempy_labs/report/_report_bpa.py +110 -122
  87. sempy_labs/report/_report_bpa_rules.py +2 -0
  88. sempy_labs/report/_report_functions.py +7 -0
  89. sempy_labs/report/_reportwrapper.py +86 -48
  90. sempy_labs/theme/__init__.py +12 -0
  91. sempy_labs/theme/_org_themes.py +96 -0
  92. sempy_labs/tom/_model.py +702 -35
  93. {semantic_link_labs-0.10.0.dist-info → semantic_link_labs-0.11.0.dist-info}/WHEEL +0 -0
  94. {semantic_link_labs-0.10.0.dist-info → semantic_link_labs-0.11.0.dist-info}/licenses/LICENSE +0 -0
  95. {semantic_link_labs-0.10.0.dist-info → semantic_link_labs-0.11.0.dist-info}/top_level.txt +0 -0
sempy_labs/_ml_models.py CHANGED
@@ -1,15 +1,17 @@
1
1
  import pandas as pd
2
2
  from typing import Optional
3
3
  from sempy_labs._helper_functions import (
4
- resolve_workspace_name_and_id,
4
+ resolve_workspace_id,
5
5
  _base_api,
6
6
  delete_item,
7
7
  _create_dataframe,
8
8
  create_item,
9
9
  )
10
10
  from uuid import UUID
11
+ from sempy._utils._log import log
11
12
 
12
13
 
14
+ @log
13
15
  def list_ml_models(workspace: Optional[str | UUID] = None) -> pd.DataFrame:
14
16
  """
15
17
  Shows the ML models within a workspace.
@@ -36,7 +38,7 @@ def list_ml_models(workspace: Optional[str | UUID] = None) -> pd.DataFrame:
36
38
  }
37
39
  df = _create_dataframe(columns=columns)
38
40
 
39
- (workspace_name, workspace_id) = resolve_workspace_name_and_id(workspace)
41
+ workspace_id = resolve_workspace_id(workspace)
40
42
 
41
43
  responses = _base_api(
42
44
  request=f"/v1/workspaces/{workspace_id}/mlModels",
@@ -44,6 +46,7 @@ def list_ml_models(workspace: Optional[str | UUID] = None) -> pd.DataFrame:
44
46
  uses_pagination=True,
45
47
  )
46
48
 
49
+ dfs = []
47
50
  for r in responses:
48
51
  for v in r.get("value", []):
49
52
  model_id = v.get("id")
@@ -55,11 +58,15 @@ def list_ml_models(workspace: Optional[str | UUID] = None) -> pd.DataFrame:
55
58
  "ML Model Id": model_id,
56
59
  "Description": desc,
57
60
  }
58
- df = pd.concat([df, pd.DataFrame(new_data, index=[0])], ignore_index=True)
61
+ dfs.append(pd.DataFrame(new_data, index=[0]))
62
+
63
+ if dfs:
64
+ df = pd.concat(dfs, ignore_index=True)
59
65
 
60
66
  return df
61
67
 
62
68
 
69
+ @log
63
70
  def create_ml_model(
64
71
  name: str, description: Optional[str] = None, workspace: Optional[str | UUID] = None
65
72
  ):
@@ -83,6 +90,7 @@ def create_ml_model(
83
90
  create_item(name=name, description=description, type="MLModel", workspace=workspace)
84
91
 
85
92
 
93
+ @log
86
94
  def delete_ml_model(name: str | UUID, workspace: Optional[str | UUID] = None):
87
95
  """
88
96
  Deletes a Fabric ML model.
@@ -2,8 +2,10 @@ import sempy
2
2
  import pandas as pd
3
3
  import re
4
4
  from typing import Optional
5
+ from sempy._utils._log import log
5
6
 
6
7
 
8
+ @log
7
9
  def model_bpa_rules(
8
10
  dependencies: Optional[pd.DataFrame] = None,
9
11
  **kwargs,
@@ -1,20 +1,19 @@
1
1
  import pandas as pd
2
- import json
3
2
  from typing import Optional
4
3
  from sempy_labs._helper_functions import (
5
- resolve_workspace_name_and_id,
4
+ resolve_workspace_id,
6
5
  _base_api,
7
6
  _create_dataframe,
8
7
  _update_dataframe_datatypes,
9
- resolve_item_id,
10
- _decode_b64,
11
- delete_item,
12
8
  get_item_definition,
9
+ delete_item,
13
10
  )
14
11
 
15
12
  from uuid import UUID
13
+ from sempy._utils._log import log
16
14
 
17
15
 
16
+ @log
18
17
  def list_mounted_data_factories(
19
18
  workspace: Optional[str | UUID] = None,
20
19
  ) -> pd.DataFrame:
@@ -36,7 +35,7 @@ def list_mounted_data_factories(
36
35
  A pandas dataframe showing a list of mounted data factories from the specified workspace.
37
36
  """
38
37
 
39
- (workspace_name, workspace_id) = resolve_workspace_name_and_id(workspace)
38
+ workspace_id = resolve_workspace_id(workspace)
40
39
 
41
40
  columns = {
42
41
  "Mounted Data Factory Name": "str",
@@ -50,6 +49,7 @@ def list_mounted_data_factories(
50
49
  uses_pagination=True,
51
50
  )
52
51
 
52
+ dfs = []
53
53
  for r in responses:
54
54
  for v in r.get("value", []):
55
55
  new_data = {
@@ -58,13 +58,16 @@ def list_mounted_data_factories(
58
58
  "Description": v.get("description"),
59
59
  }
60
60
 
61
- df = pd.concat([df, pd.DataFrame(new_data, index=[0])], ignore_index=True)
61
+ dfs.append(pd.DataFrame(new_data, index=[0]))
62
62
 
63
- _update_dataframe_datatypes(dataframe=df, column_map=columns)
63
+ if dfs:
64
+ df = pd.concat(dfs, ignore_index=True)
65
+ _update_dataframe_datatypes(dataframe=df, column_map=columns)
64
66
 
65
67
  return df
66
68
 
67
69
 
70
+ @log
68
71
  def get_mounted_data_factory_definition(
69
72
  mounted_data_factory: str | UUID, workspace: Optional[str | UUID] = None
70
73
  ) -> dict:
@@ -96,6 +99,7 @@ def get_mounted_data_factory_definition(
96
99
  )
97
100
 
98
101
 
102
+ @log
99
103
  def delete_mounted_data_factory(
100
104
  mounted_data_factory: str | UUID, workspace: Optional[str | UUID]
101
105
  ):
sempy_labs/_notebooks.py CHANGED
@@ -60,6 +60,7 @@ def _get_notebook_type(
60
60
  return file_extension[1:]
61
61
 
62
62
 
63
+ @log
63
64
  def get_notebook_definition(
64
65
  notebook_name: str,
65
66
  workspace: Optional[str | UUID] = None,
@@ -159,6 +160,7 @@ def import_notebook_from_web(
159
160
  notebook_content=response.content,
160
161
  workspace=workspace_id,
161
162
  description=description,
163
+ format="ipynb",
162
164
  )
163
165
  elif len(dfI_filt) > 0 and overwrite:
164
166
  print(f"{icons.info} Overwrite of notebooks is currently not supported.")
@@ -171,6 +173,7 @@ def import_notebook_from_web(
171
173
  )
172
174
 
173
175
 
176
+ @log
174
177
  def create_notebook(
175
178
  name: str,
176
179
  notebook_content: str,
@@ -202,9 +205,8 @@ def create_notebook(
202
205
  otherwise notebook_content should be GIT friendly format
203
206
  """
204
207
 
205
- notebook_payload = base64.b64encode(notebook_content.encode("utf-8")).decode(
206
- "utf-8"
207
- )
208
+ notebook_payload = base64.b64encode(notebook_content).decode("utf-8")
209
+
208
210
  definition_payload = {
209
211
  "parts": [
210
212
  {
@@ -227,6 +229,7 @@ def create_notebook(
227
229
  )
228
230
 
229
231
 
232
+ @log
230
233
  def update_notebook_definition(
231
234
  name: str,
232
235
  notebook_content: str,
@@ -321,6 +321,7 @@ def cancel_dataset_refresh(
321
321
  )
322
322
 
323
323
 
324
+ @log
324
325
  def get_semantic_model_refresh_history(
325
326
  dataset: str | UUID,
326
327
  request_id: Optional[str] = None,
@@ -8,11 +8,15 @@ from sempy_labs._helper_functions import (
8
8
  resolve_workspace_name_and_id,
9
9
  resolve_dataset_name_and_id,
10
10
  delete_item,
11
+ resolve_dataset_id,
12
+ resolve_workspace_id,
11
13
  )
12
14
  import sempy_labs._icons as icons
13
15
  import re
16
+ from sempy._utils._log import log
14
17
 
15
18
 
19
+ @log
16
20
  def get_semantic_model_refresh_schedule(
17
21
  dataset: str | UUID, workspace: Optional[str | UUID] = None
18
22
  ) -> pd.DataFrame:
@@ -70,6 +74,7 @@ def get_semantic_model_refresh_schedule(
70
74
  return df
71
75
 
72
76
 
77
+ @log
73
78
  def enable_semantic_model_scheduled_refresh(
74
79
  dataset: str | UUID,
75
80
  workspace: Optional[str | UUID] = None,
@@ -119,6 +124,7 @@ def enable_semantic_model_scheduled_refresh(
119
124
  )
120
125
 
121
126
 
127
+ @log
122
128
  def delete_semantic_model(dataset: str | UUID, workspace: Optional[str | UUID] = None):
123
129
  """
124
130
  Deletes a semantic model.
@@ -138,6 +144,7 @@ def delete_semantic_model(dataset: str | UUID, workspace: Optional[str | UUID] =
138
144
  delete_item(item=dataset, type="SemanticModel", workspace=workspace)
139
145
 
140
146
 
147
+ @log
141
148
  def update_semantic_model_refresh_schedule(
142
149
  dataset: str | UUID,
143
150
  days: Optional[str | List[str]] = None,
@@ -227,3 +234,103 @@ def update_semantic_model_refresh_schedule(
227
234
  print(
228
235
  f"{icons.green_dot} Refresh schedule for the '{dataset_name}' within the '{workspace_name}' workspace has been updated."
229
236
  )
237
+
238
+
239
+ @log
240
+ def list_semantic_model_datasources(
241
+ dataset: str | UUID,
242
+ workspace: Optional[str | UUID] = None,
243
+ expand_details: bool = True,
244
+ ) -> pd.DataFrame:
245
+ """
246
+ Lists the data sources for the specified semantic model.
247
+
248
+ This is a wrapper function for the following API: `Datasets - Get Datasources In Group <https://learn.microsoft.com/rest/api/power-bi/datasets/get-datasources-in-group>`_.
249
+
250
+ Parameters
251
+ ----------
252
+ dataset : str | uuid.UUID
253
+ Name or ID of the semantic model.
254
+ workspace : str | uuid.UUID, default=None
255
+ The workspace name or ID.
256
+ Defaults to None which resolves to the workspace of the attached lakehouse
257
+ or if no lakehouse attached, resolves to the workspace of the notebook.
258
+ expand_details : bool, default=True
259
+ If True, expands the connection details for each data source.
260
+
261
+ Returns
262
+ -------
263
+ pandas.DataFrame
264
+ DataFrame containing the data sources for the specified semantic model.
265
+ """
266
+
267
+ workspace_id = resolve_workspace_id(workspace)
268
+ dataset_id = resolve_dataset_id(dataset, workspace_id)
269
+
270
+ if expand_details:
271
+ columns = {
272
+ "Datasource Type": "str",
273
+ "Connection Server": "str",
274
+ "Connection Database": "str",
275
+ "Connection Path": "str",
276
+ "Connection Account": "str",
277
+ "Connection Domain": "str",
278
+ "Connection Kind": "str",
279
+ "Connection Email Address": "str",
280
+ "Connection URL": "str",
281
+ "Connection Class Info": "str",
282
+ "Connection Login Server": "str",
283
+ "Datasource Id": "str",
284
+ "Gateway Id": "str",
285
+ }
286
+ else:
287
+ columns = {
288
+ "Datasource Type": "str",
289
+ "Connection Details": "str",
290
+ "Datasource Id": "str",
291
+ "Gateway Id": "str",
292
+ }
293
+
294
+ df = _create_dataframe(columns)
295
+
296
+ response = _base_api(
297
+ request=f"/v1.0/myorg/groups/{workspace_id}/datasets/{dataset_id}/datasources",
298
+ client="fabric_sp",
299
+ )
300
+
301
+ dfs = []
302
+ for item in response.json().get("value", []):
303
+ ds_type = item.get("datasourceType")
304
+ conn_details = item.get("connectionDetails", {})
305
+ ds_id = item.get("datasourceId")
306
+ gateway_id = item.get("gatewayId")
307
+ if expand_details:
308
+ new_data = {
309
+ "Datasource Type": ds_type,
310
+ "Connection Server": conn_details.get("server"),
311
+ "Connection Database": conn_details.get("database"),
312
+ "Connection Path": conn_details.get("path"),
313
+ "Connection Account": conn_details.get("account"),
314
+ "Connection Domain": conn_details.get("domain"),
315
+ "Connection Kind": conn_details.get("kind"),
316
+ "Connection Email Address": conn_details.get("emailAddress"),
317
+ "Connection URL": conn_details.get("url"),
318
+ "Connection Class Info": conn_details.get("classInfo"),
319
+ "Connection Login Server": conn_details.get("loginServer"),
320
+ "Datasource Id": ds_id,
321
+ "Gateway Id": gateway_id,
322
+ }
323
+ dfs.append(pd.DataFrame(new_data, index=[0]))
324
+ else:
325
+ new_data = {
326
+ "Datasource Type": ds_type,
327
+ "Connection Details": conn_details,
328
+ "Datasource Id": ds_id,
329
+ "Gateway Id": gateway_id,
330
+ }
331
+ dfs.append(pd.DataFrame([new_data]))
332
+
333
+ if dfs:
334
+ df = pd.concat(dfs, ignore_index=True)
335
+
336
+ return df
sempy_labs/_spark.py CHANGED
@@ -8,8 +8,10 @@ from sempy_labs._helper_functions import (
8
8
  _create_dataframe,
9
9
  )
10
10
  from uuid import UUID
11
+ from sempy._utils._log import log
11
12
 
12
13
 
14
+ @log
13
15
  def list_custom_pools(workspace: Optional[str | UUID] = None) -> pd.DataFrame:
14
16
  """
15
17
  Lists all `custom pools <https://learn.microsoft.com/fabric/data-engineering/create-custom-spark-pools>`_ within a workspace.
@@ -73,6 +75,7 @@ def list_custom_pools(workspace: Optional[str | UUID] = None) -> pd.DataFrame:
73
75
  return df
74
76
 
75
77
 
78
+ @log
76
79
  def create_custom_pool(
77
80
  pool_name: str,
78
81
  node_size: str,
@@ -145,6 +148,7 @@ def create_custom_pool(
145
148
  )
146
149
 
147
150
 
151
+ @log
148
152
  def update_custom_pool(
149
153
  pool_name: str,
150
154
  node_size: Optional[str] = None,
@@ -251,6 +255,7 @@ def update_custom_pool(
251
255
  )
252
256
 
253
257
 
258
+ @log
254
259
  def delete_custom_pool(pool_name: str, workspace: Optional[str | UUID] = None):
255
260
  """
256
261
  Deletes a `custom pool <https://learn.microsoft.com/fabric/data-engineering/create-custom-spark-pools>`_ within a workspace.
@@ -286,6 +291,7 @@ def delete_custom_pool(pool_name: str, workspace: Optional[str | UUID] = None):
286
291
  )
287
292
 
288
293
 
294
+ @log
289
295
  def get_spark_settings(
290
296
  workspace: Optional[str | UUID] = None, return_dataframe: bool = True
291
297
  ) -> pd.DataFrame | dict:
@@ -362,6 +368,7 @@ def get_spark_settings(
362
368
  return response.json()
363
369
 
364
370
 
371
+ @log
365
372
  def update_spark_settings(
366
373
  automatic_log_enabled: Optional[bool] = None,
367
374
  high_concurrency_enabled: Optional[bool] = None,
@@ -0,0 +1,208 @@
1
+ from typing import Optional, Literal
2
+ from uuid import UUID
3
+ import pandas as pd
4
+ from sempy_labs._helper_functions import (
5
+ _base_api,
6
+ _create_dataframe,
7
+ resolve_workspace_name_and_id,
8
+ resolve_item_name_and_id,
9
+ _update_dataframe_datatypes,
10
+ resolve_workspace_id,
11
+ )
12
+ import sempy_labs._icons as icons
13
+ from sempy._utils._log import log
14
+
15
+
16
+ @log
17
+ def list_sql_endpoints(workspace: Optional[str | UUID] = None) -> pd.DataFrame:
18
+ """
19
+ Shows the SQL endpoints within a workspace.
20
+
21
+ Parameters
22
+ ----------
23
+ workspace : str | uuid.UUID, default=None
24
+ The Fabric workspace name or ID.
25
+ Defaults to None which resolves to the workspace of the attached lakehouse
26
+ or if no lakehouse attached, resolves to the workspace of the notebook.
27
+
28
+ Returns
29
+ -------
30
+ pandas.DataFrame
31
+ A pandas dataframe showing the SQL endpoints within a workspace.
32
+ """
33
+
34
+ columns = {
35
+ "SQL Endpoint Id": "string",
36
+ "SQL Endpoint Name": "string",
37
+ "Description": "string",
38
+ }
39
+ df = _create_dataframe(columns=columns)
40
+
41
+ workspace_id = resolve_workspace_id(workspace)
42
+
43
+ responses = _base_api(
44
+ request=f"/v1/workspaces/{workspace_id}/sqlEndpoints", uses_pagination=True
45
+ )
46
+
47
+ dfs = []
48
+ for r in responses:
49
+ for v in r.get("value", []):
50
+
51
+ new_data = {
52
+ "SQL Endpoint Id": v.get("id"),
53
+ "SQL Endpoint Name": v.get("displayName"),
54
+ "Description": v.get("description"),
55
+ }
56
+ dfs.append(pd.DataFrame(new_data, index=[0]))
57
+
58
+ if dfs:
59
+ df = pd.concat(dfs, ignore_index=True)
60
+
61
+ return df
62
+
63
+
64
+ @log
65
+ def refresh_sql_endpoint_metadata(
66
+ item: str | UUID,
67
+ type: Literal["Lakehouse", "MirroredDatabase"],
68
+ workspace: Optional[str | UUID] = None,
69
+ tables: dict[str, list[str]] = None,
70
+ ) -> pd.DataFrame:
71
+ """
72
+ Refreshes the metadata of a SQL endpoint.
73
+
74
+ This is a wrapper function for the following API: `Items - Refresh Sql Endpoint Metadata <https://learn.microsoft.com/rest/api/fabric/sqlendpoint/items/refresh-sql-endpoint-metadata>`_.
75
+
76
+ Parameters
77
+ ----------
78
+ item : str | uuid.UUID
79
+ The name or ID of the item (Lakehouse or MirroredDatabase).
80
+ type : Literal['Lakehouse', 'MirroredDatabase']
81
+ The type of the item. Must be 'Lakehouse' or 'MirroredDatabase'.
82
+ workspace : str | uuid.UUID, default=None
83
+ The Fabric workspace name or ID.
84
+ Defaults to None which resolves to the workspace of the attached lakehouse
85
+ or if no lakehouse attached, resolves to the workspace of the notebook.
86
+ tables : dict[str, list[str]], default=None
87
+ A dictionary where the keys are schema names and the values are lists of table names.
88
+ If empty, all table metadata will be refreshed.
89
+
90
+ Example:
91
+ {
92
+ "dbo": ["DimDate", "DimGeography"],
93
+ "sls": ["FactSales", "FactBudget"],
94
+ }
95
+
96
+ Returns
97
+ -------
98
+ pandas.DataFrame
99
+ A pandas dataframe showing the status of the metadata refresh operation.
100
+ """
101
+
102
+ (workspace_name, workspace_id) = resolve_workspace_name_and_id(workspace)
103
+
104
+ (item_name, item_id) = resolve_item_name_and_id(
105
+ item=item, type=type, workspace=workspace
106
+ )
107
+
108
+ if type == "Lakehouse":
109
+ response = _base_api(
110
+ request=f"/v1/workspaces/{workspace_id}/lakehouses/{item_id}",
111
+ client="fabric_sp",
112
+ )
113
+ sql_endpoint_id = (
114
+ response.json()
115
+ .get("properties", {})
116
+ .get("sqlEndpointProperties", {})
117
+ .get("id")
118
+ )
119
+ elif type == "MirroredDatabase":
120
+ response = _base_api(
121
+ request=f"/v1/workspaces/{workspace_id}/mirroredDatabases/{item_id}",
122
+ client="fabric_sp",
123
+ )
124
+ sql_endpoint_id = (
125
+ response.json()
126
+ .get("properties", {})
127
+ .get("sqlEndpointProperties", {})
128
+ .get("id")
129
+ )
130
+ else:
131
+ raise ValueError("Invalid type. Must be 'Lakehouse' or 'MirroredDatabase'.")
132
+
133
+ payload = {}
134
+ if tables:
135
+ payload = {
136
+ "tableDefinitions": [
137
+ {"schema": schema, "tableNames": tables}
138
+ for schema, tables in tables.items()
139
+ ]
140
+ }
141
+
142
+ result = _base_api(
143
+ request=f"v1/workspaces/{workspace_id}/sqlEndpoints/{sql_endpoint_id}/refreshMetadata?preview=true",
144
+ method="post",
145
+ status_codes=[200, 202],
146
+ lro_return_json=True,
147
+ payload=payload,
148
+ )
149
+
150
+ columns = {
151
+ "Table Name": "string",
152
+ "Status": "string",
153
+ "Start Time": "datetime",
154
+ "End Time": "datetime",
155
+ "Last Successful Sync Time": "datetime",
156
+ "Error Code": "string",
157
+ "Error Message": "string",
158
+ }
159
+
160
+ if result:
161
+ df = pd.json_normalize(result)
162
+
163
+ # Extract error code and message, set to None if no error
164
+ df["Error Code"] = df.get("error.errorCode", None)
165
+ df["Error Message"] = df.get("error.message", None)
166
+
167
+ # Friendly column renaming
168
+ df.rename(
169
+ columns={
170
+ "tableName": "Table Name",
171
+ "startDateTime": "Start Time",
172
+ "endDateTime": "End Time",
173
+ "status": "Status",
174
+ "lastSuccessfulSyncDateTime": "Last Successful Sync Time",
175
+ },
176
+ inplace=True,
177
+ )
178
+
179
+ # Drop the original 'error' column if present
180
+ df.drop(columns=[col for col in ["error"] if col in df.columns], inplace=True)
181
+
182
+ # Optional: Reorder columns
183
+ column_order = [
184
+ "Table Name",
185
+ "Status",
186
+ "Start Time",
187
+ "End Time",
188
+ "Last Successful Sync Time",
189
+ "Error Code",
190
+ "Error Message",
191
+ ]
192
+ df = df[column_order]
193
+
194
+ printout = f"{icons.green_dot} The metadata of the SQL endpoint for the '{item_name}' {type.lower()} within the '{workspace_name}' workspace has been refreshed"
195
+ if tables:
196
+ print(f"{printout} for the following tables: {tables}.")
197
+ else:
198
+ print(f"{printout} for all tables.")
199
+ else:
200
+ # If the target item has no tables to refresh the metadata for
201
+ df = pd.DataFrame(columns=columns.keys())
202
+ print(
203
+ f"{icons.yellow_dot} The SQL endpoint '{item_name}' {type.lower()} within the '{workspace_name}' workspace has no tables to refresh..."
204
+ )
205
+
206
+ _update_dataframe_datatypes(df, columns)
207
+
208
+ return df
@@ -1,5 +1,5 @@
1
1
  from sempy_labs._helper_functions import (
2
- resolve_workspace_name_and_id,
2
+ resolve_workspace_id,
3
3
  _base_api,
4
4
  _create_dataframe,
5
5
  _update_dataframe_datatypes,
@@ -9,8 +9,10 @@ from sempy_labs._helper_functions import (
9
9
  import pandas as pd
10
10
  from typing import Optional
11
11
  from uuid import UUID
12
+ from sempy._utils._log import log
12
13
 
13
14
 
15
+ @log
14
16
  def create_sql_database(
15
17
  name: str, description: Optional[str] = None, workspace: Optional[str | UUID] = None
16
18
  ):
@@ -36,6 +38,7 @@ def create_sql_database(
36
38
  )
37
39
 
38
40
 
41
+ @log
39
42
  def delete_sql_database(
40
43
  sql_database: str | UUID, workspace: Optional[str | UUID] = None
41
44
  ):
@@ -57,6 +60,7 @@ def delete_sql_database(
57
60
  delete_item(item=sql_database, type="SQLDatabase", workspace=workspace)
58
61
 
59
62
 
63
+ @log
60
64
  def list_sql_databases(workspace: Optional[str | UUID] = None) -> pd.DataFrame:
61
65
  """
62
66
  Lists all SQL databases in the Fabric workspace.
@@ -78,7 +82,7 @@ def list_sql_databases(workspace: Optional[str | UUID] = None) -> pd.DataFrame:
78
82
  A pandas dataframe showing a list of SQL databases in the Fabric workspace.
79
83
  """
80
84
 
81
- (workspace_name, workspace_id) = resolve_workspace_name_and_id(workspace)
85
+ workspace_id = resolve_workspace_id(workspace)
82
86
 
83
87
  columns = {
84
88
  "SQL Database Name": "string",
@@ -96,6 +100,7 @@ def list_sql_databases(workspace: Optional[str | UUID] = None) -> pd.DataFrame:
96
100
  client="fabric_sp",
97
101
  )
98
102
 
103
+ dfs = []
99
104
  for r in responses:
100
105
  for v in r.get("value", []):
101
106
  prop = v.get("properties", {})
@@ -108,13 +113,16 @@ def list_sql_databases(workspace: Optional[str | UUID] = None) -> pd.DataFrame:
108
113
  "Server FQDN": prop.get("serverFqdn"),
109
114
  }
110
115
 
111
- df = pd.concat([df, pd.DataFrame(new_data, index=[0])], ignore_index=True)
116
+ dfs.append(pd.DataFrame(new_data, index=[0]))
112
117
 
113
- _update_dataframe_datatypes(dataframe=df, column_map=columns)
118
+ if dfs:
119
+ df = pd.concat(dfs, ignore_index=True)
120
+ _update_dataframe_datatypes(dataframe=df, column_map=columns)
114
121
 
115
122
  return df
116
123
 
117
124
 
125
+ @log
118
126
  def get_sql_database_tables(
119
127
  sql_database: str | UUID, workspace: Optional[str | UUID] = None
120
128
  ) -> pd.DataFrame:
@@ -150,6 +158,7 @@ def get_sql_database_tables(
150
158
  return df
151
159
 
152
160
 
161
+ @log
153
162
  def get_sql_database_columns(
154
163
  sql_database: str | UUID, workspace: Optional[str | UUID] = None
155
164
  ) -> pd.DataFrame: