semantic-link-labs 0.12.8__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.
Files changed (243) hide show
  1. semantic_link_labs-0.12.8.dist-info/METADATA +354 -0
  2. semantic_link_labs-0.12.8.dist-info/RECORD +243 -0
  3. semantic_link_labs-0.12.8.dist-info/WHEEL +5 -0
  4. semantic_link_labs-0.12.8.dist-info/licenses/LICENSE +21 -0
  5. semantic_link_labs-0.12.8.dist-info/top_level.txt +1 -0
  6. sempy_labs/__init__.py +606 -0
  7. sempy_labs/_a_lib_info.py +2 -0
  8. sempy_labs/_ai.py +437 -0
  9. sempy_labs/_authentication.py +264 -0
  10. sempy_labs/_bpa_translation/_model/_translations_am-ET.po +869 -0
  11. sempy_labs/_bpa_translation/_model/_translations_ar-AE.po +908 -0
  12. sempy_labs/_bpa_translation/_model/_translations_bg-BG.po +968 -0
  13. sempy_labs/_bpa_translation/_model/_translations_ca-ES.po +963 -0
  14. sempy_labs/_bpa_translation/_model/_translations_cs-CZ.po +943 -0
  15. sempy_labs/_bpa_translation/_model/_translations_da-DK.po +945 -0
  16. sempy_labs/_bpa_translation/_model/_translations_de-DE.po +988 -0
  17. sempy_labs/_bpa_translation/_model/_translations_el-GR.po +993 -0
  18. sempy_labs/_bpa_translation/_model/_translations_es-ES.po +971 -0
  19. sempy_labs/_bpa_translation/_model/_translations_fa-IR.po +933 -0
  20. sempy_labs/_bpa_translation/_model/_translations_fi-FI.po +942 -0
  21. sempy_labs/_bpa_translation/_model/_translations_fr-FR.po +994 -0
  22. sempy_labs/_bpa_translation/_model/_translations_ga-IE.po +967 -0
  23. sempy_labs/_bpa_translation/_model/_translations_he-IL.po +902 -0
  24. sempy_labs/_bpa_translation/_model/_translations_hi-IN.po +944 -0
  25. sempy_labs/_bpa_translation/_model/_translations_hu-HU.po +963 -0
  26. sempy_labs/_bpa_translation/_model/_translations_id-ID.po +946 -0
  27. sempy_labs/_bpa_translation/_model/_translations_is-IS.po +939 -0
  28. sempy_labs/_bpa_translation/_model/_translations_it-IT.po +986 -0
  29. sempy_labs/_bpa_translation/_model/_translations_ja-JP.po +846 -0
  30. sempy_labs/_bpa_translation/_model/_translations_ko-KR.po +839 -0
  31. sempy_labs/_bpa_translation/_model/_translations_mt-MT.po +967 -0
  32. sempy_labs/_bpa_translation/_model/_translations_nl-NL.po +978 -0
  33. sempy_labs/_bpa_translation/_model/_translations_pl-PL.po +962 -0
  34. sempy_labs/_bpa_translation/_model/_translations_pt-BR.po +962 -0
  35. sempy_labs/_bpa_translation/_model/_translations_pt-PT.po +957 -0
  36. sempy_labs/_bpa_translation/_model/_translations_ro-RO.po +968 -0
  37. sempy_labs/_bpa_translation/_model/_translations_ru-RU.po +964 -0
  38. sempy_labs/_bpa_translation/_model/_translations_sk-SK.po +952 -0
  39. sempy_labs/_bpa_translation/_model/_translations_sl-SL.po +950 -0
  40. sempy_labs/_bpa_translation/_model/_translations_sv-SE.po +942 -0
  41. sempy_labs/_bpa_translation/_model/_translations_ta-IN.po +976 -0
  42. sempy_labs/_bpa_translation/_model/_translations_te-IN.po +947 -0
  43. sempy_labs/_bpa_translation/_model/_translations_th-TH.po +924 -0
  44. sempy_labs/_bpa_translation/_model/_translations_tr-TR.po +953 -0
  45. sempy_labs/_bpa_translation/_model/_translations_uk-UA.po +961 -0
  46. sempy_labs/_bpa_translation/_model/_translations_zh-CN.po +804 -0
  47. sempy_labs/_bpa_translation/_model/_translations_zu-ZA.po +969 -0
  48. sempy_labs/_capacities.py +1198 -0
  49. sempy_labs/_capacity_migration.py +660 -0
  50. sempy_labs/_clear_cache.py +351 -0
  51. sempy_labs/_connections.py +610 -0
  52. sempy_labs/_dashboards.py +69 -0
  53. sempy_labs/_data_access_security.py +98 -0
  54. sempy_labs/_data_pipelines.py +162 -0
  55. sempy_labs/_dataflows.py +668 -0
  56. sempy_labs/_dax.py +501 -0
  57. sempy_labs/_daxformatter.py +80 -0
  58. sempy_labs/_delta_analyzer.py +467 -0
  59. sempy_labs/_delta_analyzer_history.py +301 -0
  60. sempy_labs/_dictionary_diffs.py +221 -0
  61. sempy_labs/_documentation.py +147 -0
  62. sempy_labs/_domains.py +51 -0
  63. sempy_labs/_eventhouses.py +182 -0
  64. sempy_labs/_external_data_shares.py +230 -0
  65. sempy_labs/_gateways.py +521 -0
  66. sempy_labs/_generate_semantic_model.py +521 -0
  67. sempy_labs/_get_connection_string.py +84 -0
  68. sempy_labs/_git.py +543 -0
  69. sempy_labs/_graphQL.py +90 -0
  70. sempy_labs/_helper_functions.py +2833 -0
  71. sempy_labs/_icons.py +149 -0
  72. sempy_labs/_job_scheduler.py +609 -0
  73. sempy_labs/_kql_databases.py +149 -0
  74. sempy_labs/_kql_querysets.py +124 -0
  75. sempy_labs/_kusto.py +137 -0
  76. sempy_labs/_labels.py +124 -0
  77. sempy_labs/_list_functions.py +1720 -0
  78. sempy_labs/_managed_private_endpoints.py +253 -0
  79. sempy_labs/_mirrored_databases.py +416 -0
  80. sempy_labs/_mirrored_warehouses.py +60 -0
  81. sempy_labs/_ml_experiments.py +113 -0
  82. sempy_labs/_model_auto_build.py +140 -0
  83. sempy_labs/_model_bpa.py +557 -0
  84. sempy_labs/_model_bpa_bulk.py +378 -0
  85. sempy_labs/_model_bpa_rules.py +859 -0
  86. sempy_labs/_model_dependencies.py +343 -0
  87. sempy_labs/_mounted_data_factories.py +123 -0
  88. sempy_labs/_notebooks.py +441 -0
  89. sempy_labs/_one_lake_integration.py +151 -0
  90. sempy_labs/_onelake.py +131 -0
  91. sempy_labs/_query_scale_out.py +433 -0
  92. sempy_labs/_refresh_semantic_model.py +435 -0
  93. sempy_labs/_semantic_models.py +468 -0
  94. sempy_labs/_spark.py +455 -0
  95. sempy_labs/_sql.py +241 -0
  96. sempy_labs/_sql_audit_settings.py +207 -0
  97. sempy_labs/_sql_endpoints.py +214 -0
  98. sempy_labs/_tags.py +201 -0
  99. sempy_labs/_translations.py +43 -0
  100. sempy_labs/_user_delegation_key.py +44 -0
  101. sempy_labs/_utils.py +79 -0
  102. sempy_labs/_vertipaq.py +1021 -0
  103. sempy_labs/_vpax.py +388 -0
  104. sempy_labs/_warehouses.py +234 -0
  105. sempy_labs/_workloads.py +140 -0
  106. sempy_labs/_workspace_identity.py +72 -0
  107. sempy_labs/_workspaces.py +595 -0
  108. sempy_labs/admin/__init__.py +170 -0
  109. sempy_labs/admin/_activities.py +167 -0
  110. sempy_labs/admin/_apps.py +145 -0
  111. sempy_labs/admin/_artifacts.py +65 -0
  112. sempy_labs/admin/_basic_functions.py +463 -0
  113. sempy_labs/admin/_capacities.py +508 -0
  114. sempy_labs/admin/_dataflows.py +45 -0
  115. sempy_labs/admin/_datasets.py +186 -0
  116. sempy_labs/admin/_domains.py +522 -0
  117. sempy_labs/admin/_external_data_share.py +100 -0
  118. sempy_labs/admin/_git.py +72 -0
  119. sempy_labs/admin/_items.py +265 -0
  120. sempy_labs/admin/_labels.py +211 -0
  121. sempy_labs/admin/_reports.py +241 -0
  122. sempy_labs/admin/_scanner.py +118 -0
  123. sempy_labs/admin/_shared.py +82 -0
  124. sempy_labs/admin/_sharing_links.py +110 -0
  125. sempy_labs/admin/_tags.py +131 -0
  126. sempy_labs/admin/_tenant.py +503 -0
  127. sempy_labs/admin/_tenant_keys.py +89 -0
  128. sempy_labs/admin/_users.py +140 -0
  129. sempy_labs/admin/_workspaces.py +236 -0
  130. sempy_labs/deployment_pipeline/__init__.py +23 -0
  131. sempy_labs/deployment_pipeline/_items.py +580 -0
  132. sempy_labs/directlake/__init__.py +57 -0
  133. sempy_labs/directlake/_autosync.py +58 -0
  134. sempy_labs/directlake/_directlake_schema_compare.py +120 -0
  135. sempy_labs/directlake/_directlake_schema_sync.py +161 -0
  136. sempy_labs/directlake/_dl_helper.py +274 -0
  137. sempy_labs/directlake/_generate_shared_expression.py +94 -0
  138. sempy_labs/directlake/_get_directlake_lakehouse.py +62 -0
  139. sempy_labs/directlake/_get_shared_expression.py +34 -0
  140. sempy_labs/directlake/_guardrails.py +96 -0
  141. sempy_labs/directlake/_list_directlake_model_calc_tables.py +70 -0
  142. sempy_labs/directlake/_show_unsupported_directlake_objects.py +90 -0
  143. sempy_labs/directlake/_update_directlake_model_lakehouse_connection.py +239 -0
  144. sempy_labs/directlake/_update_directlake_partition_entity.py +259 -0
  145. sempy_labs/directlake/_warm_cache.py +236 -0
  146. sempy_labs/dotnet_lib/dotnet.runtime.config.json +10 -0
  147. sempy_labs/environment/__init__.py +23 -0
  148. sempy_labs/environment/_items.py +212 -0
  149. sempy_labs/environment/_pubstage.py +223 -0
  150. sempy_labs/eventstream/__init__.py +37 -0
  151. sempy_labs/eventstream/_items.py +263 -0
  152. sempy_labs/eventstream/_topology.py +652 -0
  153. sempy_labs/graph/__init__.py +59 -0
  154. sempy_labs/graph/_groups.py +651 -0
  155. sempy_labs/graph/_sensitivity_labels.py +120 -0
  156. sempy_labs/graph/_teams.py +125 -0
  157. sempy_labs/graph/_user_licenses.py +96 -0
  158. sempy_labs/graph/_users.py +516 -0
  159. sempy_labs/graph_model/__init__.py +15 -0
  160. sempy_labs/graph_model/_background_jobs.py +63 -0
  161. sempy_labs/graph_model/_items.py +149 -0
  162. sempy_labs/lakehouse/__init__.py +67 -0
  163. sempy_labs/lakehouse/_blobs.py +247 -0
  164. sempy_labs/lakehouse/_get_lakehouse_columns.py +102 -0
  165. sempy_labs/lakehouse/_get_lakehouse_tables.py +274 -0
  166. sempy_labs/lakehouse/_helper.py +250 -0
  167. sempy_labs/lakehouse/_lakehouse.py +351 -0
  168. sempy_labs/lakehouse/_livy_sessions.py +143 -0
  169. sempy_labs/lakehouse/_materialized_lake_views.py +157 -0
  170. sempy_labs/lakehouse/_partitioning.py +165 -0
  171. sempy_labs/lakehouse/_schemas.py +217 -0
  172. sempy_labs/lakehouse/_shortcuts.py +440 -0
  173. sempy_labs/migration/__init__.py +35 -0
  174. sempy_labs/migration/_create_pqt_file.py +238 -0
  175. sempy_labs/migration/_direct_lake_to_import.py +105 -0
  176. sempy_labs/migration/_migrate_calctables_to_lakehouse.py +398 -0
  177. sempy_labs/migration/_migrate_calctables_to_semantic_model.py +148 -0
  178. sempy_labs/migration/_migrate_model_objects_to_semantic_model.py +533 -0
  179. sempy_labs/migration/_migrate_tables_columns_to_semantic_model.py +172 -0
  180. sempy_labs/migration/_migration_validation.py +71 -0
  181. sempy_labs/migration/_refresh_calc_tables.py +131 -0
  182. sempy_labs/mirrored_azure_databricks_catalog/__init__.py +15 -0
  183. sempy_labs/mirrored_azure_databricks_catalog/_discover.py +213 -0
  184. sempy_labs/mirrored_azure_databricks_catalog/_refresh_catalog_metadata.py +45 -0
  185. sempy_labs/ml_model/__init__.py +23 -0
  186. sempy_labs/ml_model/_functions.py +427 -0
  187. sempy_labs/report/_BPAReportTemplate.json +232 -0
  188. sempy_labs/report/__init__.py +55 -0
  189. sempy_labs/report/_bpareporttemplate/.pbi/localSettings.json +9 -0
  190. sempy_labs/report/_bpareporttemplate/.platform +11 -0
  191. sempy_labs/report/_bpareporttemplate/StaticResources/SharedResources/BaseThemes/CY24SU06.json +710 -0
  192. sempy_labs/report/_bpareporttemplate/definition/pages/01d72098bda5055bd500/page.json +11 -0
  193. sempy_labs/report/_bpareporttemplate/definition/pages/01d72098bda5055bd500/visuals/1b08bce3bebabb0a27a8/visual.json +191 -0
  194. sempy_labs/report/_bpareporttemplate/definition/pages/01d72098bda5055bd500/visuals/2f22ddb70c301693c165/visual.json +438 -0
  195. sempy_labs/report/_bpareporttemplate/definition/pages/01d72098bda5055bd500/visuals/3b1182230aa6c600b43a/visual.json +127 -0
  196. sempy_labs/report/_bpareporttemplate/definition/pages/01d72098bda5055bd500/visuals/58577ba6380c69891500/visual.json +576 -0
  197. sempy_labs/report/_bpareporttemplate/definition/pages/01d72098bda5055bd500/visuals/a2a8fa5028b3b776c96c/visual.json +207 -0
  198. sempy_labs/report/_bpareporttemplate/definition/pages/01d72098bda5055bd500/visuals/adfd47ef30652707b987/visual.json +506 -0
  199. sempy_labs/report/_bpareporttemplate/definition/pages/01d72098bda5055bd500/visuals/b6a80ee459e716e170b1/visual.json +127 -0
  200. sempy_labs/report/_bpareporttemplate/definition/pages/01d72098bda5055bd500/visuals/ce3130a721c020cc3d81/visual.json +513 -0
  201. sempy_labs/report/_bpareporttemplate/definition/pages/92735ae19b31712208ad/page.json +8 -0
  202. sempy_labs/report/_bpareporttemplate/definition/pages/92735ae19b31712208ad/visuals/66e60dfb526437cd78d1/visual.json +112 -0
  203. sempy_labs/report/_bpareporttemplate/definition/pages/c597da16dc7e63222a82/page.json +11 -0
  204. sempy_labs/report/_bpareporttemplate/definition/pages/c597da16dc7e63222a82/visuals/07deb8bce824e1be37d7/visual.json +513 -0
  205. sempy_labs/report/_bpareporttemplate/definition/pages/c597da16dc7e63222a82/visuals/0b1c68838818b32ad03b/visual.json +352 -0
  206. sempy_labs/report/_bpareporttemplate/definition/pages/c597da16dc7e63222a82/visuals/0c171de9d2683d10b930/visual.json +37 -0
  207. sempy_labs/report/_bpareporttemplate/definition/pages/c597da16dc7e63222a82/visuals/0efa01be0510e40a645e/visual.json +542 -0
  208. sempy_labs/report/_bpareporttemplate/definition/pages/c597da16dc7e63222a82/visuals/6bf2f0eb830ab53cc668/visual.json +221 -0
  209. sempy_labs/report/_bpareporttemplate/definition/pages/c597da16dc7e63222a82/visuals/88d8141cb8500b60030c/visual.json +127 -0
  210. sempy_labs/report/_bpareporttemplate/definition/pages/c597da16dc7e63222a82/visuals/a753273590beed656a03/visual.json +576 -0
  211. sempy_labs/report/_bpareporttemplate/definition/pages/c597da16dc7e63222a82/visuals/b8fdc82cddd61ac447bc/visual.json +127 -0
  212. sempy_labs/report/_bpareporttemplate/definition/pages/d37dce724a0ccc30044b/page.json +9 -0
  213. sempy_labs/report/_bpareporttemplate/definition/pages/d37dce724a0ccc30044b/visuals/ce8532a7e25020271077/visual.json +38 -0
  214. sempy_labs/report/_bpareporttemplate/definition/pages/pages.json +10 -0
  215. sempy_labs/report/_bpareporttemplate/definition/report.json +176 -0
  216. sempy_labs/report/_bpareporttemplate/definition/version.json +4 -0
  217. sempy_labs/report/_bpareporttemplate/definition.pbir +14 -0
  218. sempy_labs/report/_download_report.py +76 -0
  219. sempy_labs/report/_export_report.py +257 -0
  220. sempy_labs/report/_generate_report.py +427 -0
  221. sempy_labs/report/_paginated.py +76 -0
  222. sempy_labs/report/_report_bpa.py +354 -0
  223. sempy_labs/report/_report_bpa_rules.py +115 -0
  224. sempy_labs/report/_report_functions.py +581 -0
  225. sempy_labs/report/_report_helper.py +227 -0
  226. sempy_labs/report/_report_list_functions.py +110 -0
  227. sempy_labs/report/_report_rebind.py +149 -0
  228. sempy_labs/report/_reportwrapper.py +3100 -0
  229. sempy_labs/report/_save_report.py +147 -0
  230. sempy_labs/snowflake_database/__init__.py +10 -0
  231. sempy_labs/snowflake_database/_items.py +105 -0
  232. sempy_labs/sql_database/__init__.py +21 -0
  233. sempy_labs/sql_database/_items.py +201 -0
  234. sempy_labs/sql_database/_mirroring.py +79 -0
  235. sempy_labs/theme/__init__.py +12 -0
  236. sempy_labs/theme/_org_themes.py +129 -0
  237. sempy_labs/tom/__init__.py +3 -0
  238. sempy_labs/tom/_model.py +5977 -0
  239. sempy_labs/variable_library/__init__.py +19 -0
  240. sempy_labs/variable_library/_functions.py +403 -0
  241. sempy_labs/warehouse/__init__.py +28 -0
  242. sempy_labs/warehouse/_items.py +234 -0
  243. sempy_labs/warehouse/_restore_points.py +309 -0
