semantic-link-labs 0.12.2__py3-none-any.whl → 0.12.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 (40) hide show
  1. {semantic_link_labs-0.12.2.dist-info → semantic_link_labs-0.12.4.dist-info}/METADATA +5 -3
  2. {semantic_link_labs-0.12.2.dist-info → semantic_link_labs-0.12.4.dist-info}/RECORD +39 -31
  3. sempy_labs/__init__.py +18 -10
  4. sempy_labs/_a_lib_info.py +1 -1
  5. sempy_labs/_authentication.py +1 -1
  6. sempy_labs/_capacities.py +1 -1
  7. sempy_labs/_generate_semantic_model.py +2 -2
  8. sempy_labs/_get_connection_string.py +84 -0
  9. sempy_labs/_git.py +1 -1
  10. sempy_labs/_helper_functions.py +28 -4
  11. sempy_labs/_list_functions.py +55 -5
  12. sempy_labs/_managed_private_endpoints.py +1 -1
  13. sempy_labs/_notebooks.py +4 -2
  14. sempy_labs/_semantic_models.py +118 -0
  15. sempy_labs/_sql_audit_settings.py +208 -0
  16. sempy_labs/_sql_endpoints.py +27 -24
  17. sempy_labs/_utils.py +2 -0
  18. sempy_labs/_warehouses.py +1 -56
  19. sempy_labs/admin/__init__.py +6 -0
  20. sempy_labs/admin/_items.py +3 -3
  21. sempy_labs/admin/_labels.py +211 -0
  22. sempy_labs/directlake/_warm_cache.py +3 -1
  23. sempy_labs/eventstream/__init__.py +37 -0
  24. sempy_labs/eventstream/_items.py +263 -0
  25. sempy_labs/eventstream/_topology.py +652 -0
  26. sempy_labs/graph/__init__.py +12 -0
  27. sempy_labs/graph/_groups.py +60 -53
  28. sempy_labs/graph/_sensitivity_labels.py +120 -0
  29. sempy_labs/graph/_teams.py +19 -18
  30. sempy_labs/graph/_user_licenses.py +96 -0
  31. sempy_labs/graph/_users.py +23 -16
  32. sempy_labs/lakehouse/_get_lakehouse_tables.py +33 -1
  33. sempy_labs/lakehouse/_lakehouse.py +6 -2
  34. sempy_labs/lakehouse/_partitioning.py +165 -0
  35. sempy_labs/report/_reportwrapper.py +15 -5
  36. sempy_labs/tom/_model.py +111 -16
  37. sempy_labs/_eventstreams.py +0 -123
  38. {semantic_link_labs-0.12.2.dist-info → semantic_link_labs-0.12.4.dist-info}/WHEEL +0 -0
  39. {semantic_link_labs-0.12.2.dist-info → semantic_link_labs-0.12.4.dist-info}/licenses/LICENSE +0 -0
  40. {semantic_link_labs-0.12.2.dist-info → semantic_link_labs-0.12.4.dist-info}/top_level.txt +0 -0
@@ -55,7 +55,7 @@ def list_groups() -> pd.DataFrame:
55
55
  A pandas dataframe showing a list of groups and their properties.
