semantic-link-labs 0.7.1__py3-none-any.whl → 0.7.3__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 (35) hide show
  1. {semantic_link_labs-0.7.1.dist-info → semantic_link_labs-0.7.3.dist-info}/METADATA +3 -2
  2. {semantic_link_labs-0.7.1.dist-info → semantic_link_labs-0.7.3.dist-info}/RECORD +35 -28
  3. {semantic_link_labs-0.7.1.dist-info → semantic_link_labs-0.7.3.dist-info}/WHEEL +1 -1
  4. sempy_labs/__init__.py +60 -3
  5. sempy_labs/_bpa_translation/_translations_sv-SE.po +914 -0
  6. sempy_labs/_clear_cache.py +298 -3
  7. sempy_labs/_dataflows.py +130 -0
  8. sempy_labs/_deployment_pipelines.py +171 -0
  9. sempy_labs/_generate_semantic_model.py +148 -27
  10. sempy_labs/_git.py +380 -0
  11. sempy_labs/_helper_functions.py +57 -0
  12. sempy_labs/_list_functions.py +144 -121
  13. sempy_labs/_model_bpa.py +85 -83
  14. sempy_labs/_model_bpa_bulk.py +3 -1
  15. sempy_labs/_model_bpa_rules.py +788 -800
  16. sempy_labs/_query_scale_out.py +15 -3
  17. sempy_labs/_sql.py +96 -0
  18. sempy_labs/_translations.py +0 -1
  19. sempy_labs/_workspace_identity.py +66 -0
  20. sempy_labs/directlake/__init__.py +2 -0
  21. sempy_labs/directlake/_directlake_schema_compare.py +1 -2
  22. sempy_labs/directlake/_dl_helper.py +4 -7
  23. sempy_labs/directlake/_generate_shared_expression.py +85 -0
  24. sempy_labs/directlake/_show_unsupported_directlake_objects.py +1 -2
  25. sempy_labs/lakehouse/_get_lakehouse_tables.py +7 -3
  26. sempy_labs/migration/_migrate_calctables_to_lakehouse.py +5 -0
  27. sempy_labs/migration/_migrate_calctables_to_semantic_model.py +5 -0
  28. sempy_labs/migration/_migrate_model_objects_to_semantic_model.py +6 -2
  29. sempy_labs/migration/_migrate_tables_columns_to_semantic_model.py +6 -5
  30. sempy_labs/migration/_migration_validation.py +6 -0
  31. sempy_labs/report/_report_functions.py +21 -42
  32. sempy_labs/report/_report_rebind.py +5 -0
  33. sempy_labs/tom/_model.py +91 -52
  34. {semantic_link_labs-0.7.1.dist-info → semantic_link_labs-0.7.3.dist-info}/LICENSE +0 -0
  35. {semantic_link_labs-0.7.1.dist-info → semantic_link_labs-0.7.3.dist-info}/top_level.txt +0 -0