@@ -0,0 +1,668 @@
1
+ import pandas as pd
2
+ from sempy_labs._helper_functions import (
3
+ resolve_workspace_name_and_id,
4
+ _is_valid_uuid,
5
+ _update_dataframe_datatypes,
6
+ _base_api,
7
+ _create_dataframe,
8
+ resolve_workspace_name,
9
+ resolve_workspace_id,
10
+ _decode_b64,
11
+ _conv_b64,
12
+ get_jsonpath_value,
13
+ resolve_item_id,
14
+ )
15
+ from typing import Optional, Tuple
16
+ import sempy_labs._icons as icons
17
+ from uuid import UUID
18
+ from jsonpath_ng.ext import parse
19
+ import json
20
+ from sempy._utils._log import log
21
+
22
+
23
+ @log
24
+ def list_dataflows(workspace: Optional[str | UUID] = None):
25
+ """
26
+ Shows a list of all dataflows which exist within a workspace.
27
+
28
+ This is a wrapper function for the following API: `Items - List Dataflows <https://learn.microsoft.com/rest/api/fabric/dataflow/items/list-dataflows>`_.
29
+
30
+ Service Principal Authentication is supported (see `here <https://github.com/microsoft/semantic-link-labs/blob/main/notebooks/Service%20Principal.ipynb>`_ for examples).
31
+
32
+ Parameters
33
+ ----------
34
+ workspace : str | uuid.UUID, default=None
35
+ The Fabric workspace name or ID.
36
+ Defaults to None which resolves to the workspace of the attached lakehouse
37
+ or if no lakehouse attached, resolves to the workspace of the notebook.
38
+
39
+ Returns
40
+ -------
41
+ pandas.DataFrame
42
+ A pandas dataframe showing the dataflows which exist within a workspace.
43
+ """
44
+
45
+ workspace_id = resolve_workspace_id(workspace)
46
+
47
+ columns = {
48
+ "Dataflow Id": "string",
49
+ "Dataflow Name": "string",
50
+ "Description": "string",
51
+ "Configured By": "string",
52
+ "Users": "string",
53
+ "Generation": "string",
54
+ }
55
+ df = _create_dataframe(columns=columns)
56
+
57
+ response = _base_api(
58
+ request=f"/v1.0/myorg/groups/{workspace_id}/dataflows", client="fabric_sp"
59
+ )
60
+
61
+ rows = []
62
+ for v in response.json().get("value", []):
63
+ gen = v.get("generation")
64
+ rows.append(
65
+ {
66
+ "Dataflow Id": v.get("objectId"),
67
+ "Dataflow Name": v.get("name"),
68
+ "Description": "",
69
+ "Configured By": v.get("configuredBy"),
70
+ "Users": ", ".join(v.get("users", [])),
71
+ "Generation": "Gen2" if gen == 2 else "Gen1",
72
+ }
73
+ )
74
+
75
+ responses = _base_api(
76
+ request=f"/v1/workspaces/{workspace_id}/dataflows",
77
+ client="fabric_sp",
78
+ uses_pagination=True,
79
+ )
80
+ for r in responses:
81
+ for v in r.get("value", []):
82
+ gen = v.get("generation")
83
+ rows.append(
84
+ {
85
+ "Dataflow Id": v.get("id"),
86
+ "Dataflow Name": v.get("displayName"),
87
+ "Description": v.get("description"),
88
+ "Configured By": "",
89
+ "Users": "",
90
+ "Generation": "Gen2 CI/CD",
91
+ }
92
+ )
93
+
94
+ if rows:
95
+ df = pd.DataFrame(rows, columns=list(columns.keys()))
96
+ _update_dataframe_datatypes(dataframe=df, column_map=columns)
97
+
98
+ return df
99
+
100
+
101
+ @log
102
+ def assign_workspace_to_dataflow_storage(
103
+ dataflow_storage_account: str, workspace: Optional[str | UUID] = None
104
+ ):
105
+ """
106
+ Assigns a dataflow storage account to a workspace.
107
+
108
+ This is a wrapper function for the following API: `Dataflow Storage Accounts - Groups AssignToDataflowStorage <https://learn.microsoft.com/rest/api/power-bi/dataflow-storage-accounts/groups-assign-to-dataflow-storage>`_.
109
+
110
+ Parameters
111
+ ----------
112
+ dataflow_storage_account : str
113
+ The name of the dataflow storage account.
114
+ workspace : str | uuid.UUID, default=None
115
+ The name or ID of the workspace.
116
+ Defaults to None which resolves to the workspace of the attached lakehouse
117
+ or if no lakehouse attached, resolves to the workspace of the notebook.
118
+ """
119
+
120
+ (workspace_name, workspace_id) = resolve_workspace_name_and_id(workspace)
121
+
122
+ df = list_dataflow_storage_accounts()
123
+ df_filt = df[df["Dataflow Storage Account Name"] == dataflow_storage_account]
124
+
125
+ if len(df_filt) == 0:
126
+ raise ValueError(
127
+ f"{icons.red_dot} The '{dataflow_storage_account}' does not exist."
128
+ )
129
+
130
+ dataflow_storage_id = df_filt["Dataflow Storage Account ID"].iloc[0]
131
+ payload = {"dataflowStorageId": dataflow_storage_id}
132
+
133
+ _base_api(
134
+ request=f"/v1.0/myorg/groups/{workspace_id}/AssignToDataflowStorage",
135
+ method="post",
136
+ payload=payload,
137
+ )
138
+
139
+ print(
140
+ f"{icons.green_dot} The '{dataflow_storage_account}' dataflow storage account has been assigned to the '{workspace_name}' workspacce."
141
+ )
142
+
143
+
144
+ @log
145
+ def list_dataflow_storage_accounts() -> pd.DataFrame:
146
+ """
147
+ Shows the accessible dataflow storage accounts.
148
+
149
+ This is a wrapper function for the following API: `Dataflow Storage Accounts - Get Dataflow Storage Accounts <https://learn.microsoft.com/rest/api/power-bi/dataflow-storage-accounts/get-dataflow-storage-accounts>`_.
150
+
151
+ Returns
152
+ -------
153
+ pandas.DataFrame
154
+ A pandas dataframe showing the accessible dataflow storage accounts.
155
+ """
156
+
157
+ columns = {
158
+ "Dataflow Storage Account ID": "string",
159
+ "Dataflow Storage Account Name": "string",
160
+ "Enabled": "bool",
161
+ }
162
+ df = _create_dataframe(columns=columns)
163
+
164
+ response = _base_api(request="/v1.0/myorg/dataflowStorageAccounts")
165
+
166
+ rows = []
167
+ for v in response.json().get("value", []):
168
+ rows.append(
169
+ {
170
+ "Dataflow Storage Account ID": v.get("id"),
171
+ "Dataflow Storage Account Name": v.get("name"),
172
+ "Enabled": v.get("isEnabled"),
173
+ }
174
+ )
175
+
176
+ if rows:
177
+ df = pd.DataFrame(rows, columns=list(columns.keys()))
178
+ _update_dataframe_datatypes(dataframe=df, column_map=columns)
179
+
180
+ return df
181
+
182
+
183
+ @log
184
+ def list_upstream_dataflows(
185
+ dataflow: str | UUID, workspace: Optional[str | UUID] = None
186
+ ) -> pd.DataFrame:
187
+ """
188
+ Shows a list of upstream dataflows for the specified dataflow.
189
+
190
+ This is a wrapper function for the following API: `Dataflows - Get Upstream Dataflows In Group <https://learn.microsoft.com/rest/api/power-bi/dataflows/get-upstream-dataflows-in-group>`_.
191
+
192
+ Service Principal Authentication is supported (see `here <https://github.com/microsoft/semantic-link-labs/blob/main/notebooks/Service%20Principal.ipynb>`_ for examples).
193
+
194
+ Parameters
195
+ ----------
196
+ dataflow : str | uuid.UUID
197
+ Name or UUID of the dataflow.
198
+ workspace : str | uuid.UUID, default=None
199
+ The Fabric workspace name or ID.
200
+ Defaults to None which resolves to the workspace of the attached lakehouse
201
+ or if no lakehouse attached, resolves to the workspace of the notebook.
202
+
203
+ Returns
204
+ -------
205
+ pandas.DataFrame
206
+ A pandas dataframe showing a list of upstream dataflows for the specified dataflow.
207
+ """
208
+
209
+ (workspace_name, workspace_id) = resolve_workspace_name_and_id(workspace)
210
+ (dataflow_name, dataflow_id, dataflow_generation) = (
211
+ _resolve_dataflow_name_and_id_and_generation(
212
+ dataflow=dataflow, workspace=workspace_id
213
+ )
214
+ )
215
+
216
+ columns = {
217
+ "Dataflow Name": "string",
218
+ "Dataflow Id": "string",
219
+ "Workspace Name": "string",
220
+ "Workspace Id": "string",
221
+ "Upstream Dataflow Name": "string",
222
+ "Upstream Dataflow Id": "string",
223
+ "Upstream Workspace Name": "string",
224
+ "Upstream Workspace Id": "string",
225
+ }
226
+ df = _create_dataframe(columns=columns)
227
+
228
+ def collect_upstreams(dataflow_id, dataflow_name, workspace_id, workspace_name):
229
+ response = _base_api(
230
+ request=f"/v1.0/myorg/groups/{workspace_id}/dataflows/{dataflow_id}/upstreamDataflows",
231
+ client="fabric_sp",
232
+ )
233
+
234
+ values = response.json().get("value", [])
235
+ for v in values:
236
+ tgt_dataflow_id = v.get("targetDataflowId")
237
+ tgt_workspace_id = v.get("groupId")
238
+ tgt_workspace_name = resolve_workspace_name(workspace_id=tgt_workspace_id)
239
+ (tgt_dataflow_name, _, _) = _resolve_dataflow_name_and_id_and_generation(
240
+ dataflow=tgt_dataflow_id, workspace=tgt_workspace_id
241
+ )
242
+
243
+ df.loc[len(df)] = {
244
+ "Dataflow Name": dataflow_name,
245
+ "Dataflow Id": dataflow_id,
246
+ "Workspace Name": workspace_name,
247
+ "Workspace Id": workspace_id,
248
+ "Upstream Dataflow Name": tgt_dataflow_name,
249
+ "Upstream Dataflow Id": tgt_dataflow_id,
250
+ "Upstream Workspace Name": tgt_workspace_name,
251
+ "Upstream Workspace Id": tgt_workspace_id,
252
+ }
253
+
254
+ collect_upstreams(
255
+ tgt_dataflow_id,
256
+ tgt_dataflow_name,
257
+ tgt_workspace_id,
258
+ tgt_workspace_name,
259
+ )
260
+
261
+ collect_upstreams(dataflow_id, dataflow_name, workspace_id, workspace_name)
262
+
263
+ return df
264
+
265
+
266
+ @log
267
+ def _resolve_dataflow_name_and_id_and_generation(
268
+ dataflow: str | UUID, workspace: Optional[str | UUID] = None
269
+ ) -> Tuple[str, UUID, str]:
270
+
271
+ (workspace_name, workspace_id) = resolve_workspace_name_and_id(workspace)
272
+
273
+ dfD = list_dataflows(workspace=workspace_id)
274
+
275
+ if _is_valid_uuid(dataflow):
276
+ dfD_filt = dfD[dfD["Dataflow Id"] == dataflow]
277
+ else:
278
+ dfD_filt = dfD[dfD["Dataflow Name"] == dataflow]
279
+
280
+ if dfD_filt.empty:
281
+ raise ValueError(
282
+ f"{icons.red_dot} The '{dataflow}' dataflow does not exist within the '{workspace_name}' workspace."
283
+ )
284
+
285
+ dataflow_id = dfD_filt["Dataflow Id"].iloc[0]
286
+ dataflow_name = dfD_filt["Dataflow Name"].iloc[0]
287
+ dataflow_generation = dfD_filt["Generation"].iloc[0]
288
+
289
+ return (dataflow_name, dataflow_id, dataflow_generation)
290
+
291
+
292
+ @log
293
+ def get_dataflow_definition(
294
+ dataflow: str | UUID,
295
+ workspace: Optional[str | UUID] = None,
296
+ decode: bool = True,
297
+ ) -> dict:
298
+ """
299
+ Obtains the definition of a dataflow. This supports Gen1, Gen2 and Gen 2 CI/CD dataflows.
300
+
301
+ This is a wrapper function for the following API: `Dataflows - Get Dataflow <https://learn.microsoft.com/rest/api/power-bi/dataflows/get-dataflow>`_.
302
+
303
+ Parameters
304
+ ----------
305
+ dataflow : str | uuid.UUID
306
+ The name or ID of the dataflow.
307
+ workspace : str | uuid.UUID, default=None
308
+ The Fabric workspace name.
309
+ Defaults to None, which resolves to the workspace of the attached lakehouse
310
+ or if no lakehouse is attached, resolves to the workspace of the notebook.
311
+ decode : bool, optional
312
+ If True, decodes the dataflow definition file.
313
+
314
+ Returns
315
+ -------
316
+ dict
317
+ The dataflow definition.
318
+ """
319
+
320
+ workspace_id = resolve_workspace_id(workspace)
321
+
322
+ (dataflow_name, dataflow_id, dataflow_generation) = (
323
+ _resolve_dataflow_name_and_id_and_generation(
324
+ dataflow=dataflow, workspace=workspace_id
325
+ )
326
+ )
327
+
328
+ if dataflow_generation == "Gen2 CI/CD":
329
+ result = _base_api(
330
+ request=f"/v1/workspaces/{workspace_id}/items/{dataflow_id}/getDefinition",
331
+ client="fabric_sp",
332
+ method="post",
333
+ lro_return_json=True,
334
+ status_codes=[200, 202],
335
+ )
336
+
337
+ if decode:
338
+ # Decode the payload from base64
339
+ definition = {"definition": {"parts": []}}
340
+
341
+ for part in result.get("definition", {}).get("parts", []):
342
+ path = part.get("path")
343
+ payload = part.get("payload")
344
+ decoded_payload = _decode_b64(payload)
345
+ definition["definition"]["parts"].append(
346
+ {"path": path, "payload": decoded_payload}
347
+ )
348
+ return definition
349
+ else:
350
+ return result
351
+ else:
352
+ result = _base_api(
353
+ request=f"/v1.0/myorg/groups/{workspace_id}/dataflows/{dataflow_id}",
354
+ client="fabric_sp",
355
+ ).json()
356
+
357
+ return result
358
+
359
+
360
+ @log
361
+ def upgrade_dataflow(
362
+ dataflow: str | UUID,
363
+ workspace: Optional[str | UUID] = None,
364
+ new_dataflow_name: Optional[str] = None,
365
+ new_dataflow_workspace: Optional[str | UUID] = None,
366
+ ):
367
+ """
368
+ Creates a Dataflow Gen2 CI/CD item based on the mashup definition from an existing Gen1/Gen2 dataflow. After running this function, update the connections in the dataflow to ensure the data can be properly refreshed.
369
+
370
+ Parameters
371
+ ----------
372
+ dataflow : str | uuid.UUID
373
+ The name or ID of the dataflow.
374
+ workspace : str | uuid.UUID, default=None
375
+ The workspace name or ID.
376
+ Defaults to None which resolves to the workspace of the attached lakehouse
377
+ or if no lakehouse attached, resolves to the workspace of the notebook.
378
+ new_dataflow_name: str, default=None
379
+ Name of the new dataflow.
380
+ new_dataflow_workspace : str | uuid.UUID, default=None
381
+ The Fabric workspace name or ID of the dataflow to be created.
382
+ Defaults to None which resolves to the existing workspace of the attached lakehouse
383
+ or if no lakehouse attached, resolves to the workspace of the notebook.
384
+ """
385
+
386
+ # Resolve the workspace name and ID
387
+ (workspace, workspace_id) = resolve_workspace_name_and_id(workspace)
388
+
389
+ # Resolve the dataflow name and ID
390
+ (dataflow_name, dataflow_id, dataflow_generation) = (
391
+ _resolve_dataflow_name_and_id_and_generation(dataflow, workspace_id)
392
+ )
393
+
394
+ if dataflow_generation == "Gen2 CI/CD":
395
+ # Print an error message that the dataflow is already a native Fabric item
396
+ print(
397
+ f"{icons.info} The dataflow '{dataflow_name}' is already a Fabric native Dataflow Gen2 item. No changes made."
398
+ )
399
+ return
400
+
401
+ (new_dataflow_workspace, new_dataflow_workspace_id) = resolve_workspace_name_and_id(
402
+ new_dataflow_workspace
403
+ )
404
+
405
+ # If no new dataflow name is provided, use the existing dataflow name
406
+ if not new_dataflow_name:
407
+ new_dataflow_name = dataflow_name
408
+
409
+ # Get dataflow definition
410
+ definition = get_dataflow_definition(dataflow, workspace_id)
411
+
412
+ # Check for linked table references
413
+ matches = (
414
+ parse("$['pbi:mashup'].connectionOverrides[*].kind").find(definition) or []
415
+ )
416
+ if any(match.value in {"PowerPlatformDataflows", "PowerBI"} for match in matches):
417
+ print(
418
+ f"""{icons.red_dot} The dataflow '{dataflow_name}' contains a linked table reference to an existing dataflow as a connection source and will not be upgraded. No changes were made.
419
+ - To track the upstream lineage of linked tables across dataflows use the list_upstream_dataflows function.
420
+ - To automatically remove the tables and upgrade the existing dataflow use the upgrade_powerbippdf_dataflow function."""
421
+ )
422
+ return
423
+
424
+ description = get_jsonpath_value(data=definition, path="$.description")
425
+
426
+ payload = {
427
+ "displayName": new_dataflow_name,
428
+ }
429
+ if description:
430
+ payload["description"] = description
431
+
432
+ # Query Groups
433
+ matches = parse("$.annotations[?(@.name=='pbi:QueryGroups')].value").find(
434
+ definition
435
+ )
436
+ query_groups_value = json.loads(matches[0].value) if matches else []
437
+
438
+ queries_metadata = get_jsonpath_value(
439
+ data=definition, path="$['pbi:mashup'].queriesMetadata"
440
+ )
441
+
442
+ default_staging = True if "DefaultStaging" in queries_metadata else False
443
+
444
+ # Collect keys to delete
445
+ keys_to_delete = [
446
+ key
447
+ for key in queries_metadata
448
+ if key.endswith("_DataDestination")
449
+ or key.endswith("_WriteToDataDestination")
450
+ or key.endswith("_TransformForWriteToDataDestination")
451
+ or key == "FastCopyStaging"
452
+ ]
453
+
454
+ # Delete them
455
+ for key in keys_to_delete:
456
+ del queries_metadata[key]
457
+
458
+ # Set load enabled and isHidden
459
+ for key, items in queries_metadata.items():
460
+ items["loadEnabled"] = False
461
+ if key in ["DefaultDestination", "DefaultStaging"]:
462
+ items["isHidden"] = True
463
+
464
+ # Prepare the dataflow definition
465
+ query_metadata = {
466
+ "formatVersion": "202502",
467
+ "computeEngineSettings": {}, # How to set this?
468
+ "name": new_dataflow_name,
469
+ "queryGroups": query_groups_value,
470
+ "documentLocale": get_jsonpath_value(data=definition, path="$.culture"),
471
+ "queriesMetadata": queries_metadata,
472
+ "fastCombine": get_jsonpath_value(
473
+ data=definition, path="$['pbi:mashup'].fastCombine", default=False
474
+ ),
475
+ "allowNativeQueries": get_jsonpath_value(
476
+ data=definition, path="$['pbi:mashup'].allowNativeQueries", default=False
477
+ ),
478
+ # "connections": [],
479
+ }
480
+
481
+ fast_copy = get_jsonpath_value(
482
+ data=definition, path="$['ppdf:fastCopy']", default=False
483
+ )
484
+ max_concurrency = get_jsonpath_value(
485
+ data=definition, path="$['ppdf:maxConcurrency']"
486
+ )
487
+ if fast_copy:
488
+ query_metadata["computeEngineSettings"] = {}
489
+
490
+ if max_concurrency:
491
+ query_metadata["computeEngineSettings"]["maxConcurrency"] = max_concurrency
492
+
493
+ mashup_doc = get_jsonpath_value(data=definition, path="$['pbi:mashup'].document")
494
+
495
+ # Remove the FastCopyStaging section if it exists
496
+ new_mashup_doc = ""
497
+ if default_staging and fast_copy:
498
+ new_mashup_doc = '[DefaultOutputDestinationSettings = [DestinationDefinition = [Kind = "Reference", QueryName = "DefaultDestination", IsNewTarget = true], UpdateMethod = [Kind = "Replace"]], StagingDefinition = [Kind = "FastCopy"]]\r\nsection Section1'
499
+ elif default_staging and not fast_copy:
500
+ new_mashup_doc = '[DefaultOutputDestinationSettings = [DestinationDefinition = [Kind = "Reference", QueryName = "DefaultDestination", IsNewTarget = true], UpdateMethod = [Kind = "Replace"]]\r\nsection Section1'
501
+ elif not default_staging and fast_copy:
502
+ new_mashup_doc = '[StagingDefinition = [Kind = "FastCopy"]]\r\nsection Section1'
503
+ else:
504
+ new_mashup_doc = "section Section1"
505
+ for i in mashup_doc.split(";\r\nshared "):
506
+ # if 'IsParameterQuery=true' in i:
507
+ # Add to queries_metadata
508
+ if not (
509
+ "FastCopyStaging = let" in i
510
+ or '_WriteToDataDestination" = let' in i
511
+ or "_WriteToDataDestination = let" in i
512
+ or '_DataDestination" = let' in i
513
+ or "_DataDestination = let" in i
514
+ or '_TransformForWriteToDataDestination" = let' in i
515
+ or "_TransformForWriteToDataDestination = let" in i
516
+ ):
517
+ if i != "section Section1":
518
+ if default_staging and (
519
+ "IsParameterQuery=true" not in i
520
+ and not i.startswith("DefaultStaging")
521
+ and not i.startswith("DefaultDestination")
522
+ ):
523
+ new_mashup_doc += (
524
+ ";\r\n[BindToDefaultDestination = true]\r\nshared " + i
525
+ )
526
+ else:
527
+ new_mashup_doc += ";\r\nshared " + i
528
+ new_mashup_doc = f"{new_mashup_doc};"
529
+
530
+ # Add the dataflow definition to the payload
531
+ new_definition = {
532
+ "parts": [
533
+ {
534
+ "path": "queryMetadata.json",
535
+ "payload": _conv_b64(query_metadata),
536
+ "payloadType": "InlineBase64",
537
+ },
538
+ {
539
+ "path": "mashup.pq",
540
+ "payload": _conv_b64(new_mashup_doc, json_dumps=False),
541
+ "payloadType": "InlineBase64",
542
+ },
543
+ ]
544
+ }
545
+
546
+ create_dataflow(
547
+ name=new_dataflow_name,
548
+ workspace=new_dataflow_workspace,
549
+ definition=new_definition,
550
+ )
551
+
552
+
553
+ @log
554
+ def create_dataflow(
555
+ name: str,
556
+ workspace: Optional[str | UUID] = None,
557
+ description: Optional[str] = None,
558
+ definition: Optional[dict] = None,
559
+ ):
560
+ """
561
+ Creates a native Fabric Dataflow Gen2 CI/CD item.
562
+
563
+ This is a wrapper function for the following API: `Items - Create Dataflow <https://learn.microsoft.com/rest/api/fabric/dataflow/items/create-dataflow>`_.
564
+
565
+ Service Principal Authentication is supported (see `here <https://github.com/microsoft/semantic-link-labs/blob/main/notebooks/Service%20Principal.ipynb>`_ for examples).
566
+
567
+ Parameters
568
+ ----------
569
+ name : str
570
+ The name the dataflow.
571
+ workspace : str | uuid.UUID, default=None
572
+ The workspace name or ID.
573
+ Defaults to None which resolves to the workspace of the attached lakehouse
574
+ or if no lakehouse attached, resolves to the workspace of the notebook.
575
+ description : str, default=None
576
+ The description of the dataflow.
577
+ definition : dict, default=None
578
+ The definition of the dataflow in the form of a dictionary.
579
+ """
580
+
581
+ (workspace_name, workspace_id) = resolve_workspace_name_and_id(workspace)
582
+
583
+ payload = {
584
+ "displayName": name,
585
+ }
586
+ if description:
587
+ payload["description"] = description
588
+
589
+ if definition:
590
+ payload["definition"] = definition
591
+
592
+ _base_api(
593
+ request=f"/v1/workspaces/{workspace_id}/dataflows",
594
+ method="post",
595
+ payload=payload,
596
+ client="fabric_sp",
597
+ lro_return_json=True,
598
+ status_codes=[201, 202],
599
+ )
600
+
601
+ print(
602
+ f"{icons.green_dot} The dataflow '{name}' has been created within the '{workspace_name}' workspace."
603
+ )
604
+
605
+
606
+ @log
607
+ def discover_dataflow_parameters(
608
+ dataflow: str | UUID, workspace: str | UUID
609
+ ) -> pd.DataFrame:
610
+ """
611
+ Retrieves all parameters defined in the specified Dataflow.
612
+
613
+ This is a wrapper function for the following API: `Items - Discover Dataflow Parameters <https://learn.microsoft.com/rest/api/fabric/dataflow/items/discover-dataflow-parameters>`_.
614
+
615
+ Service Principal Authentication is supported (see `here <https://github.com/microsoft/semantic-link-labs/blob/main/notebooks/Service%20Principal.ipynb>`_ for examples).
616
+
617
+ Parameters
618
+ ----------
619
+ dataflow : str | uuid.UUID
620
+ Name or ID of the dataflow.
621
+ workspace : str | uuid.UUID, default=None
622
+ The Fabric workspace name or ID.
623
+ Defaults to None which resolves to the workspace of the attached lakehouse
624
+ or if no lakehouse attached, resolves to the workspace of the notebook.
625
+
626
+ Returns
627
+ -------
628
+ pandas.DataFrame
629
+ A pandas dataframe showing all parameters defined in the specified Dataflow.
630
+ """
631
+
632
+ workspace_id = resolve_workspace_id(workspace)
633
+ dataflow_id = resolve_item_id(
634
+ item=dataflow, type="Dataflow", workspace=workspace_id
635
+ )
636
+ responses = _base_api(
637
+ request=f"/v1/workspaces/{workspace_id}/dataflows/{dataflow_id}/parameters",
638
+ client="fabric_sp",
639
+ uses_pagination=True,
640
+ )
641
+
642
+ columns = {
643
+ "Parameter Name": "string",
644
+ "Is Required": "bool",
645
+ "Description": "string",
646
+ "Parameter Type": "string",
647
+ "Default Value": "string",
648
+ }
649
+
650
+ df = _create_dataframe(columns=columns)
651
+ rows = []
652
+ for r in responses:
653
+ for v in r.get("value", []):
654
+ rows.append(
655
+ {
656
+ "Parameter Name": v.get("name"),
657
+ "Is Required": v.get("isRequired"),
658
+ "Description": v.get("description"),
659
+ "Parameter Type": v.get("type"),
660
+ "Default Value": v.get("defaultValue"),
661
+ }
662
+ )
663
+
664
+ if rows:
665
+ df = pd.DataFrame(rows, columns=list(columns.keys()))
666
+ _update_dataframe_datatypes(dataframe=df, column_map=columns)
667
+
668
+ return df