56
56
  """
57
57
 
58
- result = _base_api(request="groups", client="graph").json()
58
+ result = _base_api(request="groups", client="graph", uses_pagination=True)
59
59
 
60
60
  columns = {
61
61
  "Group Id": "string",
@@ -76,24 +76,25 @@ def list_groups() -> pd.DataFrame:
76
76
  df = _create_dataframe(columns=columns)
77
77
 
78
78
  rows = []
79
- for v in result.get("value"):
80
- rows.append(
81
- {
82
- "Group Id": v.get("id"),
83
- "Group Name": v.get("displayName"),
84
- "Mail": v.get("mail"),
85
- "Description": v.get("description"),
86
- "Classification": v.get("classification"),
87
- "Mail Enabled": v.get("mailEnabled"),
88
- "Security Enabled": v.get("securityEnabled"),
89
- "Created Date Time": v.get("createdDateTime"),
90
- "Expiration Date Time": v.get("expirationDateTime"),
91
- "Renewed Date Time": v.get("renewedDateTime"),
92
- "Deleted Date Time": v.get("deletedDateTime"),
93
- "Visibility": v.get("visibility"),
94
- "Security Identifier": v.get("securityIdentifier"),
95
- }
96
- )
79
+ for r in result:
80
+ for v in r.get("value", []):
81
+ rows.append(
82
+ {
83
+ "Group Id": v.get("id"),
84
+ "Group Name": v.get("displayName"),
85
+ "Mail": v.get("mail"),
86
+ "Description": v.get("description"),
87
+ "Classification": v.get("classification"),
88
+ "Mail Enabled": v.get("mailEnabled"),
89
+ "Security Enabled": v.get("securityEnabled"),
90
+ "Created Date Time": v.get("createdDateTime"),
91
+ "Expiration Date Time": v.get("expirationDateTime"),
92
+ "Renewed Date Time": v.get("renewedDateTime"),
93
+ "Deleted Date Time": v.get("deletedDateTime"),
94
+ "Visibility": v.get("visibility"),
95
+ "Security Identifier": v.get("securityIdentifier"),
96
+ }
97
+ )
97
98
 
98
99
  if rows:
99
100
  df = pd.DataFrame(rows, columns=list(columns.keys()))
@@ -190,7 +191,9 @@ def list_group_members(group: str | UUID) -> pd.DataFrame:
190
191
 
191
192
  group_id = resolve_group_id(group)
192
193
 
193
- result = _base_api(request=f"groups/{group_id}/members", client="graph").json()
194
+ result = _base_api(
195
+ request=f"groups/{group_id}/members", client="graph", uses_pagination=True
196
+ )
194
197
 
195
198
  columns = {
196
199
  "Member Id": "string",
@@ -209,22 +212,23 @@ def list_group_members(group: str | UUID) -> pd.DataFrame:
209
212
  df = _create_dataframe(columns=columns)
210
213
 
211
214
  rows = []
212
- for v in result.get("value"):
213
- rows.append(
214
- {
215
- "Member Id": v.get("id"),
216
- "Member Name": v.get("displayName"),
217
- "User Principal Name": v.get("userPrincipalName"),
218
- "Mail": v.get("mail"),
219
- "Job Title": v.get("jobTitle"),
220
- "Office Location": v.get("officeLocation"),
221
- "Mobile Phone": v.get("mobilePhone"),
222
- "Business Phones": str(v.get("businessPhones")),
223
- "Preferred Language": v.get("preferredLanguage"),
224
- "Given Name": v.get("givenName"),
225
- "Surname": v.get("surname"),
226
- }
227
- )
215
+ for r in result:
216
+ for v in r.get("value", []):
217
+ rows.append(
218
+ {
219
+ "Member Id": v.get("id"),
220
+ "Member Name": v.get("displayName"),
221
+ "User Principal Name": v.get("userPrincipalName"),
222
+ "Mail": v.get("mail"),
223
+ "Job Title": v.get("jobTitle"),
224
+ "Office Location": v.get("officeLocation"),
225
+ "Mobile Phone": v.get("mobilePhone"),
226
+ "Business Phones": str(v.get("businessPhones")),
227
+ "Preferred Language": v.get("preferredLanguage"),
228
+ "Given Name": v.get("givenName"),
229
+ "Surname": v.get("surname"),
230
+ }
231
+ )
228
232
 
229
233
  if rows:
230
234
  df = pd.DataFrame(rows, columns=list(columns.keys()))
@@ -254,7 +258,9 @@ def list_group_owners(group: str | UUID) -> pd.DataFrame:
254
258
 
255
259
  group_id = resolve_group_id(group)
256
260
 
257
- result = _base_api(request=f"groups/{group_id}/owners", client="graph").json()
261
+ result = _base_api(
262
+ request=f"groups/{group_id}/owners", client="graph", uses_pagination=True
263
+ )
258
264
 
259
265
  columns = {
260
266
  "Owner Id": "string",
@@ -273,22 +279,23 @@ def list_group_owners(group: str | UUID) -> pd.DataFrame:
273
279
  df = _create_dataframe(columns=columns)
274
280
 
275
281
  rows = []
276
- for v in result.get("value"):
277
- rows.append(
278
- {
279
- "Owner Id": v.get("id"),
280
- "Owner Name": v.get("displayName"),
281
- "User Principal Name": v.get("userPrincipalName"),
282
- "Mail": v.get("mail"),
283
- "Job Title": v.get("jobTitle"),
284
- "Office Location": v.get("officeLocation"),
285
- "Mobile Phone": v.get("mobilePhone"),
286
- "Business Phones": str(v.get("businessPhones")),
287
- "Preferred Language": v.get("preferredLanguage"),
288
- "Given Name": v.get("givenName"),
289
- "Surname": v.get("surname"),
290
- }
291
- )
282
+ for r in result:
283
+ for v in r.get("value", []):
284
+ rows.append(
285
+ {
286
+ "Owner Id": v.get("id"),
287
+ "Owner Name": v.get("displayName"),
288
+ "User Principal Name": v.get("userPrincipalName"),
289
+ "Mail": v.get("mail"),
290
+ "Job Title": v.get("jobTitle"),
291
+ "Office Location": v.get("officeLocation"),
292
+ "Mobile Phone": v.get("mobilePhone"),
293
+ "Business Phones": str(v.get("businessPhones")),
294
+ "Preferred Language": v.get("preferredLanguage"),
295
+ "Given Name": v.get("givenName"),
296
+ "Surname": v.get("surname"),
297
+ }
298
+ )
292
299
 
293
300
  if rows:
294
301
  df = pd.DataFrame(rows, columns=list(columns.keys()))
@@ -0,0 +1,120 @@
1
+ import pandas as pd
2
+ from uuid import UUID
3
+ from typing import Optional
4
+ from sempy_labs._helper_functions import (
5
+ _base_api,
6
+ _create_dataframe,
7
+ _update_dataframe_datatypes,
8
+ _is_valid_uuid,
9
+ )
10
+ from sempy._utils._log import log
11
+
12
+
13
+ @log
14
+ def list_sensitivity_labels(user: Optional[str | UUID] = None) -> pd.DataFrame:
15
+ """
16
+ Get a list of `sensitivity label <https://learn.microsoft.com/graph/api/resources/security-sensitivitylabel>`_ objects associated with a user or organization.
17
+
18
+ This is a wrapper function for the following API: `List sensitivityLabels https://learn.microsoft.com/graph/api/security-informationprotection-list-sensitivitylabels>`_.
19
+
20
+ Service Principal Authentication is required (see `here <https://github.com/microsoft/semantic-link-labs/blob/main/notebooks/Service%20Principal.ipynb>`_ for examples).
21
+
22
+ Parameters
23
+ ----------
24
+ user : str | uuid.UUID
25
+ The user ID or user principal name.
26
+
27
+ Returns
28
+ -------
29
+ pandas.DataFrame
30
+ A pandas dataframe showing a list of `sensitivity label <https://learn.microsoft.com/graph/api/resources/security-sensitivitylabel>`_ objects associated with a user or organization.
31
+ """
32
+ from sempy_labs.graph import resolve_user_id
33
+
34
+ url = "/security/informationProtection/sensitivityLabels"
35
+
36
+ if user is not None:
37
+ user_id = resolve_user_id(user=user)
38
+ url = f"users/{user_id}{url}"
39
+
40
+ result = _base_api(request=url, client="graph").json()
41
+
42
+ columns = {
43
+ "Sensitivity Label Id": "str",
44
+ "Sensitivity Label Name": "str",
45
+ "Description": "str",
46
+ "Color": "str",
47
+ "Sensitivity": "int",
48
+ "Tooltip": "str",
49
+ "Is Active": "bool",
50
+ "Is Appliable": "bool",
51
+ "Has Protection": "bool",
52
+ "Parent Sensitivity Label Id": "str",
53
+ "Parent Sensitivity Label Name": "str",
54
+ }
55
+ df = _create_dataframe(columns=columns)
56
+
57
+ rows = []
58
+ for item in result.get("value", []):
59
+ row = {
60
+ "Sensitivity Label Id": item.get("id"),
61
+ "Sensitivity Label Name": item.get("name"),
62
+ "Description": item.get("description"),
63
+ "Color": item.get("color"),
64
+ "Sensitivity": item.get("sensitivity"),
65
+ "Tooltip": item.get("tooltip"),
66
+ "Is Active": item.get("isActive"),
67
+ "Is Appliable": item.get("isAppliable"),
68
+ "Has Protection": item.get("hasProtection"),
69
+ "Parent Sensitivity Label Id": (
70
+ item.get("parent", {}).get("id") if item.get("parent") else None
71
+ ),
72
+ "Parent Sensitivity Label Name": (
73
+ item.get("parent", {}).get("name") if item.get("parent") else None
74
+ ),
75
+ }
76
+ rows.append(row)
77
+
78
+ if rows:
79
+ df = pd.DataFrame(rows, columns=list(columns.keys()))
80
+ _update_dataframe_datatypes(dataframe=df, column_map=columns)
81
+
82
+ return df
83
+
84
+
85
+ @log
86
+ def resolve_sensitivity_label_id(
87
+ label: str | UUID, user: Optional[str | UUID] = None
88
+ ) -> UUID | None:
89
+ """
90
+ Resolve a sensitivity label name or ID to its corresponding sensitivity label ID.
91
+
92
+ Service Principal Authentication is required (see `here <https://github.com/microsoft/semantic-link-labs/blob/main/notebooks/Service%20Principal.ipynb>`_ for examples).
93
+
94
+ Parameters
95
+ ----------
96
+ label : str | uuid.UUID
97
+ The name or ID of the sensitivity label.
98
+ user : str | uuid.UUID, default=None
99
+ The user ID or user principal name.
100
+
101
+ Returns
102
+ -------
103
+ uuid.UUID | None
104
+ The ID of the sensitivity label if found, otherwise None.
105
+ """
106
+
107
+ if _is_valid_uuid(label):
108
+ return str(label)
109
+
110
+ df = list_sensitivity_labels(user=user)
111
+
112
+ if df.empty:
113
+ return None
114
+
115
+ # Try to find the label by name
116
+ label_row = df[df["Sensitivity Label Name"] == label]
117
+ if not label_row.empty:
118
+ return label_row["Sensitivity Label Id"].iloc[0]
119
+
120
+ return None
@@ -23,7 +23,7 @@ def list_teams() -> pd.DataFrame:
23
23
  A pandas dataframe showing a list of teams and their properties.
24
24
  """
