semantic-link-labs 0.9.2__py3-none-any.whl → 0.9.4__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 (54) hide show
  1. {semantic_link_labs-0.9.2.dist-info → semantic_link_labs-0.9.4.dist-info}/METADATA +10 -6
  2. {semantic_link_labs-0.9.2.dist-info → semantic_link_labs-0.9.4.dist-info}/RECORD +54 -44
  3. {semantic_link_labs-0.9.2.dist-info → semantic_link_labs-0.9.4.dist-info}/WHEEL +1 -1
  4. sempy_labs/__init__.py +27 -1
  5. sempy_labs/_ai.py +8 -5
  6. sempy_labs/_capacity_migration.py +3 -2
  7. sempy_labs/_connections.py +45 -9
  8. sempy_labs/_dax.py +17 -3
  9. sempy_labs/_delta_analyzer.py +308 -138
  10. sempy_labs/_eventhouses.py +70 -1
  11. sempy_labs/_gateways.py +56 -8
  12. sempy_labs/_generate_semantic_model.py +30 -9
  13. sempy_labs/_helper_functions.py +84 -9
  14. sempy_labs/_job_scheduler.py +226 -2
  15. sempy_labs/_list_functions.py +42 -19
  16. sempy_labs/_ml_experiments.py +1 -1
  17. sempy_labs/_model_bpa.py +17 -2
  18. sempy_labs/_model_bpa_rules.py +20 -8
  19. sempy_labs/_semantic_models.py +117 -0
  20. sempy_labs/_sql.py +73 -6
  21. sempy_labs/_sqldatabase.py +227 -0
  22. sempy_labs/_translations.py +2 -2
  23. sempy_labs/_vertipaq.py +3 -3
  24. sempy_labs/_warehouses.py +1 -1
  25. sempy_labs/admin/__init__.py +49 -8
  26. sempy_labs/admin/_activities.py +166 -0
  27. sempy_labs/admin/_apps.py +143 -0
  28. sempy_labs/admin/_basic_functions.py +32 -652
  29. sempy_labs/admin/_capacities.py +250 -0
  30. sempy_labs/admin/_datasets.py +184 -0
  31. sempy_labs/admin/_domains.py +1 -3
  32. sempy_labs/admin/_items.py +3 -1
  33. sempy_labs/admin/_reports.py +165 -0
  34. sempy_labs/admin/_scanner.py +53 -49
  35. sempy_labs/admin/_shared.py +74 -0
  36. sempy_labs/admin/_tenant.py +489 -0
  37. sempy_labs/directlake/_dl_helper.py +0 -1
  38. sempy_labs/directlake/_update_directlake_partition_entity.py +6 -0
  39. sempy_labs/graph/_teams.py +1 -1
  40. sempy_labs/graph/_users.py +9 -1
  41. sempy_labs/lakehouse/_get_lakehouse_columns.py +2 -2
  42. sempy_labs/lakehouse/_get_lakehouse_tables.py +2 -2
  43. sempy_labs/lakehouse/_lakehouse.py +3 -3
  44. sempy_labs/lakehouse/_shortcuts.py +29 -16
  45. sempy_labs/migration/_migrate_calctables_to_lakehouse.py +2 -2
  46. sempy_labs/migration/_refresh_calc_tables.py +2 -2
  47. sempy_labs/report/__init__.py +3 -1
  48. sempy_labs/report/_download_report.py +4 -1
  49. sempy_labs/report/_export_report.py +272 -0
  50. sempy_labs/report/_report_functions.py +11 -263
  51. sempy_labs/report/_report_rebind.py +1 -1
  52. sempy_labs/tom/_model.py +281 -29
  53. {semantic_link_labs-0.9.2.dist-info → semantic_link_labs-0.9.4.dist-info}/LICENSE +0 -0
  54. {semantic_link_labs-0.9.2.dist-info → semantic_link_labs-0.9.4.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,250 @@
1
+ import pandas as pd
2
+ from uuid import UUID
3
+ import sempy_labs._icons as icons
4
+ from typing import Optional, Tuple
5
+ from sempy._utils._log import log
6
+ from sempy_labs._helper_functions import (
7
+ _base_api,
8
+ _create_dataframe,
9
+ _update_dataframe_datatypes,
10
+ _is_valid_uuid,
11
+ get_capacity_id,
12
+ )
13
+
14
+
15
+ def patch_capacity(capacity: str | UUID, tenant_key_id: UUID):
16
+ """
17
+ Changes specific capacity information. Currently, this API call only supports changing the capacity's encryption key.
18
+
19
+ This is a wrapper function for the following API: `Admin - Patch Capacity As Admin <https://learn.microsoft.com/rest/api/power-bi/admin/patch-capacity-as-admin>`_.
20
+
21
+ Parameters
22
+ ----------
23
+ capacity : str | uuid.UUID
24
+ The name or ID of the capacity.
25
+ tenant_key_id : str
26
+ The ID of the encryption key.
27
+ """
28
+
29
+ (capacity_name, capacity_id) = _resolve_capacity_name_and_id(capacity)
30
+
31
+ payload = {
32
+ "tenantKeyId": tenant_key_id,
33
+ }
34
+
35
+ _base_api(
36
+ request=f"/v1.0/myorg/admin/capacities/{capacity_id}",
37
+ method="patch",
38
+ payload=payload,
39
+ )
40
+
41
+ print(
42
+ f"{icons.green_dot} The capacity '{capacity_name}' has been successfully patched."
43
+ )
44
+
45
+
46
+ def _resolve_capacity_name_and_id(
47
+ capacity: str | UUID,
48
+ ) -> Tuple[str, UUID]:
49
+
50
+ dfC = list_capacities(capacity=capacity)
51
+ if dfC.empty:
52
+ raise ValueError(f"{icons.red_dot} The '{capacity}' capacity was not found.")
53
+
54
+ capacity_name = dfC["Capacity Name"].iloc[0]
55
+ capacity_id = dfC["Capacity Id"].iloc[0]
56
+
57
+ return capacity_name, capacity_id
58
+
59
+
60
+ def _list_capacities_meta() -> pd.DataFrame:
61
+ """
62
+ Shows the a list of capacities and their properties. This function is the admin version.
63
+
64
+ This is a wrapper function for the following API: `Admin - Get Capacities As Admin <https://learn.microsoft.com/rest/api/power-bi/admin/get-capacities-as-admin>`_.
65
+
66
+ Returns
67
+ -------
68
+ pandas.DataFrame
69
+ A pandas dataframe showing the capacities and their properties
70
+ """
71
+
72
+ columns = {
73
+ "Capacity Id": "string",
74
+ "Capacity Name": "string",
75
+ "Sku": "string",
76
+ "Region": "string",
77
+ "State": "string",
78
+ "Admins": "string",
79
+ }
80
+ df = _create_dataframe(columns=columns)
81
+
82
+ responses = _base_api(
83
+ request="/v1.0/myorg/admin/capacities", client="fabric_sp", uses_pagination=True
84
+ )
85
+
86
+ for r in responses:
87
+ for i in r.get("value", []):
88
+ new_data = {
89
+ "Capacity Id": i.get("id").lower(),
90
+ "Capacity Name": i.get("displayName"),
91
+ "Sku": i.get("sku"),
92
+ "Region": i.get("region"),
93
+ "State": i.get("state"),
94
+ "Admins": [i.get("admins", [])],
95
+ }
96
+ df = pd.concat([df, pd.DataFrame(new_data, index=[0])], ignore_index=True)
97
+
98
+ return df
99
+
100
+
101
+ def get_capacity_assignment_status(
102
+ workspace: Optional[str | UUID] = None,
103
+ ) -> pd.DataFrame:
104
+ """
105
+ Gets the status of the assignment-to-capacity operation for the specified workspace.
106
+
107
+ This is a wrapper function for the following API: `Capacities - Groups CapacityAssignmentStatus <https://learn.microsoft.com/rest/api/power-bi/capacities/groups-capacity-assignment-status>`_.
108
+
109
+ Service Principal Authentication is supported (see `here <https://github.com/microsoft/semantic-link-labs/blob/main/notebooks/Service%20Principal.ipynb>`_ for examples).
110
+
111
+ Parameters
112
+ ----------
113
+ workspace : str | uuid.UUID, default=None
114
+ The workspace name or ID.
115
+ Defaults to None which resolves to the workspace of the attached lakehouse
116
+ or if no lakehouse attached, resolves to the workspace of the notebook.
117
+
118
+ Returns
119
+ -------
120
+ pandas.DataFrame
121
+ A pandas dataframe showing the status of the assignment-to-capacity operation for the specified workspace.
122
+ """
123
+ from sempy_labs.admin._basic_functions import _resolve_workspace_name_and_id
124
+
125
+ (workspace_name, workspace_id) = _resolve_workspace_name_and_id(workspace)
126
+
127
+ columns = {
128
+ "Status": "string",
129
+ "Activity Id": "string",
130
+ "Start Time": "datetime",
131
+ "End Time": "datetime",
132
+ "Capacity Id": "string",
133
+ "Capacity Name": "string",
134
+ }
135
+ df = _create_dataframe(columns=columns)
136
+
137
+ response = _base_api(
138
+ request=f"/v1.0/myorg/groups/{workspace_id}/CapacityAssignmentStatus",
139
+ client="fabric_sp",
140
+ )
141
+ v = response.json()
142
+ capacity_id = v.get("capacityId")
143
+
144
+ (capacity_name, capacity_id) = _resolve_capacity_name_and_id(capacity=capacity_id)
145
+
146
+ new_data = {
147
+ "Status": v.get("status"),
148
+ "Activity Id": v.get("activityId"),
149
+ "Start Time": v.get("startTime"),
150
+ "End Time": v.get("endTime"),
151
+ "Capacity Id": capacity_id,
152
+ "Capacity Name": capacity_name,
153
+ }
154
+
155
+ df = pd.concat([df, pd.DataFrame([new_data])], ignore_index=True)
156
+
157
+ _update_dataframe_datatypes(dataframe=df, column_map=columns)
158
+
159
+ return df
160
+
161
+
162
+ def get_capacity_state(capacity: Optional[str | UUID] = None):
163
+ """
164
+ Gets the state of a capacity.
165
+
166
+ Service Principal Authentication is supported (see `here <https://github.com/microsoft/semantic-link-labs/blob/main/notebooks/Service%20Principal.ipynb>`_ for examples).
167
+
168
+ Parameters
169
+ ----------
170
+ capacity : str | uuid.UUID, default=None
171
+ The capacity name or ID.
172
+ Defaults to None which resolves to the capacity of the attached lakehouse
173
+ or if no lakehouse is attached, resolves to the workspace of the notebook.
174
+
175
+ Returns
176
+ -------
177
+ str
178
+ The capacity state.
179
+ """
180
+
181
+ df = list_capacities()
182
+
183
+ if capacity is None:
184
+ capacity = get_capacity_id()
185
+ if _is_valid_uuid(capacity):
186
+ df_filt = df[df["Capacity Id"] == capacity]
187
+ else:
188
+ df_filt = df[df["Capacity Name"] == capacity]
189
+
190
+ if df_filt.empty:
191
+ raise ValueError(f"{icons.red_dot} The capacity '{capacity}' was not found.")
192
+
193
+ return df_filt["State"].iloc[0]
194
+
195
+
196
+ @log
197
+ def list_capacities(
198
+ capacity: Optional[str | UUID] = None,
199
+ ) -> pd.DataFrame:
200
+ """
201
+ Shows the a list of capacities and their properties.
202
+
203
+ This is a wrapper function for the following API: `Admin - Get Capacities As Admin <https://learn.microsoft.com/rest/api/power-bi/admin/get-capacities-as-admin>`_.
204
+
205
+ Service Principal Authentication is supported (see `here <https://github.com/microsoft/semantic-link-labs/blob/main/notebooks/Service%20Principal.ipynb>`_ for examples).
206
+
207
+ Parameters
208
+ ----------
209
+ capacity : str | uuid.UUID, default=None
210
+ The capacity name or ID to filter. If None, all capacities are returned.
211
+
212
+ Returns
213
+ -------
214
+ pandas.DataFrame
215
+ A pandas dataframe showing the capacities and their properties.
216
+ """
217
+
218
+ columns = {
219
+ "Capacity Id": "string",
220
+ "Capacity Name": "string",
221
+ "Sku": "string",
222
+ "Region": "string",
223
+ "State": "string",
224
+ "Admins": "string",
225
+ }
226
+ df = _create_dataframe(columns=columns)
227
+
228
+ responses = _base_api(
229
+ request="/v1.0/myorg/admin/capacities", client="fabric_sp", uses_pagination=True
230
+ )
231
+
232
+ for r in responses:
233
+ for i in r.get("value", []):
234
+ new_data = {
235
+ "Capacity Id": i.get("id").lower(),
236
+ "Capacity Name": i.get("displayName"),
237
+ "Sku": i.get("sku"),
238
+ "Region": i.get("region"),
239
+ "State": i.get("state"),
240
+ "Admins": [i.get("admins", [])],
241
+ }
242
+ df = pd.concat([df, pd.DataFrame(new_data, index=[0])], ignore_index=True)
243
+
244
+ if capacity is not None:
245
+ if _is_valid_uuid(capacity):
246
+ df = df[df["Capacity Id"] == capacity.lower()]
247
+ else:
248
+ df = df[df["Capacity Name"] == capacity]
249
+
250
+ return df
@@ -0,0 +1,184 @@
1
+ import pandas as pd
2
+ from typing import Optional
3
+ from sempy_labs._helper_functions import (
4
+ _build_url,
5
+ _base_api,
6
+ _create_dataframe,
7
+ _update_dataframe_datatypes,
8
+ _is_valid_uuid,
9
+ )
10
+ from uuid import UUID
11
+ import sempy_labs._icons as icons
12
+
13
+
14
+ def list_datasets(
15
+ top: Optional[int] = None,
16
+ filter: Optional[str] = None,
17
+ skip: Optional[int] = None,
18
+ ) -> pd.DataFrame:
19
+ """
20
+ Shows a list of datasets for the organization.
21
+
22
+ This is a wrapper function for the following API: `Admin - Datasets GetDatasetsAsAdmin <https://learn.microsoft.com/rest/api/power-bi/admin/datasets-get-datasets-as-admin>`_.
23
+
24
+ Service Principal Authentication is supported (see `here <https://github.com/microsoft/semantic-link-labs/blob/main/notebooks/Service%20Principal.ipynb>`_ for examples).
25
+
26
+ Parameters
27
+ ----------
28
+ top : int, default=None
29
+ Returns only the first n results.
30
+ filter : str, default=None
31
+ Returns a subset of a results based on Odata filter query parameter condition.
32
+ skip : int, default=None
33
+ Skips the first n results.
34
+
35
+ Returns
36
+ -------
37
+ pandas.DataFrame
38
+ A pandas dataframe showing a list of datasets for the organization.
39
+ """
40
+
41
+ columns = {
42
+ "Dataset Id": "string",
43
+ "Dataset Name": "string",
44
+ "Web URL": "string",
45
+ "Add Rows API Enabled": "bool",
46
+ "Configured By": "string",
47
+ "Is Refreshable": "bool",
48
+ "Is Effective Identity Required": "bool",
49
+ "Is Effective Identity Roles Required": "bool",
50
+ "Target Storage Mode": "string",
51
+ "Created Date": "datetime",
52
+ "Content Provider Type": "string",
53
+ "Create Report Embed URL": "string",
54
+ "QnA Embed URL": "string",
55
+ "Upstream Datasets": "string",
56
+ "Users": "string",
57
+ "Is In Place Sharing Enabled": "bool",
58
+ "Workspace Id": "string",
59
+ "Auto Sync Read Only Replicas": "bool",
60
+ "Max Read Only Replicas": "int",
61
+ }
62
+
63
+ df = _create_dataframe(columns=columns)
64
+
65
+ params = {}
66
+ url = "/v1.0/myorg/admin/datasets"
67
+
68
+ if top is not None:
69
+ params["$top"] = top
70
+
71
+ if filter is not None:
72
+ params["$filter"] = filter
73
+
74
+ if skip is not None:
75
+ params["$skip"] = skip
76
+
77
+ url = _build_url(url, params)
78
+ response = _base_api(request=url, client="fabric_sp")
79
+
80
+ rows = []
81
+ for v in response.json().get("value", []):
82
+ rows.append(
83
+ {
84
+ "Dataset Id": v.get("id"),
85
+ "Dataset Name": v.get("name"),
86
+ "Web URL": v.get("webUrl"),
87
+ "Add Rows API Enabled": v.get("addRowsAPIEnabled"),
88
+ "Configured By": v.get("configuredBy"),
89
+ "Is Refreshable": v.get("isRefreshable"),
90
+ "Is Effective Identity Required": v.get("isEffectiveIdentityRequired"),
91
+ "Is Effective Identity Roles Required": v.get(
92
+ "isEffectiveIdentityRolesRequired"
93
+ ),
94
+ "Target Storage Mode": v.get("targetStorageMode"),
95
+ "Created Date": pd.to_datetime(v.get("createdDate")),
96
+ "Content Provider Type": v.get("contentProviderType"),
97
+ "Create Report Embed URL": v.get("createReportEmbedURL"),
98
+ "QnA Embed URL": v.get("qnaEmbedURL"),
99
+ "Upstream Datasets": v.get("upstreamDatasets", []),
100
+ "Users": v.get("users", []),
101
+ "Is In Place Sharing Enabled": v.get("isInPlaceSharingEnabled"),
102
+ "Workspace Id": v.get("workspaceId"),
103
+ "Auto Sync Read Only Replicas": v.get("queryScaleOutSettings", {}).get(
104
+ "autoSyncReadOnlyReplicas"
105
+ ),
106
+ "Max Read Only Replicas": v.get("queryScaleOutSettings", {}).get(
107
+ "maxReadOnlyReplicas"
108
+ ),
109
+ }
110
+ )
111
+
112
+ if rows:
113
+ df = pd.DataFrame(rows, columns=list(columns.keys()))
114
+
115
+ _update_dataframe_datatypes(dataframe=df, column_map=columns)
116
+
117
+ return df
118
+
119
+
120
+ def _resolve_dataset_id(dataset: str | UUID) -> str:
121
+ if _is_valid_uuid(dataset):
122
+ return dataset
123
+ else:
124
+ df = list_datasets()
125
+ df_filt = df[df["Dataset Name"] == dataset]
126
+ if df_filt.empty:
127
+ raise ValueError(f"{icons.red_dot} The '{dataset}' dataset does not exist.")
128
+ return df_filt["Dataset Id"].iloc[0]
129
+
130
+
131
+ def list_dataset_users(dataset: str | UUID) -> pd.DataFrame:
132
+ """
133
+ Shows a list of users that have access to the specified dataset.
134
+
135
+ This is a wrapper function for the following API: `Admin - Datasets GetDatasetUsersAsAdmin <https://learn.microsoft.com/rest/api/power-bi/admin/datasets-get-dataset-users-as-admin>`_.
136
+
137
+ Service Principal Authentication is supported (see `here <https://github.com/microsoft/semantic-link-labs/blob/main/notebooks/Service%20Principal.ipynb>`_ for examples).
138
+
139
+ Parameters
140
+ ----------
141
+ dataset : str | uuid.UUID
142
+ The name or ID of the dataset.
143
+
144
+ Returns
145
+ -------
146
+ pandas.DataFrame
147
+ A pandas dataframe showing a list of users that have access to the specified dataset.
148
+ """
149
+
150
+ dataset_id = _resolve_dataset_id(dataset)
151
+
152
+ columns = {
153
+ "User Name": "string",
154
+ "Email Address": "string",
155
+ "Dataset User Access Right": "string",
156
+ "Identifier": "string",
157
+ "Graph Id": "string",
158
+ "Principal Type": "string",
159
+ }
160
+
161
+ df = _create_dataframe(columns=columns)
162
+
163
+ url = f"/v1.0/myorg/admin/datasets/{dataset_id}/users"
164
+ response = _base_api(request=url, client="fabric_sp")
165
+
166
+ rows = []
167
+ for v in response.json().get("value", []):
168
+ rows.append(
169
+ {
170
+ "User Name": v.get("displayName"),
171
+ "Email Address": v.get("emailAddress"),
172
+ "Dataset User Access Right": v.get("datasetUserAccessRight"),
173
+ "Identifier": v.get("identifier"),
174
+ "Graph Id": v.get("graphId"),
175
+ "Principal Type": v.get("principalType"),
176
+ }
177
+ )
178
+
179
+ if rows:
180
+ df = pd.DataFrame(rows, columns=list(columns.keys()))
181
+
182
+ _update_dataframe_datatypes(dataframe=df, column_map=columns)
183
+
184
+ return df
@@ -319,7 +319,7 @@ def assign_domain_workspaces_by_capacities(
319
319
  The capacity names.
320
320
  """
321
321
 
322
- from sempy_labs.admin import list_capacities
322
+ from sempy_labs.admin._capacities import list_capacities
323
323
 
324
324
  if "domain_name" in kwargs:
325
325
  domain = kwargs["domain_name"]
@@ -413,8 +413,6 @@ def assign_domain_workspaces(domain: str | UUID, workspace_names: str | List[str
413
413
  request=f"/v1/admin/domains/{domain_id}/assignWorkspaces",
414
414
  method="post",
415
415
  payload=payload,
416
- lro_return_status_code=True,
417
- status_codes=200,
418
416
  )
419
417
 
420
418
  print(
@@ -3,9 +3,11 @@ from typing import Optional, Tuple
3
3
  from uuid import UUID
4
4
  import sempy_labs._icons as icons
5
5
  from sempy_labs.admin._basic_functions import (
6
- _resolve_capacity_name_and_id,
7
6
  _resolve_workspace_name_and_id,
8
7
  )
8
+ from sempy_labs.admin._capacities import (
9
+ _resolve_capacity_name_and_id,
10
+ )
9
11
  from sempy_labs._helper_functions import (
10
12
  _is_valid_uuid,
11
13
  _build_url,
@@ -0,0 +1,165 @@
1
+ import pandas as pd
2
+ from typing import Optional
3
+ from sempy_labs._helper_functions import (
4
+ _base_api,
5
+ _create_dataframe,
6
+ _update_dataframe_datatypes,
7
+ _is_valid_uuid,
8
+ )
9
+ from uuid import UUID
10
+ import sempy_labs._icons as icons
11
+
12
+
13
+ def list_reports(
14
+ top: Optional[int] = None,
15
+ skip: Optional[int] = None,
16
+ filter: Optional[str] = None,
17
+ ) -> pd.DataFrame:
18
+ """
19
+ Shows a list of reports for the organization.
20
+
21
+ This is a wrapper function for the following API: `Admin - Reports GetReportsAsAdmin <https://learn.microsoft.com/rest/api/power-bi/admin/reports-get-reports-as-admin>`_.
22
+
23
+ Service Principal Authentication is supported (see `here <https://github.com/microsoft/semantic-link-labs/blob/main/notebooks/Service%20Principal.ipynb>`_ for examples).
24
+
25
+ Parameters
26
+ ----------
27
+ top : int, default=None
28
+ Returns only the first n results.
29
+ skip : int, default=None
30
+ Skips the first n results.
31
+ filter : str, default=None
32
+ Returns a subset of a results based on Odata filter query parameter condition.
33
+
34
+ Returns
35
+ -------
36
+ pandas.DataFrame
37
+ A pandas dataframe showing a list of reports for the organization.
38
+ """
39
+
40
+ columns = {
41
+ "Report Id": "string",
42
+ "Report Name": "string",
43
+ "Type": "string",
44
+ "Web URL": "string",
45
+ "Embed URL": "string",
46
+ "Dataset Id": "string",
47
+ "Created Date": "datetime_coerce",
48
+ "Modified Date": "datetime_coerce",
49
+ "Created By": "string",
50
+ "Modified By": "string",
51
+ "Sensitivity Label Id": "string",
52
+ "Users": "string",
53
+ "Subscriptions": "string",
54
+ "Workspace Id": "string",
55
+ "Report Flags": "int",
56
+ }
57
+
58
+ df = _create_dataframe(columns=columns)
59
+
60
+ url = "/v1.0/myorg/admin/reports?"
61
+ if top is not None:
62
+ url += f"$top={top}&"
63
+ if skip is not None:
64
+ url += f"$skip={skip}&"
65
+ if filter is not None:
66
+ url += f"$filter={filter}&"
67
+
68
+ url.rstrip("$").rstrip("?")
69
+ response = _base_api(request=url, client="fabric_sp")
70
+ rows = []
71
+
72
+ for v in response.json().get("value", []):
73
+ rows.append(
74
+ {
75
+ "Report Id": v.get("id"),
76
+ "Report Name": v.get("name"),
77
+ "Type": v.get("reportType"),
78
+ "Web URL": v.get("webUrl"),
79
+ "Embed URL": v.get("embedUrl"),
80
+ "Dataset Id": v.get("datasetId"),
81
+ "Created Date": v.get("createdDateTime"),
82
+ "Modified Date": v.get("modifiedDateTime"),
83
+ "Created By": v.get("createdBy"),
84
+ "Modified By": v.get("modifiedBy"),
85
+ "Sensitivity Label Id": v.get("sensitivityLabel", {}).get("labelId"),
86
+ "Users": v.get("users"),
87
+ "Subscriptions": v.get("subscriptions"),
88
+ "Workspace Id": v.get("workspaceId"),
89
+ "Report Flags": v.get("reportFlags"),
90
+ }
91
+ )
92
+
93
+ if rows:
94
+ df = pd.DataFrame(rows, columns=list(columns.keys()))
95
+
96
+ _update_dataframe_datatypes(dataframe=df, column_map=columns)
97
+
98
+ return df
99
+
100
+
101
+ def _resolve_report_id(report: str | UUID) -> str:
102
+ if _is_valid_uuid(report):
103
+ return report
104
+ else:
105
+ df = list_reports()
106
+ df_filt = df[df["Report Name"] == report]
107
+ if df_filt.empty:
108
+ raise ValueError(f"{icons.red_dot} The '{report}' report does not exist.")
109
+ return df_filt["Report Id"].iloc[0]
110
+
111
+
112
+ def list_report_users(report: str | UUID) -> pd.DataFrame:
113
+ """
114
+ Shows a list of users that have access to the specified report.
115
+
116
+ This is a wrapper function for the following API: `Admin - Reports GetDatasetUsersAsAdmin <https://learn.microsoft.com/rest/api/power-bi/admin/datasets-get-report-users-as-admin>`_.
117
+
118
+ Service Principal Authentication is supported (see `here <https://github.com/microsoft/semantic-link-labs/blob/main/notebooks/Service%20Principal.ipynb>`_ for examples).
119
+
120
+ Parameters
121
+ ----------
122
+ report : str | uuid.UUID
123
+ The name or ID of the report.
124
+
125
+ Returns
126
+ -------
127
+ pandas.DataFrame
128
+ A pandas dataframe showing a list of users that have access to the specified report.
129
+ """
130
+
131
+ report_id = _resolve_report_id(report)
132
+
133
+ columns = {
134
+ "User Name": "string",
135
+ "Email Address": "string",
136
+ "Report User Access Right": "string",
137
+ "Identifier": "string",
138
+ "Graph Id": "string",
139
+ "Principal Type": "string",
140
+ }
141
+
142
+ df = _create_dataframe(columns=columns)
143
+
144
+ url = f"/v1.0/myorg/admin/reports/{report_id}/users"
145
+ response = _base_api(request=url, client="fabric_sp")
146
+
147
+ rows = []
148
+ for v in response.json().get("value", []):
149
+ rows.append(
150
+ {
151
+ "User Name": v.get("displayName"),
152
+ "Email Address": v.get("emailAddress"),
153
+ "Report User Access Right": v.get("reportUserAccessRight"),
154
+ "Identifier": v.get("identifier"),
155
+ "Graph Id": v.get("graphId"),
156
+ "Principal Type": v.get("principalType"),
157
+ }
158
+ )
159
+
160
+ if rows:
161
+ df = pd.DataFrame(rows, columns=list(columns.keys()))
162
+
163
+ _update_dataframe_datatypes(dataframe=df, column_map=columns)
164
+
165
+ return df