semantic-link-labs 0.8.6__py3-none-any.whl → 0.8.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.
Potentially problematic release.
This version of semantic-link-labs might be problematic. Click here for more details.
- {semantic_link_labs-0.8.6.dist-info → semantic_link_labs-0.8.8.dist-info}/METADATA +15 -6
- {semantic_link_labs-0.8.6.dist-info → semantic_link_labs-0.8.8.dist-info}/RECORD +35 -29
- {semantic_link_labs-0.8.6.dist-info → semantic_link_labs-0.8.8.dist-info}/WHEEL +1 -1
- sempy_labs/__init__.py +37 -6
- sempy_labs/_authentication.py +108 -0
- sempy_labs/_connections.py +356 -177
- sempy_labs/_dataflows.py +0 -1
- sempy_labs/_gateways.py +439 -0
- sempy_labs/_generate_semantic_model.py +51 -30
- sempy_labs/_git.py +13 -5
- sempy_labs/_helper_functions.py +14 -3
- sempy_labs/_list_functions.py +1 -1
- sempy_labs/_model_auto_build.py +4 -2
- sempy_labs/_model_bpa.py +2 -15
- sempy_labs/_model_bpa_bulk.py +4 -2
- sempy_labs/_model_dependencies.py +2 -1
- sempy_labs/_refresh_semantic_model.py +6 -0
- sempy_labs/admin/__init__.py +19 -9
- sempy_labs/admin/_basic_functions.py +475 -548
- sempy_labs/admin/_external_data_share.py +97 -0
- sempy_labs/admin/_git.py +69 -0
- sempy_labs/admin/_items.py +264 -0
- sempy_labs/admin/_scanner.py +104 -0
- sempy_labs/directlake/_dl_helper.py +6 -2
- sempy_labs/directlake/_get_shared_expression.py +5 -35
- sempy_labs/directlake/_update_directlake_model_lakehouse_connection.py +3 -2
- sempy_labs/migration/_migrate_tables_columns_to_semantic_model.py +4 -2
- sempy_labs/report/_generate_report.py +10 -4
- sempy_labs/report/_report_bpa.py +1 -0
- sempy_labs/report/_report_helper.py +58 -0
- sempy_labs/report/_report_list_functions.py +2 -0
- sempy_labs/report/_reportwrapper.py +358 -175
- sempy_labs/tom/_model.py +1 -0
- {semantic_link_labs-0.8.6.dist-info → semantic_link_labs-0.8.8.dist-info}/LICENSE +0 -0
- {semantic_link_labs-0.8.6.dist-info → semantic_link_labs-0.8.8.dist-info}/top_level.txt +0 -0
|
@@ -4,178 +4,150 @@ from uuid import UUID
|
|
|
4
4
|
import sempy_labs._icons as icons
|
|
5
5
|
from sempy.fabric.exceptions import FabricHTTPException
|
|
6
6
|
from sempy_labs._helper_functions import (
|
|
7
|
-
resolve_workspace_name_and_id,
|
|
8
7
|
pagination,
|
|
8
|
+
_is_valid_uuid,
|
|
9
|
+
_build_url,
|
|
9
10
|
)
|
|
10
11
|
import numpy as np
|
|
11
12
|
import pandas as pd
|
|
12
|
-
import
|
|
13
|
+
from dateutil.parser import parse as dtparser
|
|
13
14
|
import urllib.parse
|
|
14
|
-
from datetime import datetime
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
def list_workspaces(
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
capacity: Optional[str | UUID] = None,
|
|
19
|
+
workspace: Optional[str | UUID] = None,
|
|
20
|
+
workspace_state: Optional[str] = None,
|
|
21
|
+
workspace_type: Optional[str] = None,
|
|
22
|
+
**kwargs,
|
|
21
23
|
) -> pd.DataFrame:
|
|
22
24
|
"""
|
|
23
25
|
Lists workspaces for the organization. This function is the admin version of list_workspaces.
|
|
24
26
|
|
|
25
|
-
This is a wrapper function for the following API: `
|
|
27
|
+
This is a wrapper function for the following API: `Workspaces - List Workspaces - REST API (Admin) <https://learn.microsoft.com/en-us/rest/api/fabric/admin/workspaces/list-workspaces>`_.
|
|
26
28
|
|
|
27
29
|
Parameters
|
|
28
30
|
----------
|
|
29
|
-
|
|
30
|
-
Returns only the
|
|
31
|
-
|
|
32
|
-
Returns
|
|
33
|
-
|
|
34
|
-
|
|
31
|
+
capacity : str | UUID, default=None
|
|
32
|
+
Returns only the workspaces in the specified Capacity.
|
|
33
|
+
workspace : str | UUID, default=None
|
|
34
|
+
Returns the workspace with the specific name.
|
|
35
|
+
workspace_state : str, default=None
|
|
36
|
+
Return only the workspace with the requested state. You can find the possible states in `Workspace States <https://learn.microsoft.com/en-us/rest/api/fabric/admin/workspaces/list-workspaces?tabs=HTTP#workspacestate>`_.
|
|
37
|
+
workspace_type : str, default=None
|
|
38
|
+
Return only the workspace of the specific type. You can find the possible types in `Workspace Types <https://learn.microsoft.com/en-us/rest/api/fabric/admin/workspaces/list-workspaces?tabs=HTTP#workspacetype>`_.
|
|
35
39
|
|
|
36
40
|
Returns
|
|
37
41
|
-------
|
|
38
42
|
pandas.DataFrame
|
|
39
43
|
A pandas dataframe showing a list of workspaces for the organization.
|
|
40
44
|
"""
|
|
45
|
+
if "filter" in kwargs:
|
|
46
|
+
print(
|
|
47
|
+
"The 'filter' parameter has been deprecated. Please remove this parameter from the function going forward."
|
|
48
|
+
)
|
|
49
|
+
del kwargs["filter"]
|
|
50
|
+
|
|
51
|
+
if "top" in kwargs:
|
|
52
|
+
print(
|
|
53
|
+
"The 'top' parameter has been deprecated. Please remove this parameter from the function going forward."
|
|
54
|
+
)
|
|
55
|
+
del kwargs["top"]
|
|
56
|
+
|
|
57
|
+
if "skip" in kwargs:
|
|
58
|
+
print(
|
|
59
|
+
"The 'skip' parameter has been deprecated. Please remove this parameter from the function going forward."
|
|
60
|
+
)
|
|
61
|
+
del kwargs["skip"]
|
|
62
|
+
|
|
63
|
+
client = fabric.FabricRestClient()
|
|
41
64
|
|
|
42
65
|
df = pd.DataFrame(
|
|
43
66
|
columns=[
|
|
44
67
|
"Id",
|
|
45
|
-
"Is Read Only",
|
|
46
|
-
"Is On Dedicated Capacity",
|
|
47
|
-
"Type",
|
|
48
68
|
"Name",
|
|
69
|
+
"State",
|
|
70
|
+
"Type",
|
|
49
71
|
"Capacity Id",
|
|
50
|
-
"Default Dataset Storage Format",
|
|
51
|
-
"Pipeline Id",
|
|
52
|
-
"Has Workspace Level Settings",
|
|
53
72
|
]
|
|
54
73
|
)
|
|
55
74
|
|
|
56
|
-
url =
|
|
57
|
-
|
|
58
|
-
url = f"{url}&$skip={skip}"
|
|
59
|
-
if filter is not None:
|
|
60
|
-
url = f"{url}&$filter={filter}"
|
|
75
|
+
url = "/v1/admin/workspaces"
|
|
76
|
+
params = {}
|
|
61
77
|
|
|
62
|
-
|
|
63
|
-
|
|
78
|
+
if capacity is not None:
|
|
79
|
+
params["capacityId"] = _resolve_capacity_name_and_id(capacity)[1]
|
|
64
80
|
|
|
65
|
-
if
|
|
66
|
-
|
|
81
|
+
if workspace is not None and not _is_valid_uuid(workspace):
|
|
82
|
+
params["name"] = workspace
|
|
67
83
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
if capacity_id:
|
|
71
|
-
capacity_id = capacity_id.lower()
|
|
72
|
-
new_data = {
|
|
73
|
-
"Id": v.get("id"),
|
|
74
|
-
"Is Read Only": v.get("isReadOnly"),
|
|
75
|
-
"Is On Dedicated Capacity": v.get("isOnDedicatedCapacity"),
|
|
76
|
-
"Capacity Id": capacity_id,
|
|
77
|
-
"Default Dataset Storage Format": v.get("defaultDatasetStorageFormat"),
|
|
78
|
-
"Type": v.get("type"),
|
|
79
|
-
"Name": v.get("name"),
|
|
80
|
-
"State": v.get("state"),
|
|
81
|
-
"Pipeline Id": v.get("pipelineId"),
|
|
82
|
-
"Has Workspace Level Settings": v.get("hasWorkspaceLevelSettings"),
|
|
83
|
-
}
|
|
84
|
-
df = pd.concat([df, pd.DataFrame(new_data, index=[0])], ignore_index=True)
|
|
84
|
+
if workspace_state is not None:
|
|
85
|
+
params["state"] = workspace_state
|
|
85
86
|
|
|
86
|
-
|
|
87
|
-
"
|
|
88
|
-
"Is On Dedicated Capacity",
|
|
89
|
-
"Has Workspace Level Settings",
|
|
90
|
-
]
|
|
91
|
-
df[bool_cols] = df[bool_cols].astype(bool)
|
|
87
|
+
if workspace_type is not None:
|
|
88
|
+
params["type"] = workspace_type
|
|
92
89
|
|
|
93
|
-
|
|
90
|
+
url = _build_url(url, params)
|
|
94
91
|
|
|
92
|
+
response = client.get(path_or_url=url)
|
|
95
93
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
target_capacity: str,
|
|
99
|
-
workspace: Optional[str | List[str]] = None,
|
|
100
|
-
):
|
|
101
|
-
"""
|
|
102
|
-
Assigns a workspace to a capacity. This function is the admin version.
|
|
94
|
+
if response.status_code != 200:
|
|
95
|
+
raise FabricHTTPException(response)
|
|
103
96
|
|
|
104
|
-
|
|
97
|
+
responsePaginated = pagination(client, response)
|
|
105
98
|
|
|
106
|
-
|
|
107
|
-
----------
|
|
108
|
-
source_capacity : str
|
|
109
|
-
The name of the source capacity.
|
|
110
|
-
target_capacity : str
|
|
111
|
-
The name of the target capacity.
|
|
112
|
-
workspace : str | List[str], default=None
|
|
113
|
-
The name of the workspace(s).
|
|
114
|
-
Defaults to None which resolves to migrating all workspaces within the source capacity to the target capacity.
|
|
115
|
-
"""
|
|
99
|
+
workspaces = []
|
|
116
100
|
|
|
117
|
-
|
|
118
|
-
|
|
101
|
+
for r in responsePaginated:
|
|
102
|
+
workspaces = workspaces + r.get("workspaces", [])
|
|
119
103
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
104
|
+
if len(workspaces) > 0:
|
|
105
|
+
df = pd.DataFrame(workspaces)
|
|
106
|
+
df.rename(
|
|
107
|
+
columns={
|
|
108
|
+
"id": "Id",
|
|
109
|
+
"name": "Name",
|
|
110
|
+
"state": "State",
|
|
111
|
+
"type": "Type",
|
|
112
|
+
"capacityId": "Capacity Id",
|
|
113
|
+
},
|
|
114
|
+
inplace=True,
|
|
115
|
+
)
|
|
123
116
|
|
|
124
|
-
|
|
125
|
-
target_capacity_id = dfC_filt["Capacity Id"].iloc[0]
|
|
117
|
+
df["Capacity Id"] = df["Capacity Id"].str.lower()
|
|
126
118
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
dfW = dfW[dfW["Capacity Id"].str.upper() == source_capacity_id.upper()]
|
|
130
|
-
workspaces = dfW["Id"].tolist()
|
|
131
|
-
else:
|
|
132
|
-
dfW = list_workspaces()
|
|
133
|
-
workspaces = dfW[dfW["Name"].isin(workspace)]["Id"].tolist()
|
|
134
|
-
|
|
135
|
-
workspaces = np.array(workspaces)
|
|
136
|
-
batch_size = 999
|
|
137
|
-
for i in range(0, len(workspaces), batch_size):
|
|
138
|
-
batch = workspaces[i : i + batch_size].tolist()
|
|
139
|
-
request_body = {
|
|
140
|
-
"capacityMigrationAssignments": [
|
|
141
|
-
{
|
|
142
|
-
"targetCapacityObjectId": target_capacity_id.upper(),
|
|
143
|
-
"workspacesToAssign": batch,
|
|
144
|
-
}
|
|
145
|
-
]
|
|
146
|
-
}
|
|
119
|
+
if workspace is not None and _is_valid_uuid(workspace):
|
|
120
|
+
df = df[df["Id"] == workspace.lower()]
|
|
147
121
|
|
|
148
|
-
|
|
149
|
-
response = client.post(
|
|
150
|
-
"/v1.0/myorg/admin/capacities/AssignWorkspaces",
|
|
151
|
-
json=request_body,
|
|
152
|
-
)
|
|
153
|
-
|
|
154
|
-
if response.status_code != 200:
|
|
155
|
-
raise FabricHTTPException(response)
|
|
156
|
-
print(
|
|
157
|
-
f"{icons.green_dot} The workspaces have been assigned to the '{target_capacity}' capacity."
|
|
158
|
-
)
|
|
122
|
+
return df
|
|
159
123
|
|
|
160
124
|
|
|
161
|
-
def list_capacities(
|
|
125
|
+
def list_capacities(
|
|
126
|
+
capacity: Optional[str | UUID] = None,
|
|
127
|
+
) -> pd.DataFrame:
|
|
162
128
|
"""
|
|
163
129
|
Shows the a list of capacities and their properties. This function is the admin version.
|
|
164
130
|
|
|
165
131
|
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>`_.
|
|
166
132
|
|
|
133
|
+
Parameters
|
|
134
|
+
----------
|
|
135
|
+
capacity : str | UUID, default=None
|
|
136
|
+
Capacity name or id to filter.
|
|
137
|
+
|
|
167
138
|
Returns
|
|
168
139
|
-------
|
|
169
140
|
pandas.DataFrame
|
|
170
141
|
A pandas dataframe showing the capacities and their properties
|
|
171
142
|
"""
|
|
143
|
+
client = fabric.FabricRestClient()
|
|
172
144
|
|
|
173
145
|
df = pd.DataFrame(
|
|
174
146
|
columns=["Capacity Id", "Capacity Name", "Sku", "Region", "State", "Admins"]
|
|
175
147
|
)
|
|
176
148
|
|
|
177
|
-
client = fabric.PowerBIRestClient()
|
|
178
149
|
response = client.get("/v1.0/myorg/admin/capacities")
|
|
150
|
+
|
|
179
151
|
if response.status_code != 200:
|
|
180
152
|
raise FabricHTTPException(response)
|
|
181
153
|
|
|
@@ -193,85 +165,108 @@ def list_capacities() -> pd.DataFrame:
|
|
|
193
165
|
}
|
|
194
166
|
df = pd.concat([df, pd.DataFrame(new_data, index=[0])], ignore_index=True)
|
|
195
167
|
|
|
168
|
+
if capacity is not None:
|
|
169
|
+
if _is_valid_uuid(capacity):
|
|
170
|
+
df = df[df["Capacity Id"] == capacity.lower()]
|
|
171
|
+
else:
|
|
172
|
+
df = df[df["Capacity Name"] == capacity]
|
|
173
|
+
|
|
196
174
|
return df
|
|
197
175
|
|
|
198
176
|
|
|
199
|
-
def
|
|
177
|
+
def assign_workspaces_to_capacity(
|
|
178
|
+
source_capacity: Optional[str | UUID] = None,
|
|
179
|
+
target_capacity: Optional[str | UUID] = None,
|
|
180
|
+
workspace: Optional[str | List[str] | UUID | List[UUID]] = None,
|
|
181
|
+
):
|
|
200
182
|
"""
|
|
201
|
-
|
|
183
|
+
Assigns a workspace to a capacity. This function is the admin version.
|
|
202
184
|
|
|
203
|
-
This is a wrapper function for the following API: `
|
|
185
|
+
This is a wrapper function for the following API: `Admin - Capacities AssignWorkspacesToCapacity <https://learn.microsoft.com/rest/api/power-bi/admin/capacities-assign-workspaces-to-capacity>`_.
|
|
204
186
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
187
|
+
Parameters
|
|
188
|
+
----------
|
|
189
|
+
source_capacity : str | UUID, default=None
|
|
190
|
+
The name of the source capacity. If the Workspace is not specified, this is parameter mandatory.
|
|
191
|
+
target_capacity : str | UUID, default=None
|
|
192
|
+
The name of the target capacity.
|
|
193
|
+
workspace : str | List[str] | UUID | List[UUID], default=None
|
|
194
|
+
The name or id of the workspace(s).
|
|
195
|
+
Defaults to None which resolves to migrating all workspaces within the source capacity to the target capacity.
|
|
209
196
|
"""
|
|
197
|
+
if target_capacity is None:
|
|
198
|
+
raise ValueError(
|
|
199
|
+
f"{icons.red_dot} The parameter 'target_capacity' is mandatory."
|
|
200
|
+
)
|
|
210
201
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
raise FabricHTTPException(response)
|
|
202
|
+
if source_capacity is None and workspace is None:
|
|
203
|
+
raise ValueError(
|
|
204
|
+
f"{icons.red_dot} The parameters 'source_capacity' or 'workspace' needs to be specified."
|
|
205
|
+
)
|
|
216
206
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
207
|
+
if workspace is None:
|
|
208
|
+
source_capacity_id = _resolve_capacity_name_and_id(source_capacity)[1]
|
|
209
|
+
dfW = list_workspaces(capacity=source_capacity_id)
|
|
210
|
+
workspaces = dfW["Id"].tolist()
|
|
211
|
+
else:
|
|
212
|
+
if isinstance(workspace, str) or isinstance(workspace, UUID):
|
|
213
|
+
workspace = [workspace]
|
|
214
|
+
if source_capacity is None:
|
|
215
|
+
dfW = list_workspaces()
|
|
216
|
+
else:
|
|
217
|
+
dfW = list_workspaces(capacity=source_capacity_id)
|
|
218
|
+
|
|
219
|
+
# Extract names and IDs that are mapped in dfW
|
|
220
|
+
workspaces_names = dfW[dfW["Name"].isin(workspace)]["Name"].tolist()
|
|
221
|
+
workspaces_ids = dfW[dfW["Id"].isin(workspace)]["Id"].tolist()
|
|
222
|
+
|
|
223
|
+
# Combine IDs into the final workspaces list
|
|
224
|
+
workspaces = workspaces_ids + dfW[dfW["Name"].isin(workspace)]["Id"].tolist()
|
|
225
|
+
|
|
226
|
+
# Identify unmapped workspaces
|
|
227
|
+
unmapped_workspaces = [
|
|
228
|
+
item
|
|
229
|
+
for item in workspace
|
|
230
|
+
if item not in workspaces_names and item not in workspaces_ids
|
|
225
231
|
]
|
|
226
|
-
)
|
|
227
232
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
"
|
|
231
|
-
|
|
232
|
-
"Enabled": i.get("enabled"),
|
|
233
|
-
"Can Specify Security Groups": i.get("canSpecifySecurityGroups"),
|
|
234
|
-
"Tenant Setting Group": i.get("tenantSettingGroup"),
|
|
235
|
-
"Enabled Security Groups": [i.get("enabledSecurityGroups", [])],
|
|
236
|
-
}
|
|
237
|
-
df = pd.concat([df, pd.DataFrame(new_data, index=[0])], ignore_index=True)
|
|
238
|
-
|
|
239
|
-
bool_cols = ["Enabled", "Can Specify Security Groups"]
|
|
240
|
-
df[bool_cols] = df[bool_cols].astype(bool)
|
|
233
|
+
if len(workspace) != len(workspaces):
|
|
234
|
+
raise ValueError(
|
|
235
|
+
f"{icons.red_dot} The following workspaces are invalid: {unmapped_workspaces}."
|
|
236
|
+
)
|
|
241
237
|
|
|
242
|
-
|
|
238
|
+
target_capacity_id = _resolve_capacity_name_and_id(target_capacity)[1]
|
|
243
239
|
|
|
240
|
+
workspaces = np.array(workspaces)
|
|
241
|
+
batch_size = 999
|
|
242
|
+
for i in range(0, len(workspaces), batch_size):
|
|
243
|
+
batch = workspaces[i : i + batch_size].tolist()
|
|
244
|
+
request_body = {
|
|
245
|
+
"capacityMigrationAssignments": [
|
|
246
|
+
{
|
|
247
|
+
"targetCapacityObjectId": target_capacity_id.upper(),
|
|
248
|
+
"workspacesToAssign": batch,
|
|
249
|
+
}
|
|
250
|
+
]
|
|
251
|
+
}
|
|
244
252
|
|
|
245
|
-
|
|
253
|
+
client = fabric.FabricRestClient()
|
|
246
254
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
255
|
+
response = client.post(
|
|
256
|
+
"/v1.0/myorg/admin/capacities/AssignWorkspaces",
|
|
257
|
+
json=request_body,
|
|
258
|
+
)
|
|
250
259
|
|
|
251
|
-
|
|
252
|
-
try:
|
|
253
|
-
response = client.get("/v1.0/myorg/admin/capacities")
|
|
254
|
-
except Exception as e:
|
|
255
|
-
if e.status_code not in [200, 401]:
|
|
260
|
+
if response.status_code != 200:
|
|
256
261
|
raise FabricHTTPException(response)
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
for i in response.json().get("value", []):
|
|
261
|
-
new_data = {
|
|
262
|
-
"Capacity Id": i.get("id").lower(),
|
|
263
|
-
"Capacity Name": i.get("displayName"),
|
|
264
|
-
"Sku": i.get("sku"),
|
|
265
|
-
"Region": i.get("region"),
|
|
266
|
-
"State": i.get("state"),
|
|
267
|
-
"Admins": [i.get("admins", [])],
|
|
268
|
-
}
|
|
269
|
-
df = pd.concat([df, pd.DataFrame(new_data, index=[0])], ignore_index=True)
|
|
270
|
-
|
|
271
|
-
return df
|
|
262
|
+
print(
|
|
263
|
+
f"{icons.green_dot} The workspaces have been assigned to the '{target_capacity}' capacity. A total of {len(workspaces)} were moved."
|
|
264
|
+
)
|
|
272
265
|
|
|
273
266
|
|
|
274
|
-
def unassign_workspaces_from_capacity(
|
|
267
|
+
def unassign_workspaces_from_capacity(
|
|
268
|
+
workspaces: str | List[str] | UUID | List[UUID],
|
|
269
|
+
):
|
|
275
270
|
"""
|
|
276
271
|
Unassigns workspace(s) from their capacity.
|
|
277
272
|
|
|
@@ -279,14 +274,22 @@ def unassign_workspaces_from_capacity(workspaces: str | List[str]):
|
|
|
279
274
|
|
|
280
275
|
Parameters
|
|
281
276
|
----------
|
|
282
|
-
workspaces : str | List[str]
|
|
283
|
-
The Fabric workspace name(s).
|
|
277
|
+
workspaces : str | List[str] | UUID | List[UUID]
|
|
278
|
+
The Fabric workspace name(s) or id(s).
|
|
284
279
|
"""
|
|
285
|
-
|
|
286
280
|
if isinstance(workspaces, str):
|
|
287
281
|
workspaces = [workspaces]
|
|
288
282
|
|
|
289
|
-
|
|
283
|
+
dfW = list_workspaces()
|
|
284
|
+
workspacesIds = dfW[dfW["Name"].isin(workspaces)]["Id"].tolist()
|
|
285
|
+
workspacesIds = workspacesIds + dfW[dfW["Id"].isin(workspaces)]["Id"].tolist()
|
|
286
|
+
|
|
287
|
+
if len(workspacesIds) != len(workspaces):
|
|
288
|
+
raise ValueError(
|
|
289
|
+
f"{icons.red_dot} Some of the workspaces provided are not valid."
|
|
290
|
+
)
|
|
291
|
+
|
|
292
|
+
payload = {"workspacesToUnassign": workspacesIds}
|
|
290
293
|
|
|
291
294
|
client = fabric.PowerBIRestClient()
|
|
292
295
|
response = client.post(
|
|
@@ -297,102 +300,57 @@ def unassign_workspaces_from_capacity(workspaces: str | List[str]):
|
|
|
297
300
|
if response.status_code != 200:
|
|
298
301
|
raise FabricHTTPException(response)
|
|
299
302
|
|
|
300
|
-
print(
|
|
303
|
+
print(
|
|
304
|
+
f"{icons.green_dot} A total of {len(workspacesIds)} workspaces have been unassigned."
|
|
305
|
+
)
|
|
301
306
|
|
|
302
307
|
|
|
303
|
-
def
|
|
308
|
+
def list_tenant_settings() -> pd.DataFrame:
|
|
304
309
|
"""
|
|
305
|
-
Lists
|
|
310
|
+
Lists all tenant settings.
|
|
306
311
|
|
|
307
|
-
This is a wrapper function for the following API: `
|
|
312
|
+
This is a wrapper function for the following API: `Tenants - List Tenant Settings <https://learn.microsoft.com/rest/api/fabric/admin/tenants/list-tenant-settings>`_.
|
|
308
313
|
|
|
309
314
|
Returns
|
|
310
315
|
-------
|
|
311
316
|
pandas.DataFrame
|
|
312
|
-
A pandas dataframe showing
|
|
317
|
+
A pandas dataframe showing the tenant settings.
|
|
313
318
|
"""
|
|
319
|
+
client = fabric.FabricRestClient()
|
|
320
|
+
|
|
321
|
+
response = client.get("/v1/admin/tenantsettings")
|
|
322
|
+
|
|
323
|
+
if response.status_code != 200:
|
|
324
|
+
raise FabricHTTPException(response)
|
|
314
325
|
|
|
315
326
|
df = pd.DataFrame(
|
|
316
327
|
columns=[
|
|
317
|
-
"
|
|
318
|
-
"
|
|
319
|
-
"
|
|
320
|
-
"
|
|
321
|
-
"
|
|
322
|
-
"
|
|
323
|
-
"Recipient UPN",
|
|
324
|
-
"Status",
|
|
325
|
-
"Expiration Time UTC",
|
|
326
|
-
"Workspace Id",
|
|
327
|
-
"Item Id",
|
|
328
|
-
"Invitation URL",
|
|
328
|
+
"Setting Name",
|
|
329
|
+
"Title",
|
|
330
|
+
"Enabled",
|
|
331
|
+
"Can Specify Security Groups",
|
|
332
|
+
"Tenant Setting Group",
|
|
333
|
+
"Enabled Security Groups",
|
|
329
334
|
]
|
|
330
335
|
)
|
|
331
336
|
|
|
332
|
-
|
|
333
|
-
response = client.get("/v1/admin/items/externalDataShares")
|
|
334
|
-
|
|
335
|
-
if response.status_code != 200:
|
|
336
|
-
raise FabricHTTPException(response)
|
|
337
|
-
|
|
338
|
-
for i in response.json().get("value", []):
|
|
339
|
-
cp = i.get("creatorPrincipal", {})
|
|
337
|
+
for i in response.json().get("tenantSettings", []):
|
|
340
338
|
new_data = {
|
|
341
|
-
"
|
|
342
|
-
"
|
|
343
|
-
"
|
|
344
|
-
"
|
|
345
|
-
"
|
|
346
|
-
"
|
|
347
|
-
"Recipient UPN": i.get("recipient", {}).get("userPrincipalName"),
|
|
348
|
-
"Status": i.get("status"),
|
|
349
|
-
"Expiration Time UTC": i.get("expirationTimeUtc"),
|
|
350
|
-
"Workspace Id": i.get("workspaceId"),
|
|
351
|
-
"Item Id": i.get("itemId"),
|
|
352
|
-
"Invitation URL": i.get("invitationUrl"),
|
|
339
|
+
"Setting Name": i.get("settingName"),
|
|
340
|
+
"Title": i.get("title"),
|
|
341
|
+
"Enabled": i.get("enabled"),
|
|
342
|
+
"Can Specify Security Groups": i.get("canSpecifySecurityGroups"),
|
|
343
|
+
"Tenant Setting Group": i.get("tenantSettingGroup"),
|
|
344
|
+
"Enabled Security Groups": [i.get("enabledSecurityGroups", [])],
|
|
353
345
|
}
|
|
354
|
-
|
|
355
346
|
df = pd.concat([df, pd.DataFrame(new_data, index=[0])], ignore_index=True)
|
|
356
347
|
|
|
357
|
-
|
|
358
|
-
df[
|
|
348
|
+
bool_cols = ["Enabled", "Can Specify Security Groups"]
|
|
349
|
+
df[bool_cols] = df[bool_cols].astype(bool)
|
|
359
350
|
|
|
360
351
|
return df
|
|
361
352
|
|
|
362
353
|
|
|
363
|
-
def revoke_external_data_share(
|
|
364
|
-
external_data_share_id: UUID, item_id: UUID, workspace: str
|
|
365
|
-
):
|
|
366
|
-
"""
|
|
367
|
-
Revokes the specified external data share. Note: This action cannot be undone.
|
|
368
|
-
|
|
369
|
-
This is a wrapper function for the following API: `External Data Shares - Revoke External Data Share <https://learn.microsoft.com/rest/api/fabric/admin/external-data-shares/revoke-external-data-share>`_.
|
|
370
|
-
|
|
371
|
-
Parameters
|
|
372
|
-
----------
|
|
373
|
-
external_data_share_id : UUID
|
|
374
|
-
The external data share ID.
|
|
375
|
-
item_id : int, default=None
|
|
376
|
-
The Item ID
|
|
377
|
-
workspace : str
|
|
378
|
-
The Fabric workspace name.
|
|
379
|
-
"""
|
|
380
|
-
|
|
381
|
-
(workspace, workspace_id) = resolve_workspace_name_and_id(workspace)
|
|
382
|
-
|
|
383
|
-
client = fabric.FabricRestClient()
|
|
384
|
-
response = client.post(
|
|
385
|
-
f"/v1/admin/workspaces/{workspace_id}/items/{item_id}/externalDataShares/{external_data_share_id}/revoke"
|
|
386
|
-
)
|
|
387
|
-
|
|
388
|
-
if response.status_code != 200:
|
|
389
|
-
raise FabricHTTPException(response)
|
|
390
|
-
|
|
391
|
-
print(
|
|
392
|
-
f"{icons.green_dot} The '{external_data_share_id}' external data share for the '{item_id}' item within the '{workspace}' workspace has been revoked."
|
|
393
|
-
)
|
|
394
|
-
|
|
395
|
-
|
|
396
354
|
def list_capacities_delegated_tenant_settings(
|
|
397
355
|
return_dataframe: bool = True,
|
|
398
356
|
) -> pd.DataFrame | dict:
|
|
@@ -411,7 +369,6 @@ def list_capacities_delegated_tenant_settings(
|
|
|
411
369
|
pandas.DataFrame | dict
|
|
412
370
|
A pandas dataframe showing a list of tenant setting overrides that override at the capacities.
|
|
413
371
|
"""
|
|
414
|
-
|
|
415
372
|
df = pd.DataFrame(
|
|
416
373
|
columns=[
|
|
417
374
|
"Capacity Id",
|
|
@@ -476,68 +433,93 @@ def list_capacities_delegated_tenant_settings(
|
|
|
476
433
|
"continuationToken": "",
|
|
477
434
|
}
|
|
478
435
|
for r in responses:
|
|
479
|
-
combined_response["overrides"].extend(r["
|
|
436
|
+
combined_response["overrides"].extend(r["Overrides"])
|
|
480
437
|
combined_response["continuationUri"] = r["continuationUri"]
|
|
481
438
|
combined_response["continuationToken"] = r["continuationToken"]
|
|
482
439
|
|
|
483
440
|
return combined_response
|
|
484
441
|
|
|
485
442
|
|
|
486
|
-
def
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
workspace
|
|
493
|
-
) -> dict:
|
|
443
|
+
def list_modified_workspaces(
|
|
444
|
+
modified_since: Optional[str] = None,
|
|
445
|
+
exclude_inactive_workspaces: Optional[bool] = False,
|
|
446
|
+
exclude_personal_workspaces: Optional[bool] = False,
|
|
447
|
+
) -> pd.DataFrame:
|
|
448
|
+
"""
|
|
449
|
+
Gets a list of workspace IDs in the organization.
|
|
494
450
|
|
|
495
|
-
|
|
496
|
-
workspace = fabric.resolve_workspace_name()
|
|
451
|
+
This is a wrapper function for the following API: `Admin - WorkspaceInfo GetModifiedWorkspaces <https://learn.microsoft.com/rest/api/power-bi/admin/workspace-info-get-modified-workspaces>`_.
|
|
497
452
|
|
|
498
|
-
|
|
499
|
-
|
|
453
|
+
Parameters
|
|
454
|
+
----------
|
|
455
|
+
modified_since : str, default=None
|
|
456
|
+
Last modified date (must be in ISO compliant UTC format). Example: "2024-11-02T05:51:30" or "2024-11-02T05:51:30.0000000Z".
|
|
457
|
+
exclude_inactive_workspaces : bool, default=False
|
|
458
|
+
Whether to exclude inactive workspaces.
|
|
459
|
+
exclude_personal_workspaces : bool, default=False
|
|
460
|
+
Whether to exclude personal workspaces.
|
|
500
461
|
|
|
501
|
-
|
|
462
|
+
Returns
|
|
463
|
+
-------
|
|
464
|
+
pandas.DataFrame
|
|
465
|
+
A pandas dataframe showing a list of workspace IDs in the organization.
|
|
466
|
+
"""
|
|
467
|
+
client = fabric.PowerBIRestClient()
|
|
502
468
|
|
|
503
|
-
|
|
504
|
-
workspace_list.append(fabric.resolve_workspace_id(w))
|
|
469
|
+
params = {}
|
|
505
470
|
|
|
506
|
-
|
|
507
|
-
|
|
471
|
+
url = "/v1.0/myorg/admin/workspaces/modified"
|
|
472
|
+
|
|
473
|
+
if modified_since is not None:
|
|
474
|
+
modified_since_dt = dtparser(modified_since)
|
|
475
|
+
params["modifiedSince"] = (
|
|
476
|
+
f"{modified_since_dt.isoformat(timespec='microseconds')}0Z"
|
|
477
|
+
)
|
|
508
478
|
|
|
509
|
-
|
|
510
|
-
|
|
479
|
+
if exclude_inactive_workspaces is not None:
|
|
480
|
+
params["excludeInActiveWorkspaces"] = exclude_inactive_workspaces
|
|
481
|
+
|
|
482
|
+
if exclude_personal_workspaces is not None:
|
|
483
|
+
params["excludePersonalWorkspaces"] = exclude_personal_workspaces
|
|
484
|
+
|
|
485
|
+
url = _build_url(url, params)
|
|
486
|
+
|
|
487
|
+
response = client.get(url)
|
|
511
488
|
|
|
512
|
-
if response.status_code != 202:
|
|
513
|
-
raise FabricHTTPException(response)
|
|
514
|
-
scan_id = response.json()["id"]
|
|
515
|
-
scan_status = response.json().get("status")
|
|
516
|
-
while scan_status not in ["Succeeded", "Failed"]:
|
|
517
|
-
time.sleep(1)
|
|
518
|
-
response = client.get(f"/v1.0/myorg/admin/workspaces/scanStatus/{scan_id}")
|
|
519
|
-
scan_status = response.json().get("status")
|
|
520
|
-
if scan_status == "Failed":
|
|
521
|
-
raise FabricHTTPException(response)
|
|
522
|
-
response = client.get(f"/v1.0/myorg/admin/workspaces/scanResult/{scan_id}")
|
|
523
489
|
if response.status_code != 200:
|
|
524
490
|
raise FabricHTTPException(response)
|
|
525
491
|
|
|
526
|
-
|
|
492
|
+
df = pd.DataFrame(response.json()).rename(columns={"id": "Workspace Id"})
|
|
493
|
+
|
|
494
|
+
return df
|
|
527
495
|
|
|
528
496
|
|
|
529
|
-
def list_datasets(
|
|
497
|
+
def list_datasets(
|
|
498
|
+
top: Optional[int] = None,
|
|
499
|
+
filter: Optional[str] = None,
|
|
500
|
+
skip: Optional[int] = None,
|
|
501
|
+
) -> pd.DataFrame:
|
|
530
502
|
"""
|
|
531
503
|
Shows a list of datasets for the organization.
|
|
532
504
|
|
|
533
505
|
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>`_.
|
|
534
506
|
|
|
507
|
+
Parameters
|
|
508
|
+
----------
|
|
509
|
+
top : int, default=None
|
|
510
|
+
Returns only the first n results.
|
|
511
|
+
filter : str, default=None
|
|
512
|
+
Returns a subset of a results based on Odata filter query parameter condition.
|
|
513
|
+
skip : int, default=None
|
|
514
|
+
Skips the first n results.
|
|
515
|
+
token_provider : Optional[TokenProvider] = None,
|
|
516
|
+
Authentication provider used to be use in the request. Supports Service Principal.
|
|
517
|
+
|
|
535
518
|
Returns
|
|
536
519
|
-------
|
|
537
520
|
pandas.DataFrame
|
|
538
521
|
A pandas dataframe showing a list of datasets for the organization.
|
|
539
522
|
"""
|
|
540
|
-
|
|
541
523
|
df = pd.DataFrame(
|
|
542
524
|
columns=[
|
|
543
525
|
"Dataset Id",
|
|
@@ -563,7 +545,22 @@ def list_datasets() -> pd.DataFrame:
|
|
|
563
545
|
)
|
|
564
546
|
|
|
565
547
|
client = fabric.PowerBIRestClient()
|
|
566
|
-
|
|
548
|
+
|
|
549
|
+
params = {}
|
|
550
|
+
url = "/v1.0/myorg/admin/datasets"
|
|
551
|
+
|
|
552
|
+
if top is not None:
|
|
553
|
+
params["$top"] = top
|
|
554
|
+
|
|
555
|
+
if filter is not None:
|
|
556
|
+
params["$filter"] = filter
|
|
557
|
+
|
|
558
|
+
if skip is not None:
|
|
559
|
+
params["$skip"] = skip
|
|
560
|
+
|
|
561
|
+
url = _build_url(url, params)
|
|
562
|
+
|
|
563
|
+
response = client.get(url)
|
|
567
564
|
|
|
568
565
|
if response.status_code != 200:
|
|
569
566
|
raise FabricHTTPException(response)
|
|
@@ -614,74 +611,6 @@ def list_datasets() -> pd.DataFrame:
|
|
|
614
611
|
return df
|
|
615
612
|
|
|
616
613
|
|
|
617
|
-
def list_item_access_details(
|
|
618
|
-
item_name: str, type: str, workspace: Optional[str] = None
|
|
619
|
-
) -> pd.DataFrame:
|
|
620
|
-
"""
|
|
621
|
-
Returns a list of users (including groups and service principals) and lists their workspace roles.
|
|
622
|
-
|
|
623
|
-
This is a wrapper function for the following API: `Items - List Item Access Details <https://learn.microsoft.com/rest/api/fabric/admin/items/list-item-access-details>`_.
|
|
624
|
-
|
|
625
|
-
Parameters
|
|
626
|
-
----------
|
|
627
|
-
item_name : str
|
|
628
|
-
Name of the Fabric item.
|
|
629
|
-
type : str
|
|
630
|
-
Type of Fabric item.
|
|
631
|
-
workspace : str, default=None
|
|
632
|
-
The Fabric workspace name.
|
|
633
|
-
Defaults to None which resolves to the workspace of the attached lakehouse
|
|
634
|
-
or if no lakehouse attached, resolves to the workspace of the notebook.
|
|
635
|
-
|
|
636
|
-
Returns
|
|
637
|
-
-------
|
|
638
|
-
pandas.DataFrame
|
|
639
|
-
A pandas dataframe showing a list of users (including groups and service principals) and lists their workspace roles.
|
|
640
|
-
"""
|
|
641
|
-
|
|
642
|
-
workspace_name, workspace_id = _resolve_workspace_name_and_id(workspace)
|
|
643
|
-
item_id = _resolve_item_id(item_name=item_name, type=type, workspace=workspace_name)
|
|
644
|
-
|
|
645
|
-
df = pd.DataFrame(
|
|
646
|
-
columns=[
|
|
647
|
-
"User Id",
|
|
648
|
-
"User Name",
|
|
649
|
-
"User Type",
|
|
650
|
-
"User Principal Name",
|
|
651
|
-
"Item Name",
|
|
652
|
-
"Item Type",
|
|
653
|
-
"Item Id",
|
|
654
|
-
"Permissions",
|
|
655
|
-
"Additional Permissions",
|
|
656
|
-
]
|
|
657
|
-
)
|
|
658
|
-
client = fabric.FabricRestClient()
|
|
659
|
-
response = client.get(f"/v1/admin/workspaces/{workspace_id}/items/{item_id}/users")
|
|
660
|
-
|
|
661
|
-
if response.status_code != 200:
|
|
662
|
-
raise FabricHTTPException(response)
|
|
663
|
-
|
|
664
|
-
for v in response.json().get("accessDetails", []):
|
|
665
|
-
new_data = {
|
|
666
|
-
"User Id": v.get("principal", {}).get("id"),
|
|
667
|
-
"User Name": v.get("principal", {}).get("displayName"),
|
|
668
|
-
"User Type": v.get("principal", {}).get("type"),
|
|
669
|
-
"User Principal Name": v.get("principal", {})
|
|
670
|
-
.get("userDetails", {})
|
|
671
|
-
.get("userPrincipalName"),
|
|
672
|
-
"Item Type": v.get("itemAccessDetails", {}).get("type"),
|
|
673
|
-
"Permissions": v.get("itemAccessDetails", {}).get("permissions"),
|
|
674
|
-
"Additional Permissions": v.get("itemAccessDetails", {}).get(
|
|
675
|
-
"additionalPermissions"
|
|
676
|
-
),
|
|
677
|
-
"Item Name": item_name,
|
|
678
|
-
"Item Id": item_id,
|
|
679
|
-
}
|
|
680
|
-
df = pd.concat([df, pd.DataFrame([new_data])], ignore_index=True)
|
|
681
|
-
|
|
682
|
-
return df
|
|
683
|
-
|
|
684
|
-
|
|
685
614
|
def list_access_entities(
|
|
686
615
|
user_email_address: str,
|
|
687
616
|
) -> pd.DataFrame:
|
|
@@ -700,7 +629,6 @@ def list_access_entities(
|
|
|
700
629
|
pandas.DataFrame
|
|
701
630
|
A pandas dataframe showing a list of permission details for Fabric and Power BI items the specified user can access.
|
|
702
631
|
"""
|
|
703
|
-
|
|
704
632
|
df = pd.DataFrame(
|
|
705
633
|
columns=[
|
|
706
634
|
"Item Id",
|
|
@@ -711,6 +639,7 @@ def list_access_entities(
|
|
|
711
639
|
]
|
|
712
640
|
)
|
|
713
641
|
client = fabric.FabricRestClient()
|
|
642
|
+
|
|
714
643
|
response = client.get(f"/v1/admin/users/{user_email_address}/access")
|
|
715
644
|
|
|
716
645
|
if response.status_code != 200:
|
|
@@ -744,8 +673,8 @@ def list_workspace_access_details(
|
|
|
744
673
|
|
|
745
674
|
Parameters
|
|
746
675
|
----------
|
|
747
|
-
workspace : str, default=None
|
|
748
|
-
The Fabric workspace name.
|
|
676
|
+
workspace : str | UUID, default=None
|
|
677
|
+
The Fabric workspace name or id.
|
|
749
678
|
Defaults to None which resolves to the workspace of the attached lakehouse
|
|
750
679
|
or if no lakehouse attached, resolves to the workspace of the notebook.
|
|
751
680
|
|
|
@@ -754,7 +683,6 @@ def list_workspace_access_details(
|
|
|
754
683
|
pandas.DataFrame
|
|
755
684
|
A pandas dataframe showing a list of users (including groups and Service Principals) that have access to the specified workspace.
|
|
756
685
|
"""
|
|
757
|
-
|
|
758
686
|
workspace_name, workspace_id = _resolve_workspace_name_and_id(workspace)
|
|
759
687
|
|
|
760
688
|
df = pd.DataFrame(
|
|
@@ -767,7 +695,9 @@ def list_workspace_access_details(
|
|
|
767
695
|
"Workspace Role",
|
|
768
696
|
]
|
|
769
697
|
)
|
|
698
|
+
|
|
770
699
|
client = fabric.FabricRestClient()
|
|
700
|
+
|
|
771
701
|
response = client.get(f"/v1/admin/workspaces/{workspace_id}/users")
|
|
772
702
|
if response.status_code != 200:
|
|
773
703
|
raise FabricHTTPException(response)
|
|
@@ -786,140 +716,13 @@ def list_workspace_access_details(
|
|
|
786
716
|
return df
|
|
787
717
|
|
|
788
718
|
|
|
789
|
-
def _resolve_item_id(
|
|
790
|
-
item_name: str, type: str, workspace: Optional[str] = None
|
|
791
|
-
) -> UUID:
|
|
792
|
-
|
|
793
|
-
workspace_name, workspace_id = _resolve_workspace_name_and_id(workspace)
|
|
794
|
-
dfI = list_items(workspace=workspace_name, type=type)
|
|
795
|
-
dfI_filt = dfI[dfI["Item Name"] == item_name]
|
|
796
|
-
|
|
797
|
-
if len(dfI_filt) == 0:
|
|
798
|
-
raise ValueError(
|
|
799
|
-
f"The '{item_name}' {type} does not exist within the '{workspace_name}' workspace."
|
|
800
|
-
)
|
|
801
|
-
|
|
802
|
-
return dfI_filt["Item Id"].iloc[0]
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
def _resolve_workspace_name_and_id(workspace: str) -> Tuple[str, UUID]:
|
|
806
|
-
|
|
807
|
-
filter_condition = urllib.parse.quote(workspace)
|
|
808
|
-
dfW_filt = list_workspaces(filter=f"name eq '{filter_condition}'")
|
|
809
|
-
workspace_name = dfW_filt["Name"].iloc[0]
|
|
810
|
-
workspace_id = dfW_filt["Id"].iloc[0]
|
|
811
|
-
|
|
812
|
-
return workspace_name, workspace_id
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
def list_items(
|
|
816
|
-
capacity_name: Optional[str] = None,
|
|
817
|
-
workspace: Optional[str] = None,
|
|
818
|
-
state: Optional[str] = None,
|
|
819
|
-
type: Optional[str] = None,
|
|
820
|
-
) -> pd.DataFrame:
|
|
821
|
-
"""
|
|
822
|
-
Shows a list of active Fabric and Power BI items.
|
|
823
|
-
|
|
824
|
-
This is a wrapper function for the following API: `Items - List Items <https://learn.microsoft.com/rest/api/fabric/admin/items/list-items>`_.
|
|
825
|
-
|
|
826
|
-
Parameters
|
|
827
|
-
----------
|
|
828
|
-
capacity_name : str, default=None
|
|
829
|
-
The capacity name.
|
|
830
|
-
workspace : str, default=None
|
|
831
|
-
The Fabric workspace name.
|
|
832
|
-
Defaults to None which resolves to the workspace of the attached lakehouse
|
|
833
|
-
or if no lakehouse attached, resolves to the workspace of the notebook.
|
|
834
|
-
state : str, default=None
|
|
835
|
-
The item state.
|
|
836
|
-
type : str, default=None
|
|
837
|
-
The item type.
|
|
838
|
-
|
|
839
|
-
Returns
|
|
840
|
-
-------
|
|
841
|
-
pandas.DataFrame
|
|
842
|
-
A pandas dataframe showing a list of active Fabric and Power BI items.
|
|
843
|
-
"""
|
|
844
|
-
|
|
845
|
-
url = "/v1/admin/items?"
|
|
846
|
-
|
|
847
|
-
df = pd.DataFrame(
|
|
848
|
-
columns=[
|
|
849
|
-
"Item Id",
|
|
850
|
-
"Item Name",
|
|
851
|
-
"Type",
|
|
852
|
-
"Description",
|
|
853
|
-
"State",
|
|
854
|
-
"Last Updated Date",
|
|
855
|
-
"Creator Principal Id",
|
|
856
|
-
"Creator Principal Display Name",
|
|
857
|
-
"Creator Principal Type",
|
|
858
|
-
"Creator User Principal Name",
|
|
859
|
-
"Workspace Id",
|
|
860
|
-
"Capacity Id",
|
|
861
|
-
]
|
|
862
|
-
)
|
|
863
|
-
|
|
864
|
-
if workspace is not None:
|
|
865
|
-
workspace_name, workspace_id = _resolve_workspace_name_and_id(workspace)
|
|
866
|
-
url += f"workspaceId={workspace_id}&"
|
|
867
|
-
if capacity_name is not None:
|
|
868
|
-
dfC = list_capacities()
|
|
869
|
-
dfC_filt = dfC[dfC["Capacity Name"] == capacity_name]
|
|
870
|
-
if len(dfC_filt) == 0:
|
|
871
|
-
raise ValueError(
|
|
872
|
-
f"{icons.red_dot} The '{capacity_name}' capacity does not exist."
|
|
873
|
-
)
|
|
874
|
-
capacity_id = dfC_filt["Capacity Id"].iloc[0]
|
|
875
|
-
url += f"capacityId={capacity_id}&"
|
|
876
|
-
if state is not None:
|
|
877
|
-
url += f"state={state}&"
|
|
878
|
-
if type is not None:
|
|
879
|
-
url += f"type={type}&"
|
|
880
|
-
|
|
881
|
-
if url.endswith("?") or url.endswith("&"):
|
|
882
|
-
url = url[:-1]
|
|
883
|
-
|
|
884
|
-
client = fabric.FabricRestClient()
|
|
885
|
-
response = client.get(url)
|
|
886
|
-
|
|
887
|
-
if response.status_code != 200:
|
|
888
|
-
raise FabricHTTPException(response)
|
|
889
|
-
|
|
890
|
-
responses = pagination(client, response)
|
|
891
|
-
|
|
892
|
-
for r in responses:
|
|
893
|
-
for v in r.get("itemEntities", []):
|
|
894
|
-
new_data = {
|
|
895
|
-
"Item Id": v.get("id"),
|
|
896
|
-
"Type": v.get("type"),
|
|
897
|
-
"Item Name": v.get("name"),
|
|
898
|
-
"Description": v.get("description"),
|
|
899
|
-
"State": v.get("state"),
|
|
900
|
-
"Last Updated Date": v.get("lastUpdatedDate"),
|
|
901
|
-
"Creator Principal Id": v.get("creatorPrincipal", {}).get("id"),
|
|
902
|
-
"Creator Principal Display Name": v.get("creatorPrincipal", {}).get(
|
|
903
|
-
"displayName"
|
|
904
|
-
),
|
|
905
|
-
"Creator Principal Type": v.get("creatorPrincipal", {}).get("type"),
|
|
906
|
-
"Creator User Principal Name": v.get("creatorPrincipal", {})
|
|
907
|
-
.get("userDetails", {})
|
|
908
|
-
.get("userPrincipalName"),
|
|
909
|
-
"Workspace Id": v.get("workspaceId"),
|
|
910
|
-
"Capacity Id": v.get("capacityId"),
|
|
911
|
-
}
|
|
912
|
-
df = pd.concat([df, pd.DataFrame(new_data, index=[0])], ignore_index=True)
|
|
913
|
-
|
|
914
|
-
return df
|
|
915
|
-
|
|
916
|
-
|
|
917
719
|
def list_activity_events(
|
|
918
720
|
start_time: str,
|
|
919
721
|
end_time: str,
|
|
920
722
|
activity_filter: Optional[str] = None,
|
|
921
723
|
user_id_filter: Optional[str] = None,
|
|
922
|
-
|
|
724
|
+
return_dataframe: Optional[bool] = True,
|
|
725
|
+
) -> pd.DataFrame | dict:
|
|
923
726
|
"""
|
|
924
727
|
Shows a list of audit activity events for a tenant.
|
|
925
728
|
|
|
@@ -935,15 +738,16 @@ def list_activity_events(
|
|
|
935
738
|
Filter value for activities. Example: 'viewreport'.
|
|
936
739
|
user_id_filter : str, default=None
|
|
937
740
|
Email address of the user.
|
|
741
|
+
return_dataframe : bool, default=True
|
|
742
|
+
If True the response is a pandas.DataFrame. If False returns the original Json. Default True
|
|
938
743
|
|
|
939
744
|
Returns
|
|
940
745
|
-------
|
|
941
|
-
pandas.DataFrame
|
|
942
|
-
A pandas dataframe showing a list of audit activity events for a tenant.
|
|
746
|
+
pandas.DataFrame | dict
|
|
747
|
+
A pandas dataframe or json showing a list of audit activity events for a tenant.
|
|
943
748
|
"""
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
end_dt = datetime.strptime(end_time, "%Y-%m-%dT%H:%M:%S")
|
|
749
|
+
start_dt = dtparser(start_time)
|
|
750
|
+
end_dt = dtparser(end_time)
|
|
947
751
|
|
|
948
752
|
if not start_dt.date() == end_dt.date():
|
|
949
753
|
raise ValueError(
|
|
@@ -976,104 +780,227 @@ def list_activity_events(
|
|
|
976
780
|
]
|
|
977
781
|
)
|
|
978
782
|
|
|
783
|
+
resposeJson = {"activityEventEntities": []}
|
|
784
|
+
|
|
979
785
|
tic = "%27"
|
|
980
786
|
space = "%20"
|
|
981
787
|
client = fabric.PowerBIRestClient()
|
|
982
|
-
|
|
788
|
+
|
|
789
|
+
params = {}
|
|
790
|
+
url = "/v1.0/myorg/admin/activityevents"
|
|
791
|
+
|
|
792
|
+
if start_dt is not None:
|
|
793
|
+
params["startDateTime"] = f"'{start_dt.isoformat(timespec='milliseconds')}'"
|
|
794
|
+
|
|
795
|
+
if end_dt is not None:
|
|
796
|
+
params["endDateTime"] = f"'{end_dt.isoformat(timespec='milliseconds')}'"
|
|
797
|
+
|
|
983
798
|
conditions = []
|
|
984
799
|
|
|
985
800
|
if activity_filter is not None:
|
|
986
801
|
conditions.append(f"Activity{space}eq{space}{tic}{activity_filter}{tic}")
|
|
802
|
+
|
|
987
803
|
if user_id_filter is not None:
|
|
988
804
|
conditions.append(f"UserId{space}eq{space}{tic}{user_id_filter}{tic}")
|
|
989
805
|
|
|
990
|
-
|
|
991
|
-
f"
|
|
806
|
+
if conditions:
|
|
807
|
+
params["filder"] = f"{f'{space}and{space}'.join(conditions)}"
|
|
808
|
+
|
|
809
|
+
url_parts = list(urllib.parse.urlparse(url))
|
|
810
|
+
url_parts[4] = urllib.parse.urlencode(params)
|
|
811
|
+
url = urllib.parse.urlunparse(url_parts)
|
|
812
|
+
|
|
813
|
+
response = client.get(url)
|
|
814
|
+
|
|
815
|
+
if response.status_code != 200:
|
|
816
|
+
raise FabricHTTPException(response)
|
|
817
|
+
|
|
818
|
+
responses = pagination(client, response)
|
|
819
|
+
|
|
820
|
+
for r in responses:
|
|
821
|
+
if return_dataframe:
|
|
822
|
+
for i in r.get("activityEventEntities", []):
|
|
823
|
+
new_data = {
|
|
824
|
+
"Id": i.get("id"),
|
|
825
|
+
"Record Type": i.get("RecordType"),
|
|
826
|
+
"Creation Time": i.get("CreationTime"),
|
|
827
|
+
"Operation": i.get("Operation"),
|
|
828
|
+
"Organization Id": i.get("OrganizationId"),
|
|
829
|
+
"User Type": i.get("UserType"),
|
|
830
|
+
"User Key": i.get("UserKey"),
|
|
831
|
+
"Workload": i.get("Workload"),
|
|
832
|
+
"Result Status": i.get("ResultStatus"),
|
|
833
|
+
"User Id": i.get("UserId"),
|
|
834
|
+
"Client IP": i.get("ClientIP"),
|
|
835
|
+
"User Agent": i.get("UserAgent"),
|
|
836
|
+
"Activity": i.get("Activity"),
|
|
837
|
+
"Workspace Name": i.get("WorkSpaceName"),
|
|
838
|
+
"Workspace Id": i.get("WorkspaceId"),
|
|
839
|
+
"Object Id": i.get("ObjectId"),
|
|
840
|
+
"Request Id": i.get("RequestId"),
|
|
841
|
+
"Object Type": i.get("ObjectType"),
|
|
842
|
+
"Object Display Name": i.get("ObjectDisplayName"),
|
|
843
|
+
"Experience": i.get("Experience"),
|
|
844
|
+
"Refresh Enforcement Policy": i.get("RefreshEnforcementPolicy"),
|
|
845
|
+
}
|
|
846
|
+
df = pd.concat(
|
|
847
|
+
[df, pd.DataFrame(new_data, index=[0])],
|
|
848
|
+
ignore_index=True,
|
|
849
|
+
)
|
|
850
|
+
else:
|
|
851
|
+
resposeJson["activityEventEntities"].extend(r.get("activityEventEntities"))
|
|
852
|
+
|
|
853
|
+
if return_dataframe:
|
|
854
|
+
df["Creation Time"] = pd.to_datetime(df["Creation Time"])
|
|
855
|
+
activity_events = df
|
|
856
|
+
else:
|
|
857
|
+
activity_events = resposeJson
|
|
858
|
+
|
|
859
|
+
return activity_events
|
|
860
|
+
|
|
861
|
+
|
|
862
|
+
def _resolve_capacity_name_and_id(
|
|
863
|
+
capacity: str | UUID,
|
|
864
|
+
) -> Tuple[str, UUID]:
|
|
865
|
+
|
|
866
|
+
dfC = list_capacities(capacity=capacity)
|
|
867
|
+
try:
|
|
868
|
+
capacity_name = dfC["Capacity Name"].iloc[0]
|
|
869
|
+
capacity_id = dfC["Capacity Id"].iloc[0]
|
|
870
|
+
except Exception:
|
|
871
|
+
raise ValueError(f"{icons.red_dot} The '{capacity}' capacity was not found.")
|
|
872
|
+
|
|
873
|
+
return capacity_name, capacity_id
|
|
874
|
+
|
|
875
|
+
|
|
876
|
+
def _list_capacities_meta() -> pd.DataFrame:
|
|
877
|
+
"""
|
|
878
|
+
Shows the a list of capacities and their properties. This function is the admin version.
|
|
879
|
+
|
|
880
|
+
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>`_.
|
|
881
|
+
|
|
882
|
+
Returns
|
|
883
|
+
-------
|
|
884
|
+
pandas.DataFrame
|
|
885
|
+
A pandas dataframe showing the capacities and their properties
|
|
886
|
+
"""
|
|
887
|
+
|
|
888
|
+
client = fabric.FabricRestClient()
|
|
889
|
+
|
|
890
|
+
df = pd.DataFrame(
|
|
891
|
+
columns=["Capacity Id", "Capacity Name", "Sku", "Region", "State", "Admins"]
|
|
992
892
|
)
|
|
993
893
|
|
|
994
|
-
|
|
995
|
-
|
|
894
|
+
response = client.get("/v1.0/myorg/admin/capacities")
|
|
895
|
+
|
|
996
896
|
if response.status_code != 200:
|
|
997
897
|
raise FabricHTTPException(response)
|
|
998
898
|
|
|
999
899
|
responses = pagination(client, response)
|
|
1000
900
|
|
|
1001
901
|
for r in responses:
|
|
1002
|
-
for i in r.get("
|
|
902
|
+
for i in r.get("value", []):
|
|
1003
903
|
new_data = {
|
|
1004
|
-
"Id": i.get("id"),
|
|
1005
|
-
"
|
|
1006
|
-
"
|
|
1007
|
-
"
|
|
1008
|
-
"
|
|
1009
|
-
"
|
|
1010
|
-
"User Key": i.get("UserKey"),
|
|
1011
|
-
"Workload": i.get("Workload"),
|
|
1012
|
-
"Result Status": i.get("ResultStatus"),
|
|
1013
|
-
"User Id": i.get("UserId"),
|
|
1014
|
-
"Client IP": i.get("ClientIP"),
|
|
1015
|
-
"User Agent": i.get("UserAgent"),
|
|
1016
|
-
"Activity": i.get("Activity"),
|
|
1017
|
-
"Workspace Name": i.get("WorkSpaceName"),
|
|
1018
|
-
"Workspace Id": i.get("WorkspaceId"),
|
|
1019
|
-
"Object Id": i.get("ObjectId"),
|
|
1020
|
-
"Request Id": i.get("RequestId"),
|
|
1021
|
-
"Object Type": i.get("ObjectType"),
|
|
1022
|
-
"Object Display Name": i.get("ObjectDisplayName"),
|
|
1023
|
-
"Experience": i.get("Experience"),
|
|
1024
|
-
"Refresh Enforcement Policy": i.get("RefreshEnforcementPolicy"),
|
|
904
|
+
"Capacity Id": i.get("id").lower(),
|
|
905
|
+
"Capacity Name": i.get("displayName"),
|
|
906
|
+
"Sku": i.get("sku"),
|
|
907
|
+
"Region": i.get("region"),
|
|
908
|
+
"State": i.get("state"),
|
|
909
|
+
"Admins": [i.get("admins", [])],
|
|
1025
910
|
}
|
|
1026
|
-
df = pd.concat(
|
|
1027
|
-
[df, pd.DataFrame(new_data, index=[0])],
|
|
1028
|
-
ignore_index=True,
|
|
1029
|
-
)
|
|
1030
|
-
|
|
1031
|
-
df["Creation Time"] = pd.to_datetime(df["Creation Time"])
|
|
911
|
+
df = pd.concat([df, pd.DataFrame(new_data, index=[0])], ignore_index=True)
|
|
1032
912
|
|
|
1033
913
|
return df
|
|
1034
914
|
|
|
1035
915
|
|
|
1036
|
-
def
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
916
|
+
def _resolve_workspace_name_and_id(
|
|
917
|
+
workspace: str | UUID,
|
|
918
|
+
) -> Tuple[str, UUID]:
|
|
919
|
+
|
|
920
|
+
dfW = list_workspaces(workspace=workspace)
|
|
921
|
+
try:
|
|
922
|
+
workspace_name = dfW["Name"].iloc[0]
|
|
923
|
+
workspace_id = dfW["Id"].iloc[0]
|
|
924
|
+
except Exception:
|
|
925
|
+
raise ValueError(f"{icons.red_dot} The '{workspace}' workspace was not found.")
|
|
926
|
+
|
|
927
|
+
return workspace_name, workspace_id
|
|
928
|
+
|
|
929
|
+
|
|
930
|
+
def list_reports(
|
|
931
|
+
top: Optional[int] = None, skip: Optional[int] = None, filter: Optional[str] = None
|
|
1040
932
|
) -> pd.DataFrame:
|
|
1041
933
|
"""
|
|
1042
|
-
|
|
934
|
+
Shows a list of reports for the organization.
|
|
1043
935
|
|
|
1044
|
-
This is a wrapper function for the following API: `Admin -
|
|
1045
|
-
|
|
1046
|
-
Parameters
|
|
1047
|
-
----------
|
|
1048
|
-
modified_since : str
|
|
1049
|
-
Last modified date (must be in ISO 8601 compliant UTC format). Example: "2024-11-02T05:51:30.0000000Z".
|
|
1050
|
-
exclude_inactive_workspaces : bool, default=False
|
|
1051
|
-
Whether to exclude inactive workspaces.
|
|
1052
|
-
exclude_personal_workspaces : bool, default=False
|
|
1053
|
-
Whether to exclude personal workspaces.
|
|
936
|
+
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>`_.
|
|
1054
937
|
|
|
1055
938
|
Returns
|
|
1056
939
|
-------
|
|
1057
940
|
pandas.DataFrame
|
|
1058
|
-
A pandas dataframe showing a list of
|
|
941
|
+
A pandas dataframe showing a list of reports for the organization.
|
|
1059
942
|
"""
|
|
1060
943
|
|
|
1061
|
-
|
|
1062
|
-
|
|
944
|
+
df = pd.DataFrame(
|
|
945
|
+
columns=[
|
|
946
|
+
"Report Id",
|
|
947
|
+
"Report Name",
|
|
948
|
+
"Type",
|
|
949
|
+
"Web URL",
|
|
950
|
+
"Embed URL",
|
|
951
|
+
"Dataset Id",
|
|
952
|
+
"Created Date",
|
|
953
|
+
"Modified Date",
|
|
954
|
+
"Created By",
|
|
955
|
+
"Modified By",
|
|
956
|
+
"Sensitivity Label Id",
|
|
957
|
+
"Users",
|
|
958
|
+
"Subscriptions",
|
|
959
|
+
"Workspace Id",
|
|
960
|
+
"Report Flags",
|
|
961
|
+
]
|
|
962
|
+
)
|
|
1063
963
|
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
964
|
+
url = "/v1.0/myorg/admin/reports?"
|
|
965
|
+
if top is not None:
|
|
966
|
+
url += f"$top={top}&"
|
|
967
|
+
if skip is not None:
|
|
968
|
+
url += f"$skip={skip}&"
|
|
969
|
+
if filter is not None:
|
|
970
|
+
url += f"$filter={filter}&"
|
|
1070
971
|
|
|
1071
|
-
url
|
|
972
|
+
url.rstrip("$").rstrip("?")
|
|
1072
973
|
|
|
974
|
+
client = fabric.PowerBIRestClient()
|
|
1073
975
|
response = client.get(url)
|
|
976
|
+
|
|
1074
977
|
if response.status_code != 200:
|
|
1075
978
|
raise FabricHTTPException(response)
|
|
1076
979
|
|
|
1077
|
-
|
|
980
|
+
for v in response.json().get("value", []):
|
|
981
|
+
new_data = {
|
|
982
|
+
"Report Id": v.get("id"),
|
|
983
|
+
"Report Name": v.get("name"),
|
|
984
|
+
"Type": v.get("reportType"),
|
|
985
|
+
"Web URL": v.get("webUrl"),
|
|
986
|
+
"Embed URL": v.get("embedUrl"),
|
|
987
|
+
"Dataset Id": v.get("datasetId"),
|
|
988
|
+
"Created Date": v.get("createdDateTime"),
|
|
989
|
+
"Modified Date": v.get("modifiedDateTime"),
|
|
990
|
+
"Created By": v.get("createdBy"),
|
|
991
|
+
"Modified By": v.get("modifiedBy"),
|
|
992
|
+
"Sensitivity Label Id": v.get("sensitivityLabel", {}).get("labelId"),
|
|
993
|
+
"Users": v.get("users"),
|
|
994
|
+
"Subscriptions": v.get("subscriptions"),
|
|
995
|
+
"Workspace Id": v.get("workspaceId"),
|
|
996
|
+
"Report Flags": v.get("reportFlags"),
|
|
997
|
+
}
|
|
998
|
+
df = pd.concat([df, pd.DataFrame([new_data])], ignore_index=True)
|
|
999
|
+
|
|
1000
|
+
int_cols = ["Report Flags"]
|
|
1001
|
+
df[int_cols] = df[int_cols].astype(int)
|
|
1002
|
+
|
|
1003
|
+
df["Created Date"] = pd.to_datetime(df["Created Date"], errors="coerce")
|
|
1004
|
+
df["Modified Date"] = pd.to_datetime(df["Modified Date"], errors="coerce")
|
|
1078
1005
|
|
|
1079
1006
|
return df
|