25
25
 
26
- result = _base_api(request="teams", client="graph").json()
26
+ result = _base_api(request="teams", client="graph", uses_pagination=True)
27
27
 
28
28
  columns = {
29
29
  "Team Id": "str",
@@ -43,23 +43,24 @@ def list_teams() -> pd.DataFrame:
43
43
  df = _create_dataframe(columns=columns)
44
44
 
45
45
  rows = []
46
- for v in result.get("value"):
47
- rows.append(
48
- {
49
- "Team Id": v.get("id"),
50
- "Team Name": v.get("displayName"),
51
- "Description": v.get("description"),
52
- "Creation Date Time": v.get("createdDateTime"),
53
- "Classification": v.get("classification"),
54
- "Specialization": v.get("specialization"),
55
- "Visibility": v.get("visibility"),
56
- "Web Url": v.get("webUrl"),
57
- "Archived": v.get("isArchived"),
58
- "Favorite By Me": v.get("isFavoriteByMe"),
59
- "Discoverable By Me": v.get("isDiscoverableByMe"),
60
- "Member Count": v.get("memberCount"),
61
- }
62
- )
46
+ for r in result:
47
+ for v in r.get("value", []):
48
+ rows.append(
49
+ {
50
+ "Team Id": v.get("id"),
51
+ "Team Name": v.get("displayName"),
52
+ "Description": v.get("description"),
53
+ "Creation Date Time": v.get("createdDateTime"),
54
+ "Classification": v.get("classification"),
55
+ "Specialization": v.get("specialization"),
56
+ "Visibility": v.get("visibility"),
57
+ "Web Url": v.get("webUrl"),
58
+ "Archived": v.get("isArchived"),
59
+ "Favorite By Me": v.get("isFavoriteByMe"),
60
+ "Discoverable By Me": v.get("isDiscoverableByMe"),
61
+ "Member Count": v.get("memberCount"),
62
+ }
63
+ )
63
64
 
64
65
  if rows:
65
66
  df = pd.DataFrame(rows, columns=list(columns.keys()))
@@ -0,0 +1,96 @@
1
+ from uuid import UUID
2
+ import sempy_labs._icons as icons
3
+ from typing import List, Optional
4
+ from sempy_labs._helper_functions import (
5
+ _base_api,
6
+ )
7
+ from sempy._utils._log import log
8
+ from sempy_labs.graph._users import resolve_user_id
9
+
10
+
11
+ @log
12
+ def add_user_license(
13
+ user: str | UUID, sku_id: UUID, disabled_plans: Optional[UUID | List[UUID]] = None
14
+ ):
15
+ """
16
+ Assigns a license to a user.
17
+
18
+ This is a wrapper function for the following API: `user: assignLicense <https://learn.microsoft.com/graph/api/user-assignlicense>`_.
19
+
20
+ Service Principal Authentication is required (see `here <https://github.com/microsoft/semantic-link-labs/blob/main/notebooks/Service%20Principal.ipynb>`_ for examples).
21
+
22
+ Parameters
23
+ ----------
24
+ user : str | uuid.UUID
25
+ The user ID or user principal name.
26
+ sku_id : uuid.UUID
27
+ The SKU ID of the license to assign.
28
+ disabled_plans : Optional[uuid.UUID | List[uuid.UUID]], default=None
29
+ A single service plan ID or a list of service plan IDs to disable within the assigned license.
30
+ """
31
+
32
+ user_id = resolve_user_id(user)
33
+
34
+ payload = {
35
+ "addLicenses": [
36
+ {
37
+ "skuId": sku_id,
38
+ },
39
+ ],
40
+ "removeLicenses": [],
41
+ }
42
+
43
+ if disabled_plans:
44
+ if isinstance(disabled_plans, str):
45
+ disabled_plans = [disabled_plans]
46
+ payload["addLicenses"][0]["disabledPlans"] = disabled_plans
47
+
48
+ _base_api(
49
+ request=f"users/{user_id}/assignLicense",
50
+ client="graph",
51
+ method="post",
52
+ payload=payload,
53
+ )
54
+
55
+ print(
56
+ f"{icons.green_dot} The '{sku_id}' license has been assigned to the user '{user}'."
57
+ )
58
+
59
+
60
+ @log
61
+ def remove_user_license(user: str | UUID, sku_ids: UUID | List[UUID]):
62
+ """
63
+ Removes a license from a user.
64
+
65
+ This is a wrapper function for the following API: `user: assignLicense <https://learn.microsoft.com/graph/api/user-assignlicense>`_.
66
+
67
+ Service Principal Authentication is required (see `here <https://github.com/microsoft/semantic-link-labs/blob/main/notebooks/Service%20Principal.ipynb>`_ for examples).
68
+
69
+ Parameters
70
+ ----------
71
+ user : str | uuid.UUID
72
+ The user ID or user principal name.
73
+ sku_id : uuid.UUID
74
+ The SKU ID of the license to remove.
75
+ """
76
+
77
+ user_id = resolve_user_id(user)
78
+
79
+ if isinstance(sku_ids, str):
80
+ sku_ids = [sku_ids]
81
+
82
+ payload = {
83
+ "addLicenses": [],
84
+ "removeLicenses": sku_ids,
85
+ }
86
+
87
+ _base_api(
88
+ request=f"users/{user_id}/assignLicense",
89
+ client="graph",
90
+ method="post",
91
+ payload=payload,
92
+ )
93
+
94
+ print(
95
+ f"{icons.green_dot} The '{', '.join([str(s) for s in sku_ids])}' license(s) have been removed from the user '{user}'."
96
+ )
@@ -8,6 +8,7 @@ from .._helper_functions import (
8
8
  _is_valid_uuid,
9
9
  _base_api,
10
10
  _create_dataframe,
11
+ _update_dataframe_datatypes,
11
12
  _mount,
12
13
  )
13
14
  from sempy._utils._log import log
@@ -91,7 +92,7 @@ def list_users() -> pd.DataFrame:
91
92
  A pandas dataframe showing a list of users and their properties.
92
93
  """
93
94
 
94
- result = _base_api(request="users", client="graph").json()
95
+ result = _base_api(request="users", client="graph", uses_pagination=True)
95
96
 
96
97
  columns = {
97
98
  "User Id": "string",
@@ -108,21 +109,27 @@ def list_users() -> pd.DataFrame:
108
109
 
109
110
  df = _create_dataframe(columns=columns)
110
111
 
111
- for v in result.get("value"):
112
- new_data = {
113
- "User Id": v.get("id"),
114
- "User Principal Name": v.get("userPrincipalName"),
115
- "User Name": v.get("displayName"),
116
- "Mail": v.get("mail"),
117
- "Job Title": v.get("jobTitle"),
118
- "Office Location": v.get("officeLocation"),
119
- "Mobile Phone": v.get("mobilePhone"),
120
- "Business Phones": str(v.get("businessPhones")),
121
- "Preferred Language": v.get("preferredLanguage"),
122
- "Surname": v.get("surname"),
123
- }
124
-
125
- df = pd.concat([df, pd.DataFrame(new_data, index=[0])], ignore_index=True)
112
+ rows = []
113
+ for r in result:
114
+ for v in r.get("value", []):
115
+ rows.append(
116
+ {
117
+ "User Id": v.get("id"),
118
+ "User Principal Name": v.get("userPrincipalName"),
119
+ "User Name": v.get("displayName"),
120
+ "Mail": v.get("mail"),
121
+ "Job Title": v.get("jobTitle"),
122
+ "Office Location": v.get("officeLocation"),
123
+ "Mobile Phone": v.get("mobilePhone"),
124
+ "Business Phones": str(v.get("businessPhones")),
125
+ "Preferred Language": v.get("preferredLanguage"),
126
+ "Surname": v.get("surname"),
127
+ }
128
+ )
129
+
130
+ if rows:
131
+ df = pd.DataFrame(rows, columns=list(columns.keys()))
132
+ _update_dataframe_datatypes(dataframe=df, column_map=columns)
126
133
 
127
134
  return df
128
135
 
@@ -33,6 +33,7 @@ def get_lakehouse_tables(
33
33
  extended: bool = False,
34
34
  count_rows: bool = False,
35
35
  export: bool = False,
36
+ exclude_shortcuts: bool = False,
36
37
  ) -> pd.DataFrame:
37
38
  """
38
39
  Shows the tables of a lakehouse and their respective properties. Option to include additional properties relevant to Direct Lake guardrails.
@@ -60,6 +61,8 @@ def get_lakehouse_tables(
60
61
  Obtains a row count for each lakehouse table.
61
62
  export : bool, default=False
62
63
  Exports the resulting dataframe to a delta table in the lakehouse.
64
+ exclude_shortcuts : bool, default=False
65
+ If True, excludes shortcuts.
63
66
 
64
67
  Returns
65
68
  -------
@@ -83,6 +86,9 @@ def get_lakehouse_tables(
83
86
  lakehouse=lakehouse, workspace=workspace_id
84
87
  )
85
88
 
89
+ # Test if valid lakehouse:
90
+ x = _base_api(f"v1/workspaces/{workspace_id}/lakehouses/{lakehouse_id}")
91
+
86
92
  if count_rows: # Setting countrows defaults to extended=True
87
93
  extended = True
88
94
 
@@ -94,7 +100,7 @@ def get_lakehouse_tables(
94
100
  client="fabric_sp",
95
101
  )
96
102
 
97
- except Exception as e:
103
+ except Exception:
98
104
  API_called = False
99
105
 
100
106
  rows = []
@@ -246,6 +252,32 @@ def get_lakehouse_tables(
246
252
  df["Row Count"] = df["Row Count"].astype(int)
247
253
  df["Row Count Guardrail Hit"] = df["Row Count"] > df["Row Count Guardrail"]
248
254
 
255
+ if exclude_shortcuts:
256
+ from sempy_labs.lakehouse._shortcuts import list_shortcuts
257
+
258
+ # Exclude shortcuts
259
+ shortcuts = (
260
+ list_shortcuts(lakehouse=lakehouse, workspace=workspace)
261
+ .query("`Shortcut Path`.str.startswith('/Tables')", engine="python")
262
+ .assign(
263
+ FullPath=lambda df: df["Shortcut Path"].str.rstrip("/")
264
+ + "/"
265
+ + df["Shortcut Name"]
266
+ )["FullPath"]
267
+ .tolist()
268
+ )
269
+
270
+ df["FullPath"] = df.apply(
271
+ lambda x: (
272
+ f"/Tables/{x['Table Name']}"
273
+ if pd.isna(x["Schema Name"]) or x["Schema Name"] == ""
274
+ else f"/Tables/{x['Schema Name']}/{x['Table Name']}"
275
+ ),
276
+ axis=1,
277
+ )
278
+
279
+ df = df[~df["FullPath"].isin(shortcuts)].reset_index(drop=True)
280
+
249
281
  if export:
250
282
  if not lakehouse_attached():
251
283
  raise ValueError(
@@ -93,7 +93,9 @@ def optimize_lakehouse_tables(
93
93
 
94
94
  from sempy_labs.lakehouse._get_lakehouse_tables import get_lakehouse_tables
95
95
 
96
- df = get_lakehouse_tables(lakehouse=lakehouse, workspace=workspace)
96
+ df = get_lakehouse_tables(
97
+ lakehouse=lakehouse, workspace=workspace, exclude_shortcuts=True
98
+ )
97
99
  df_delta = df[df["Format"] == "delta"]
98
100
 
99
101
  if isinstance(tables, str):
@@ -142,7 +144,9 @@ def vacuum_lakehouse_tables(
142
144
 
143
145
  from sempy_labs.lakehouse._get_lakehouse_tables import get_lakehouse_tables
144
146
 
145
- df = get_lakehouse_tables(lakehouse=lakehouse, workspace=workspace)
147
+ df = get_lakehouse_tables(
148
+ lakehouse=lakehouse, workspace=workspace, exclude_shortcuts=True
149
+ )
146
150
  df_delta = df[df["Format"] == "delta"]
147
151
 
148
152
  if isinstance(tables, str):