@@ -207,11 +207,18 @@ def set_qso(
207
207
  A pandas dataframe showing the current query scale-out settings.
208
208
  """
209
209
 
210
+ from sempy_labs._helper_functions import is_default_semantic_model
211
+
210
212
  # https://learn.microsoft.com/en-us/rest/api/power-bi/datasets/update-dataset-in-group
211
213
 
212
214
  (workspace, workspace_id) = resolve_workspace_name_and_id(workspace)
213
215
  dataset_id = resolve_dataset_id(dataset, workspace)
214
216
 
217
+ if is_default_semantic_model(dataset=dataset, workspace=workspace):
218
+ raise ValueError(
219
+ f"{icons.red_dot} The 'set_qso' function does not run against default semantic models."
220
+ )
221
+
215
222
  if max_read_only_replicas == 0:
216
223
  disable_qso(dataset=dataset, workspace=workspace)
217
224
  return
@@ -223,9 +230,14 @@ def set_qso(
223
230
  }
224
231
  }
225
232
 
226
- set_semantic_model_storage_format(
227
- dataset=dataset, storage_format="Large", workspace=workspace
228
- )
233
+ dfL = list_qso_settings(dataset=dataset, workspace=workspace)
234
+ storage_mode = dfL["Storage Mode"].iloc[0]
235
+
236
+ if storage_mode == "Small":
237
+ set_semantic_model_storage_format(
238
+ dataset=dataset, storage_format="Large", workspace=workspace
239
+ )
240
+
229
241
  client = fabric.PowerBIRestClient()
230
242
  response = client.patch(
231
243
  f"/v1.0/myorg/groups/{workspace_id}/datasets/{dataset_id}",
sempy_labs/_sql.py ADDED
@@ -0,0 +1,96 @@
1
+ import sempy.fabric as fabric
2
+ import pandas as pd
3
+ from typing import Optional, Union
4
+ from sempy._utils._log import log
5
+ import struct
6
+ import uuid
7
+ from itertools import chain, repeat
8
+ from sempy.fabric.exceptions import FabricHTTPException
9
+ from sempy_labs._helper_functions import resolve_warehouse_id
10
+
11
+
12
+ def bytes2mswin_bstr(value: bytes) -> bytes:
13
+ """Convert a sequence of bytes into a (MS-Windows) BSTR (as bytes).
14
+
15
+ See https://github.com/mkleehammer/pyodbc/issues/228#issuecomment-319190980
16
+ for the original code. It appears the input is converted to an
17
+ MS-Windows BSTR (in 'Little-endian' format).
18
+
19
+ See https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-dtyp\
20
+ /692a42a9-06ce-4394-b9bc-5d2a50440168
21
+ for more info on BSTR.
22
+
23
+ :param value: the sequence of bytes to convert
24
+ :return: the converted value (as a sequence of bytes)
25
+ """
26
+
27
+ encoded_bytes = bytes(chain.from_iterable(zip(value, repeat(0))))
28
+ return struct.pack("<i", len(encoded_bytes)) + encoded_bytes
29
+
30
+
31
+ class ConnectWarehouse:
32
+ def __init__(
33
+ self,
34
+ warehouse: str,
35
+ workspace: Optional[Union[str, uuid.UUID]] = None,
36
+ timeout: Optional[int] = None,
37
+ ):
38
+ from sempy.fabric._token_provider import SynapseTokenProvider
39
+ import pyodbc
40
+
41
+ workspace = fabric.resolve_workspace_name(workspace)
42
+ workspace_id = fabric.resolve_workspace_id(workspace)
43
+ warehouse_id = resolve_warehouse_id(warehouse=warehouse, workspace=workspace)
44
+
45
+ # get the TDS endpoint
46
+ client = fabric.FabricRestClient()
47
+ response = client.get(f"v1/workspaces/{workspace_id}/warehouses/{warehouse_id}")
48
+ if response.status_code != 200:
49
+ raise FabricHTTPException(response)
50
+ tds_endpoint = response.json().get("properties", {}).get("connectionString")
51
+
52
+ access_token = SynapseTokenProvider()()
53
+ tokenstruct = bytes2mswin_bstr(access_token.encode())
54
+ conn_str = f"DRIVER={{ODBC Driver 18 for SQL Server}};SERVER={tds_endpoint};DATABASE={warehouse};Encrypt=Yes;"
55
+
56
+ if timeout is not None:
57
+ conn_str += f"Connect Timeout={timeout};"
58
+
59
+ self.connection = pyodbc.connect(conn_str, attrs_before={1256: tokenstruct})
60
+
61
+ @log
62
+ def query(self, sql: str) -> pd.DataFrame:
63
+ """
64
+ Runs a SQL query against a Fabric Warehouse.
65
+
66
+ Parameters
67
+ ----------
68
+ sql : str
69
+ The SQL query.
70
+
71
+ Returns
72
+ -------
73
+ pandas.DataFrame
74
+ A pandas dataframe with the result of the SQL query.
75
+ """
76
+ cursor = None
77
+
78
+ try:
79
+ cursor = self.connection.cursor()
80
+ cursor.execute(sql)
81
+
82
+ return pd.DataFrame.from_records(
83
+ cursor.fetchall(), columns=[col[0] for col in cursor.description]
84
+ )
85
+ finally:
86
+ if cursor:
87
+ cursor.close()
88
+
89
+ def __enter__(self):
90
+ return self
91
+
92
+ def __exit__(self, type, value, traceback):
93
+ self.close()
94
+
95
+ def close(self):
96
+ self.connection.close()
@@ -32,7 +32,6 @@ def translate_semantic_model(
32
32
  -------
33
33
  pandas.DataFrame
34
34
  Shows a pandas dataframe which displays all of the translations in the semantic model.
35
-
36
35
  """
37
36
 
38
37
  from synapse.ml.services import Translate
@@ -0,0 +1,66 @@
1
+ import sempy.fabric as fabric
2
+ from sempy_labs._helper_functions import (
3
+ resolve_workspace_name_and_id,
4
+ lro,
5
+ )
6
+ from typing import Optional
7
+ import sempy_labs._icons as icons
8
+ from sempy.fabric.exceptions import FabricHTTPException
9
+
10
+
11
+ def provision_workspace_identity(workspace: Optional[str] = None):
12
+ """
13
+ Provisions a workspace identity for a workspace.
14
+
15
+ Parameters
16
+ ----------
17
+ workspace : str, default=None
18
+ The Fabric workspace name.
19
+ Defaults to None which resolves to the workspace of the attached lakehouse
20
+ or if no lakehouse attached, resolves to the workspace of the notebook.
21
+ """
22
+
23
+ # https://learn.microsoft.com/en-us/rest/api/fabric/core/workspaces/provision-identity?tabs=HTTP
24
+
25
+ workspace, workspace_id = resolve_workspace_name_and_id(workspace)
26
+
27
+ client = fabric.FabricRestClient()
28
+ response = client.post(f"/v1/workspaces/{workspace_id}/provisionIdentity")
29
+
30
+ if response.status_code not in [200, 202]:
31
+ raise FabricHTTPException(response)
32
+
33
+ lro(client, response)
34
+
35
+ print(
36
+ f"{icons.green_dot} A workspace identity has been provisioned for the '{workspace}' workspace."
37
+ )
38
+
39
+
40
+ def deprovision_workspace_identity(workspace: Optional[str] = None):
41
+ """
42
+ Deprovisions a workspace identity for a workspace.
43
+
44
+ Parameters
45
+ ----------
46
+ workspace : str, default=None
47
+ The Fabric workspace name.
48
+ Defaults to None which resolves to the workspace of the attached lakehouse
49
+ or if no lakehouse attached, resolves to the workspace of the notebook.
50
+ """
51
+
52
+ # https://learn.microsoft.com/en-us/rest/api/fabric/core/workspaces/deprovision-identity?tabs=HTTP
53
+
54
+ workspace, workspace_id = resolve_workspace_name_and_id(workspace)
55
+
56
+ client = fabric.FabricRestClient()
57
+ response = client.post(f"/v1/workspaces/{workspace_id}/deprovisionIdentity")
58
+
59
+ if response.status_code not in [200, 202]:
60
+ raise FabricHTTPException(response)
61
+
62
+ lro(client, response)
63
+
64
+ print(
65
+ f"{icons.green_dot} The workspace identity has been deprovisioned from the '{workspace}' workspace."
66
+ )
@@ -1,3 +1,4 @@
1
+ from sempy_labs.directlake._generate_shared_expression import generate_shared_expression
1
2
  from sempy_labs.directlake._directlake_schema_compare import direct_lake_schema_compare
2
3
  from sempy_labs.directlake._directlake_schema_sync import direct_lake_schema_sync
3
4
  from sempy_labs.directlake._dl_helper import (
@@ -31,6 +32,7 @@ from sempy_labs.directlake._warm_cache import (
31
32
  )
32
33
 
33
34
  __all__ = [
35
+ "generate_shared_expression",
34
36
  "direct_lake_schema_compare",
35
37
  "direct_lake_schema_sync",
36
38
  "check_fallback_reason",
@@ -6,7 +6,6 @@ from sempy_labs._helper_functions import (
6
6
  from IPython.display import display
7
7
  from sempy_labs.lakehouse import get_lakehouse_columns
8
8
  from sempy_labs.directlake._dl_helper import get_direct_lake_source
9
- from sempy_labs._list_functions import list_tables
10
9
  from typing import Optional
11
10
  import sempy_labs._icons as icons
12
11
  from sempy._utils._log import log
@@ -61,7 +60,7 @@ def direct_lake_schema_compare(
61
60
  f"{icons.red_dot} The '{dataset}' semantic model is not in Direct Lake mode."
62
61
  )
63
62
 
64
- dfT = list_tables(dataset, workspace)
63
+ dfT = fabric.list_tables(dataset=dataset, workspace=workspace)
65
64
  dfC = fabric.list_columns(dataset=dataset, workspace=workspace)
66
65
  lc = get_lakehouse_columns(lakehouse_name, lakehouse_workspace)
67
66
 
@@ -126,18 +126,15 @@ def generate_direct_lake_semantic_model(
126
126
  expr = get_shared_expression(lakehouse=lakehouse, workspace=lakehouse_workspace)
127
127
  dfD = fabric.list_datasets(workspace=workspace)
128
128
  dfD_filt = dfD[dfD["Dataset Name"] == dataset]
129
- dfD_filt_len = len(dfD_filt)
130
129
 
131
- if dfD_filt_len > 0 and overwrite is False:
130
+ if len(dfD_filt) > 0 and not overwrite:
132
131
  raise ValueError(
133
132
  f"{icons.red_dot} The '{dataset}' semantic model within the '{workspace}' workspace already exists. Overwrite is set to False so the new semantic model has not been created."
134
133
  )
135
- if dfD_filt_len > 0 and overwrite:
136
- print(
137
- f"{icons.warning} Overwriting the existing '{dataset}' semantic model within the '{workspace}' workspace."
138
- )
139
134
 
140
- create_blank_semantic_model(dataset=dataset, workspace=workspace)
135
+ create_blank_semantic_model(
136
+ dataset=dataset, workspace=workspace, overwrite=overwrite
137
+ )
141
138
 
142
139
  @retry(
143
140
  sleep_time=1,
@@ -0,0 +1,85 @@
1
+ import sempy.fabric as fabric
2
+ from sempy_labs._helper_functions import (
3
+ resolve_lakehouse_name,
4
+ resolve_lakehouse_id,
5
+ resolve_warehouse_id,
6
+ )
7
+ from typing import Optional
8
+ import sempy_labs._icons as icons
9
+ from sempy.fabric.exceptions import FabricHTTPException
10
+
11
+
12
+ def generate_shared_expression(
13
+ item_name: Optional[str] = None,
14
+ item_type: Optional[str] = "Lakehouse",
15
+ workspace: Optional[str] = None,
16
+ ) -> str:
17
+ """
18
+ Dynamically generates the M expression used by a Direct Lake model for a given lakehouse/warehouse.
19
+
20
+ Parameters
21
+ ----------
22
+ item_name : str, default=None
23
+ The Fabric lakehouse or warehouse name.
24
+ Defaults to None which resolves to the lakehouse attached to the notebook.
25
+ item_type : str, default="Lakehouse"
26
+ The Fabric item name. Valid options: 'Lakehouse', 'Warehouse'.
27
+ workspace : str, default=None
28
+ The Fabric workspace used by the item.
29
+ Defaults to None which resolves to the workspace of the attached lakehouse
30
+ or if no lakehouse attached, resolves to the workspace of the notebook.
31
+
32
+ Returns
33
+ -------
34
+ str
35
+ Shows the expression which can be used to connect a Direct Lake semantic model to its SQL Endpoint.
36
+ """
37
+
38
+ workspace = fabric.resolve_workspace_name(workspace)
39
+ workspace_id = fabric.resolve_workspace_id(workspace)
40
+ item_types = ["Lakehouse", "Warehouse"]
41
+ item_type = item_type.capitalize()
42
+ if item_type not in item_types:
43
+ raise ValueError(
44
+ f"{icons.red_dot} Invalid item type. Valid options: {item_types}."
45
+ )
46
+
47
+ if item_name is None:
48
+ item_id = fabric.get_lakehouse_id()
49
+ item_name = resolve_lakehouse_name(item_id, workspace)
50
+ elif item_name is not None and item_type == "Lakehouse":
51
+ item_id = resolve_lakehouse_id(lakehouse=item_name, workspace=workspace)
52
+ elif item_type == "Warehouse":
53
+ item_id = resolve_warehouse_id(warehouse=item_name, workspace=workspace)
54
+
55
+ client = fabric.FabricRestClient()
56
+ item_type_rest = f"{item_type.lower()}s"
57
+ response = client.get(f"/v1/workspaces/{workspace_id}/{item_type_rest}/{item_id}")
58
+ if response.status_code != 200:
59
+ raise FabricHTTPException(response)
60
+
61
+ if item_type == "Lakehouse":
62
+ prop = response.json()["properties"]["sqlEndpointProperties"]
63
+ sqlEPCS = prop["connectionString"]
64
+ sqlepid = prop["id"]
65
+ provStatus = prop["provisioningStatus"]
66
+ elif item_type == "Warehouse":
67
+ prop = response.json()["properties"]
68
+ sqlEPCS = prop["connectionString"]
69
+ sqlepid = item_id
70
+ provStatus = None
71
+
72
+ if provStatus == "InProgress":
73
+ raise ValueError(
74
+ f"{icons.red_dot} The SQL Endpoint for the '{item_name}' lakehouse within the '{workspace}' workspace has not yet been provisioned. Please wait until it has been provisioned."
75
+ )
76
+
77
+ sh = (
78
+ 'let\n\tdatabase = Sql.Database("'
79
+ + sqlEPCS
80
+ + '", "'
81
+ + sqlepid
82
+ + '")\nin\n\tdatabase'
83
+ )
84
+
85
+ return sh
@@ -1,6 +1,5 @@
1
1
  import sempy.fabric as fabric
2
2
  import pandas as pd
3
- from sempy_labs._list_functions import list_tables
4
3
  from sempy_labs._helper_functions import format_dax_object_name
5
4
  from typing import Optional, Tuple
6
5
  from sempy._utils._log import log
@@ -33,7 +32,7 @@ def show_unsupported_direct_lake_objects(
33
32
 
34
33
  workspace = fabric.resolve_workspace_name(workspace)
35
34
 
36
- dfT = list_tables(dataset, workspace)
35
+ dfT = fabric.list_tables(dataset=dataset, workspace=workspace)
37
36
  dfC = fabric.list_columns(dataset=dataset, workspace=workspace)
38
37
  dfR = fabric.list_relationships(dataset=dataset, workspace=workspace)
39
38
 
@@ -7,6 +7,7 @@ from sempy_labs._helper_functions import (
7
7
  resolve_lakehouse_id,
8
8
  resolve_lakehouse_name,
9
9
  resolve_workspace_name_and_id,
10
+ pagination,
10
11
  )
11
12
  from sempy_labs.directlake._guardrails import (
12
13
  get_sku_size,
@@ -52,8 +53,6 @@ def get_lakehouse_tables(
52
53
  Shows the tables/columns within a lakehouse and their properties.
53
54
  """
54
55
 
55
- from sempy_labs._helper_functions import pagination
56
-
57
56
  df = pd.DataFrame(
58
57
  columns=[
59
58
  "Workspace Name",
@@ -96,6 +95,9 @@ def get_lakehouse_tables(
96
95
 
97
96
  responses = pagination(client, response)
98
97
 
98
+ if not responses[0].get("data"):
99
+ return df
100
+
99
101
  dfs = []
100
102
  for r in responses:
101
103
  for i in r.get("data", []):
@@ -108,7 +110,9 @@ def get_lakehouse_tables(
108
110
  "Location": i.get("location"),
109
111
  }
110
112
  dfs.append(pd.DataFrame(new_data, index=[0]))
111
- df = pd.concat(dfs, ignore_index=True)
113
+
114
+ if dfs:
115
+ df = pd.concat(dfs, ignore_index=True)
112
116
 
113
117
  if extended:
114
118
  sku_value = get_sku_size(workspace)
@@ -52,6 +52,11 @@ def migrate_calc_tables_to_lakehouse(
52
52
  or if no lakehouse attached, resolves to the workspace of the notebook.
53
53
  """
54
54
 
55
+ if dataset == new_dataset:
56
+ raise ValueError(
57
+ f"{icons.red_dot} The 'dataset' and 'new_dataset' parameters are both set to '{dataset}'. These parameters must be set to different values."
58
+ )
59
+
55
60
  workspace = fabric.resolve_workspace_name(workspace)
56
61
 
57
62
  if new_dataset_workspace is None:
@@ -48,6 +48,11 @@ def migrate_calc_tables_to_semantic_model(
48
48
  or if no lakehouse attached, resolves to the workspace of the notebook.
49
49
  """
50
50
 
51
+ if dataset == new_dataset:
52
+ raise ValueError(
53
+ f"{icons.red_dot} The 'dataset' and 'new_dataset' parameters are both set to '{dataset}'. These parameters must be set to different values."
54
+ )
55
+
51
56
  workspace = fabric.resolve_workspace_name(workspace)
52
57
 
53
58
  if new_dataset_workspace is None:
@@ -1,7 +1,6 @@
1
1
  import sempy
2
2
  import sempy.fabric as fabric
3
3
  import re
4
- from sempy_labs._list_functions import list_tables
5
4
  from sempy_labs._helper_functions import (
6
5
  create_relationship_name,
7
6
  retry,
@@ -43,12 +42,17 @@ def migrate_model_objects_to_semantic_model(
43
42
  import Microsoft.AnalysisServices.Tabular as TOM
44
43
  import System
45
44
 
45
+ if dataset == new_dataset:
46
+ raise ValueError(
47
+ f"{icons.red_dot} The 'dataset' and 'new_dataset' parameters are both set to '{dataset}'. These parameters must be set to different values."
48
+ )
49
+
46
50
  workspace = fabric.resolve_workspace_name(workspace)
47
51
 
48
52
  if new_dataset_workspace is None:
49
53
  new_dataset_workspace = workspace
50
54
 
51
- dfT = list_tables(dataset, workspace)
55
+ dfT = fabric.list_tables(dataset=dataset, workspace=workspace)
52
56
  dfC = fabric.list_columns(dataset=dataset, workspace=workspace)
53
57
  dfM = fabric.list_measures(dataset=dataset, workspace=workspace)
54
58
  dfRole = fabric.get_roles(dataset=dataset, workspace=workspace)
@@ -1,8 +1,5 @@
1
1
  import sempy.fabric as fabric
2
2
  import pandas as pd
3
- import datetime
4
- import time
5
- from sempy_labs._list_functions import list_tables
6
3
  from sempy_labs.directlake._get_shared_expression import get_shared_expression
7
4
  from sempy_labs._helper_functions import resolve_lakehouse_name, retry
8
5
  from sempy_labs.lakehouse._lakehouse import lakehouse_attached
@@ -45,9 +42,13 @@ def migrate_tables_columns_to_semantic_model(
45
42
  The Fabric workspace used by the lakehouse.
46
43
  Defaults to None which resolves to the workspace of the attached lakehouse
47
44
  or if no lakehouse attached, resolves to the workspace of the notebook.
48
-
49
45
  """
50
46
 
47
+ if dataset == new_dataset:
48
+ raise ValueError(
49
+ f"{icons.red_dot} The 'dataset' and 'new_dataset' parameters are both set to '{dataset}'. These parameters must be set to different values."
50
+ )
51
+
51
52
  workspace = fabric.resolve_workspace_name(workspace)
52
53
 
53
54
  if new_dataset_workspace is None:
@@ -71,7 +72,7 @@ def migrate_tables_columns_to_semantic_model(
71
72
  shEx = get_shared_expression(lakehouse, lakehouse_workspace)
72
73
 
73
74
  dfC = fabric.list_columns(dataset=dataset, workspace=workspace)
74
- dfT = list_tables(dataset, workspace)
75
+ dfT = fabric.list_tables(dataset=dataset, workspace=workspace)
75
76
  dfT.rename(columns={"Type": "Table Type"}, inplace=True)
76
77
  dfC = pd.merge(
77
78
  dfC,
@@ -3,6 +3,7 @@ import pandas as pd
3
3
  from typing import Optional
4
4
  from sempy_labs._list_functions import list_semantic_model_objects
5
5
  from sempy._utils._log import log
6
+ import sempy_labs._icons as icons
6
7
 
7
8
 
8
9
  @log
@@ -36,6 +37,11 @@ def migration_validation(
36
37
  A pandas dataframe showing a list of objects and whether they were successfully migrated. Also shows the % of objects which were migrated successfully.
37
38
  """
38
39
 
40
+ if dataset == new_dataset:
41
+ raise ValueError(
42
+ f"{icons.red_dot} The 'dataset' and 'new_dataset' parameters are both set to '{dataset}'. These parameters must be set to different values."
43
+ )
44
+
39
45
  workspace = fabric.resolve_workspace_name(workspace)
40
46
  if new_dataset_workspace is None:
41
47
  new_dataset_workspace = workspace
@@ -3,24 +3,21 @@ import pandas as pd
3
3
  import json
4
4
  import os
5
5
  import time
6
- import base64
7
6
  import copy
8
7
  from anytree import Node, RenderTree
9
8
  from powerbiclient import Report
10
- from synapse.ml.services import Translate
11
9
  from pyspark.sql.functions import col, flatten
12
- from pyspark.sql import SparkSession
13
10
  from sempy_labs.report._generate_report import update_report_from_reportjson
14
11
  from sempy_labs.lakehouse._lakehouse import lakehouse_attached
15
12
  from sempy_labs._helper_functions import (
16
13
  generate_embedded_filter,
17
- resolve_dataset_name,
18
14
  resolve_report_id,
19
15
  resolve_lakehouse_name,
20
16
  language_validate,
21
17
  resolve_workspace_name_and_id,
22
18
  lro,
23
19
  _decode_b64,
20
+ resolve_dataset_id,
24
21
  )
25
22
  from typing import List, Optional, Union
26
23
  from sempy._utils._log import log
@@ -405,6 +402,7 @@ def clone_report(
405
402
  workspace: Optional[str] = None,
406
403
  target_workspace: Optional[str] = None,
407
404
  target_dataset: Optional[str] = None,
405
+ target_dataset_workspace: Optional[str] = None,
408
406
  ):
409
407
  """
410
408
  Clones a Power BI report.
@@ -426,6 +424,9 @@ def clone_report(
426
424
  target_dataset : str, default=None
427
425
  The name of the semantic model to be used by the cloned report.
428
426
  Defaults to None which resolves to the semantic model used by the initial report.
427
+ target_dataset_workspace : str, default=None
428
+ The workspace in which the semantic model to be used by the report resides.
429
+ Defaults to None which resolves to the semantic model used by the initial report.
429
430
  """
430
431
 
431
432
  # https://learn.microsoft.com/rest/api/power-bi/reports/clone-report-in-group
@@ -446,48 +447,25 @@ def clone_report(
446
447
  target_workspace = workspace
447
448
  target_workspace_id = workspace_id
448
449
  else:
449
- dfW = fabric.list_workspaces()
450
- dfW_filt = dfW[dfW["Name"] == target_workspace]
451
-
452
- if len(dfW_filt) == 0:
453
- raise ValueError(
454
- f"{icons.red_dot} The '{workspace}' is not a valid workspace."
455
- )
450
+ target_workspace_id = fabric.resolve_workspace_id(target_workspace)
456
451
 
457
- target_workspace_id = dfW_filt["Id"].iloc[0]
452
+ if target_dataset is not None:
453
+ if target_dataset_workspace is None:
454
+ target_dataset_workspace = workspace
455
+ target_dataset_id = resolve_dataset_id(target_dataset, target_dataset_workspace)
458
456
 
459
- if target_dataset is None:
460
- dfR = fabric.list_reports(workspace=target_workspace)
461
- dfR_filt = dfR[dfR["Name"] == report]
462
- target_dataset_id = dfR_filt["Dataset Id"].iloc[0]
463
- target_dataset = resolve_dataset_name(
464
- dataset_id=target_dataset_id, workspace=target_workspace
457
+ if report == cloned_report and workspace == target_workspace:
458
+ raise ValueError(
459
+ f"{icons.warning} The 'report' and 'cloned_report' parameters have the same value of '{report}. The 'workspace' and 'target_workspace' have the same value of '{workspace}'. Either the 'cloned_report' or the 'target_workspace' must be different from the original report."
465
460
  )
466
- else:
467
- dfD = fabric.list_datasets(workspace=target_workspace)
468
- dfD_filt = dfD[dfD["Dataset Name"] == target_dataset]
469
-
470
- if len(dfD_filt) == 0:
471
- raise ValueError(
472
- f"{icons.red_dot} The '{target_dataset}' target dataset does not exist in the '{target_workspace}' workspace."
473
- )
474
-
475
- target_dataset_id = dfD_filt["Dataset Id"].iloc[0]
476
461
 
477
462
  client = fabric.PowerBIRestClient()
478
463
 
479
- if target_workspace is None and target_dataset is None:
480
- request_body = {"name": cloned_report}
481
- elif target_workspace is not None and target_dataset is None:
482
- request_body = {"name": cloned_report, "targetWorkspaceId": target_workspace_id}
483
- elif target_workspace is not None and target_dataset is not None:
484
- request_body = {
485
- "name": cloned_report,
486
- "targetModelId": target_dataset_id,
487
- "targetWorkspaceId": target_workspace_id,
488
- }
489
- elif target_workspace is None and target_dataset is not None:
490
- request_body = {"name": cloned_report, "targetModelId": target_dataset_id}
464
+ request_body = {"name": cloned_report}
465
+ if target_dataset is not None:
466
+ request_body["targetModelId"] = target_dataset_id
467
+ if target_workspace != workspace:
468
+ request_body["targetWorkspaceId"] = target_workspace_id
491
469
 
492
470
  response = client.post(
493
471
  f"/v1.0/myorg/groups/{workspace_id}/reports/{reportId}/Clone", json=request_body
@@ -496,8 +474,7 @@ def clone_report(
496
474
  if response.status_code != 200:
497
475
  raise FabricHTTPException(response)
498
476
  print(
499
- f"{icons.green_dot} The '{report}' report has been successfully cloned as the '{cloned_report}' report within the"
500
- f" '{target_workspace}' workspace using the '{target_dataset}' semantic model."
477
+ f"{icons.green_dot} The '{report}' report has been successfully cloned as the '{cloned_report}' report within the '{target_workspace}' workspace."
501
478
  )
502
479
 
503
480
 
@@ -756,6 +733,8 @@ def translate_report_titles(
756
733
  Defaults to None which resolves to the workspace of the attached lakehouse
757
734
  or if no lakehouse attached, resolves to the workspace of the notebook.
758
735
  """
736
+ from synapse.ml.services import Translate
737
+ from pyspark.sql import SparkSession
759
738
 
760
739
  if isinstance(languages, str):
761
740
  languages = [languages]
@@ -101,6 +101,11 @@ def report_rebind_all(
101
101
 
102
102
  from sempy_labs._list_functions import list_reports_using_semantic_model
103
103
 
104
+ if dataset == new_dataset:
105
+ raise ValueError(
106
+ f"{icons.red_dot} The 'dataset' and 'new_dataset' parameters are both set to '{dataset}'. These parameters must be set to different values."
107
+ )
108
+
104
109
  dataset_workspace = fabric.resolve_workspace_name(dataset_workspace)
105
110
 
106
111
  if new_dataset_workpace is None: