semantic-link-labs 0.8.0__py3-none-any.whl → 0.8.2__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.0.dist-info → semantic_link_labs-0.8.2.dist-info}/METADATA +39 -7
- {semantic_link_labs-0.8.0.dist-info → semantic_link_labs-0.8.2.dist-info}/RECORD +47 -37
- sempy_labs/__init__.py +70 -51
- sempy_labs/_ai.py +0 -2
- sempy_labs/_capacity_migration.py +1 -2
- sempy_labs/_data_pipelines.py +118 -0
- sempy_labs/_documentation.py +144 -0
- sempy_labs/_eventhouses.py +118 -0
- sempy_labs/_eventstreams.py +118 -0
- sempy_labs/_generate_semantic_model.py +3 -3
- sempy_labs/_git.py +3 -3
- sempy_labs/_helper_functions.py +117 -26
- sempy_labs/_icons.py +21 -0
- sempy_labs/_kql_databases.py +134 -0
- sempy_labs/_kql_querysets.py +124 -0
- sempy_labs/_list_functions.py +12 -425
- sempy_labs/_mirrored_warehouses.py +50 -0
- sempy_labs/_ml_experiments.py +122 -0
- sempy_labs/_ml_models.py +120 -0
- sempy_labs/_model_auto_build.py +0 -4
- sempy_labs/_model_bpa.py +11 -11
- sempy_labs/_model_bpa_bulk.py +8 -7
- sempy_labs/_model_dependencies.py +26 -18
- sempy_labs/_notebooks.py +5 -16
- sempy_labs/_query_scale_out.py +2 -2
- sempy_labs/_refresh_semantic_model.py +7 -19
- sempy_labs/_spark.py +10 -10
- sempy_labs/_vertipaq.py +16 -18
- sempy_labs/_warehouses.py +132 -0
- sempy_labs/_workspaces.py +0 -3
- sempy_labs/admin/_basic_functions.py +92 -10
- sempy_labs/admin/_domains.py +1 -1
- sempy_labs/directlake/_directlake_schema_sync.py +1 -1
- sempy_labs/directlake/_dl_helper.py +32 -16
- sempy_labs/directlake/_guardrails.py +7 -7
- sempy_labs/directlake/_update_directlake_partition_entity.py +1 -1
- sempy_labs/directlake/_warm_cache.py +1 -1
- sempy_labs/lakehouse/_get_lakehouse_tables.py +3 -3
- sempy_labs/lakehouse/_lakehouse.py +3 -2
- sempy_labs/migration/_migrate_calctables_to_lakehouse.py +5 -0
- sempy_labs/report/_generate_report.py +1 -1
- sempy_labs/report/_report_bpa.py +13 -3
- sempy_labs/report/_reportwrapper.py +14 -16
- sempy_labs/tom/_model.py +261 -24
- {semantic_link_labs-0.8.0.dist-info → semantic_link_labs-0.8.2.dist-info}/LICENSE +0 -0
- {semantic_link_labs-0.8.0.dist-info → semantic_link_labs-0.8.2.dist-info}/WHEEL +0 -0
- {semantic_link_labs-0.8.0.dist-info → semantic_link_labs-0.8.2.dist-info}/top_level.txt +0 -0
|
@@ -7,6 +7,7 @@ from sempy_labs._helper_functions import (
|
|
|
7
7
|
_extract_json,
|
|
8
8
|
_add_part,
|
|
9
9
|
lro,
|
|
10
|
+
make_clickable,
|
|
10
11
|
)
|
|
11
12
|
from typing import Optional, List
|
|
12
13
|
import pandas as pd
|
|
@@ -134,21 +135,17 @@ class ReportWrapper:
|
|
|
134
135
|
|
|
135
136
|
def get_theme(self, theme_type: str = "baseTheme") -> dict:
|
|
136
137
|
"""
|
|
137
|
-
Obtains
|
|
138
|
+
Obtains the theme file of the report.
|
|
138
139
|
|
|
139
140
|
Parameters
|
|
140
141
|
----------
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
workspace : str, default=None
|
|
144
|
-
The Fabric workspace name.
|
|
145
|
-
Defaults to None which resolves to the workspace of the attached lakehouse
|
|
146
|
-
or if no lakehouse attached, resolves to the workspace of the notebook.
|
|
142
|
+
theme_type : str, default="baseTheme"
|
|
143
|
+
The theme type. Options: "baseTheme", "customTheme".
|
|
147
144
|
|
|
148
145
|
Returns
|
|
149
146
|
-------
|
|
150
|
-
|
|
151
|
-
|
|
147
|
+
dict
|
|
148
|
+
The theme.json file
|
|
152
149
|
"""
|
|
153
150
|
|
|
154
151
|
theme_types = ["baseTheme", "customTheme"]
|
|
@@ -362,8 +359,8 @@ class ReportWrapper:
|
|
|
362
359
|
ignore_index=True,
|
|
363
360
|
)
|
|
364
361
|
|
|
365
|
-
df["Page URL"] = (
|
|
366
|
-
f"{helper.get_web_url(report=self._report, workspace=self._workspace)}/{
|
|
362
|
+
df["Page URL"] = df["Page Name"].apply(
|
|
363
|
+
lambda page_name: f"{helper.get_web_url(report=self._report, workspace=self._workspace)}/{page_name}"
|
|
367
364
|
)
|
|
368
365
|
|
|
369
366
|
bool_cols = ["Hidden", "Locked", "Used"]
|
|
@@ -373,6 +370,7 @@ class ReportWrapper:
|
|
|
373
370
|
df = self._add_extended(dataframe=df)
|
|
374
371
|
|
|
375
372
|
return df
|
|
373
|
+
# return df.style.format({"Page URL": make_clickable})
|
|
376
374
|
|
|
377
375
|
def list_visual_filters(self, extended: bool = False) -> pd.DataFrame:
|
|
378
376
|
"""
|
|
@@ -639,11 +637,12 @@ class ReportWrapper:
|
|
|
639
637
|
bool_cols = ["Hidden", "Active", "Drillthrough Target Page"]
|
|
640
638
|
df[bool_cols] = df[bool_cols].astype(bool)
|
|
641
639
|
|
|
642
|
-
df["Page URL"] = (
|
|
643
|
-
f"{helper.get_web_url(report=self._report, workspace=self._workspace)}/{
|
|
640
|
+
df["Page URL"] = df["Page Name"].apply(
|
|
641
|
+
lambda page_name: f"{helper.get_web_url(report=self._report, workspace=self._workspace)}/{page_name}"
|
|
644
642
|
)
|
|
645
643
|
|
|
646
644
|
return df
|
|
645
|
+
# return df.style.format({"Page URL": make_clickable})
|
|
647
646
|
|
|
648
647
|
def list_visuals(self) -> pd.DataFrame:
|
|
649
648
|
"""
|
|
@@ -1306,9 +1305,8 @@ class ReportWrapper:
|
|
|
1306
1305
|
----------
|
|
1307
1306
|
theme_file_path : str
|
|
1308
1307
|
The file path of the theme.json file. This can either be from a Fabric lakehouse or from the web.
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
file_path = 'https://raw.githubusercontent.com/PowerBiDevCamp/FabricUserApiDemo/main/FabricUserApiDemo/DefinitionTemplates/Shared/Reports/StaticResources/SharedResources/BaseThemes/CY23SU08.json'
|
|
1308
|
+
Example for lakehouse: file_path = '/lakehouse/default/Files/CY23SU09.json'
|
|
1309
|
+
Example for web url: file_path = 'https://raw.githubusercontent.com/PowerBiDevCamp/FabricUserApiDemo/main/FabricUserApiDemo/DefinitionTemplates/Shared/Reports/StaticResources/SharedResources/BaseThemes/CY23SU08.json'
|
|
1312
1310
|
"""
|
|
1313
1311
|
|
|
1314
1312
|
import requests
|
sempy_labs/tom/_model.py
CHANGED
|
@@ -3,7 +3,10 @@ import sempy.fabric as fabric
|
|
|
3
3
|
import pandas as pd
|
|
4
4
|
import re
|
|
5
5
|
from datetime import datetime
|
|
6
|
-
from sempy_labs._helper_functions import
|
|
6
|
+
from sempy_labs._helper_functions import (
|
|
7
|
+
format_dax_object_name,
|
|
8
|
+
generate_guid,
|
|
9
|
+
)
|
|
7
10
|
from sempy_labs._list_functions import list_relationships
|
|
8
11
|
from sempy_labs._refresh_semantic_model import refresh_semantic_model
|
|
9
12
|
from sempy_labs.directlake._dl_helper import check_fallback_reason
|
|
@@ -11,6 +14,7 @@ from contextlib import contextmanager
|
|
|
11
14
|
from typing import List, Iterator, Optional, Union, TYPE_CHECKING
|
|
12
15
|
from sempy._utils._log import log
|
|
13
16
|
import sempy_labs._icons as icons
|
|
17
|
+
from sempy.fabric.exceptions import FabricHTTPException
|
|
14
18
|
|
|
15
19
|
if TYPE_CHECKING:
|
|
16
20
|
import Microsoft.AnalysisServices.Tabular
|
|
@@ -226,7 +230,7 @@ class TOMWrapper:
|
|
|
226
230
|
measure_name: str,
|
|
227
231
|
expression: str,
|
|
228
232
|
format_string: Optional[str] = None,
|
|
229
|
-
hidden:
|
|
233
|
+
hidden: bool = False,
|
|
230
234
|
description: Optional[str] = None,
|
|
231
235
|
display_folder: Optional[str] = None,
|
|
232
236
|
format_string_expression: Optional[str] = None,
|
|
@@ -283,8 +287,12 @@ class TOMWrapper:
|
|
|
283
287
|
obj.FormatStringDefinition = fsd
|
|
284
288
|
if lineage_tag is not None:
|
|
285
289
|
obj.LineageTag = lineage_tag
|
|
290
|
+
else:
|
|
291
|
+
obj.LineageTag = generate_guid()
|
|
286
292
|
if source_lineage_tag is not None:
|
|
287
293
|
obj.SourceLineageTag = source_lineage_tag
|
|
294
|
+
else:
|
|
295
|
+
obj.SourceLineageTag = generate_guid()
|
|
288
296
|
if detail_rows_expression is not None:
|
|
289
297
|
drd = TOM.DetailRowsDefinition()
|
|
290
298
|
drd.Expression = detail_rows_expression
|
|
@@ -301,11 +309,11 @@ class TOMWrapper:
|
|
|
301
309
|
source_column: str,
|
|
302
310
|
data_type: str,
|
|
303
311
|
format_string: Optional[str] = None,
|
|
304
|
-
hidden:
|
|
312
|
+
hidden: bool = False,
|
|
305
313
|
description: Optional[str] = None,
|
|
306
314
|
display_folder: Optional[str] = None,
|
|
307
315
|
data_category: Optional[str] = None,
|
|
308
|
-
key:
|
|
316
|
+
key: bool = False,
|
|
309
317
|
summarize_by: Optional[str] = None,
|
|
310
318
|
lineage_tag: Optional[str] = None,
|
|
311
319
|
source_lineage_tag: Optional[str] = None,
|
|
@@ -376,8 +384,12 @@ class TOMWrapper:
|
|
|
376
384
|
obj.DataCategory = data_category
|
|
377
385
|
if lineage_tag is not None:
|
|
378
386
|
obj.LineageTag = lineage_tag
|
|
387
|
+
else:
|
|
388
|
+
obj.LineageTag = generate_guid()
|
|
379
389
|
if source_lineage_tag is not None:
|
|
380
390
|
obj.SourceLineageTag = source_lineage_tag
|
|
391
|
+
else:
|
|
392
|
+
obj.SourceLineageTag = generate_guid()
|
|
381
393
|
self.model.Tables[table_name].Columns.Add(obj)
|
|
382
394
|
|
|
383
395
|
def add_data_column(
|
|
@@ -387,11 +399,11 @@ class TOMWrapper:
|
|
|
387
399
|
source_column: str,
|
|
388
400
|
data_type: str,
|
|
389
401
|
format_string: Optional[str] = None,
|
|
390
|
-
hidden:
|
|
402
|
+
hidden: bool = False,
|
|
391
403
|
description: Optional[str] = None,
|
|
392
404
|
display_folder: Optional[str] = None,
|
|
393
405
|
data_category: Optional[str] = None,
|
|
394
|
-
key:
|
|
406
|
+
key: bool = False,
|
|
395
407
|
summarize_by: Optional[str] = None,
|
|
396
408
|
lineage_tag: Optional[str] = None,
|
|
397
409
|
source_lineage_tag: Optional[str] = None,
|
|
@@ -462,8 +474,12 @@ class TOMWrapper:
|
|
|
462
474
|
obj.DataCategory = data_category
|
|
463
475
|
if lineage_tag is not None:
|
|
464
476
|
obj.LineageTag = lineage_tag
|
|
477
|
+
else:
|
|
478
|
+
obj.LineageTag = generate_guid()
|
|
465
479
|
if source_lineage_tag is not None:
|
|
466
480
|
obj.SourceLineageTag = source_lineage_tag
|
|
481
|
+
else:
|
|
482
|
+
obj.SourceLineagetTag = generate_guid()
|
|
467
483
|
self.model.Tables[table_name].Columns.Add(obj)
|
|
468
484
|
|
|
469
485
|
def add_calculated_column(
|
|
@@ -473,11 +489,11 @@ class TOMWrapper:
|
|
|
473
489
|
expression: str,
|
|
474
490
|
data_type: str,
|
|
475
491
|
format_string: Optional[str] = None,
|
|
476
|
-
hidden:
|
|
492
|
+
hidden: bool = False,
|
|
477
493
|
description: Optional[str] = None,
|
|
478
494
|
display_folder: Optional[str] = None,
|
|
479
495
|
data_category: Optional[str] = None,
|
|
480
|
-
key:
|
|
496
|
+
key: bool = False,
|
|
481
497
|
summarize_by: Optional[str] = None,
|
|
482
498
|
lineage_tag: Optional[str] = None,
|
|
483
499
|
source_lineage_tag: Optional[str] = None,
|
|
@@ -548,8 +564,12 @@ class TOMWrapper:
|
|
|
548
564
|
obj.DataCategory = data_category
|
|
549
565
|
if lineage_tag is not None:
|
|
550
566
|
obj.LineageTag = lineage_tag
|
|
567
|
+
else:
|
|
568
|
+
obj.LineageTag = generate_guid()
|
|
551
569
|
if source_lineage_tag is not None:
|
|
552
570
|
obj.SourceLineageTag = source_lineage_tag
|
|
571
|
+
else:
|
|
572
|
+
obj.SourceLineagetTag = generate_guid()
|
|
553
573
|
self.model.Tables[table_name].Columns.Add(obj)
|
|
554
574
|
|
|
555
575
|
def add_calculation_item(
|
|
@@ -708,7 +728,7 @@ class TOMWrapper:
|
|
|
708
728
|
columns: List[str],
|
|
709
729
|
levels: Optional[List[str]] = None,
|
|
710
730
|
hierarchy_description: Optional[str] = None,
|
|
711
|
-
hierarchy_hidden:
|
|
731
|
+
hierarchy_hidden: bool = False,
|
|
712
732
|
lineage_tag: Optional[str] = None,
|
|
713
733
|
source_lineage_tag: Optional[str] = None,
|
|
714
734
|
):
|
|
@@ -761,8 +781,12 @@ class TOMWrapper:
|
|
|
761
781
|
obj.Description = hierarchy_description
|
|
762
782
|
if lineage_tag is not None:
|
|
763
783
|
obj.LineageTag = lineage_tag
|
|
784
|
+
else:
|
|
785
|
+
obj.LineageTag = generate_guid()
|
|
764
786
|
if source_lineage_tag is not None:
|
|
765
787
|
obj.SourceLineageTag = source_lineage_tag
|
|
788
|
+
else:
|
|
789
|
+
obj.SourceLineagetTag = generate_guid()
|
|
766
790
|
self.model.Tables[table_name].Hierarchies.Add(obj)
|
|
767
791
|
|
|
768
792
|
for col in columns:
|
|
@@ -770,6 +794,8 @@ class TOMWrapper:
|
|
|
770
794
|
lvl.Column = self.model.Tables[table_name].Columns[col]
|
|
771
795
|
lvl.Name = levels[columns.index(col)]
|
|
772
796
|
lvl.Ordinal = columns.index(col)
|
|
797
|
+
lvl.LineageTag = generate_guid()
|
|
798
|
+
lvl.SourceLineageTag = generate_guid()
|
|
773
799
|
self.model.Tables[table_name].Hierarchies[hierarchy_name].Levels.Add(lvl)
|
|
774
800
|
|
|
775
801
|
def add_relationship(
|
|
@@ -781,9 +807,9 @@ class TOMWrapper:
|
|
|
781
807
|
from_cardinality: str,
|
|
782
808
|
to_cardinality: str,
|
|
783
809
|
cross_filtering_behavior: Optional[str] = None,
|
|
784
|
-
is_active:
|
|
810
|
+
is_active: bool = True,
|
|
785
811
|
security_filtering_behavior: Optional[str] = None,
|
|
786
|
-
rely_on_referential_integrity:
|
|
812
|
+
rely_on_referential_integrity: bool = False,
|
|
787
813
|
):
|
|
788
814
|
"""
|
|
789
815
|
Adds a `relationship <https://learn.microsoft.com/dotnet/api/microsoft.analysisservices.tabular.singlecolumnrelationship?view=analysisservices-dotnet>`_ to a semantic model.
|
|
@@ -855,7 +881,7 @@ class TOMWrapper:
|
|
|
855
881
|
name: str,
|
|
856
882
|
precedence: int,
|
|
857
883
|
description: Optional[str] = None,
|
|
858
|
-
hidden:
|
|
884
|
+
hidden: bool = False,
|
|
859
885
|
):
|
|
860
886
|
"""
|
|
861
887
|
Adds a `calculation group <https://learn.microsoft.com/dotnet/api/microsoft.analysisservices.tabular.calculationgroup?view=analysisservices-dotnet>`_ to a semantic model.
|
|
@@ -939,8 +965,12 @@ class TOMWrapper:
|
|
|
939
965
|
exp.Description = description
|
|
940
966
|
if lineage_tag is not None:
|
|
941
967
|
exp.LineageTag = lineage_tag
|
|
968
|
+
else:
|
|
969
|
+
exp.LineageTag = generate_guid()
|
|
942
970
|
if source_lineage_tag is not None:
|
|
943
971
|
exp.SourceLineageTag = source_lineage_tag
|
|
972
|
+
else:
|
|
973
|
+
exp.SourceLineageTag = generate_guid()
|
|
944
974
|
exp.Kind = TOM.ExpressionKind.M
|
|
945
975
|
exp.Expression = expression
|
|
946
976
|
|
|
@@ -1034,7 +1064,7 @@ class TOMWrapper:
|
|
|
1034
1064
|
entity_name: str,
|
|
1035
1065
|
expression: Optional[str] = None,
|
|
1036
1066
|
description: Optional[str] = None,
|
|
1037
|
-
schema_name:
|
|
1067
|
+
schema_name: str = "dbo",
|
|
1038
1068
|
):
|
|
1039
1069
|
"""
|
|
1040
1070
|
Adds an entity partition to a table within a semantic model.
|
|
@@ -1050,7 +1080,7 @@ class TOMWrapper:
|
|
|
1050
1080
|
Defaults to None which resolves to the 'DatabaseQuery' expression.
|
|
1051
1081
|
description : str, default=None
|
|
1052
1082
|
A description for the partition.
|
|
1053
|
-
schema_name : str, default=
|
|
1083
|
+
schema_name : str, default="dbo"
|
|
1054
1084
|
The schema name.
|
|
1055
1085
|
"""
|
|
1056
1086
|
import Microsoft.AnalysisServices.Tabular as TOM
|
|
@@ -1062,8 +1092,7 @@ class TOMWrapper:
|
|
|
1062
1092
|
ep.ExpressionSource = self.model.Expressions["DatabaseQuery"]
|
|
1063
1093
|
else:
|
|
1064
1094
|
ep.ExpressionSource = self.model.Expressions[expression]
|
|
1065
|
-
|
|
1066
|
-
ep.SchemaName = schema_name
|
|
1095
|
+
ep.SchemaName = schema_name
|
|
1067
1096
|
p = TOM.Partition()
|
|
1068
1097
|
p.Name = table_name
|
|
1069
1098
|
p.Source = ep
|
|
@@ -1072,6 +1101,9 @@ class TOMWrapper:
|
|
|
1072
1101
|
p.Description = description
|
|
1073
1102
|
|
|
1074
1103
|
self.model.Tables[table_name].Partitions.Add(p)
|
|
1104
|
+
self.model.Tables[table_name].SourceLineageTag = (
|
|
1105
|
+
f"[{schema_name}].[{entity_name}]"
|
|
1106
|
+
)
|
|
1075
1107
|
|
|
1076
1108
|
def set_alternate_of(
|
|
1077
1109
|
self,
|
|
@@ -2483,7 +2515,7 @@ class TOMWrapper:
|
|
|
2483
2515
|
)
|
|
2484
2516
|
|
|
2485
2517
|
def set_is_available_in_mdx(
|
|
2486
|
-
self, table_name: str, column_name: str, value:
|
|
2518
|
+
self, table_name: str, column_name: str, value: bool = False
|
|
2487
2519
|
):
|
|
2488
2520
|
"""
|
|
2489
2521
|
Sets the `IsAvailableInMDX <https://learn.microsoft.com/dotnet/api/microsoft.analysisservices.tabular.column.isavailableinmdx?view=analysisservices-dotnet#microsoft-analysisservices-tabular-column-isavailableinmdx>`_ property on a column.
|
|
@@ -2586,7 +2618,7 @@ class TOMWrapper:
|
|
|
2586
2618
|
name: str,
|
|
2587
2619
|
description: Optional[str] = None,
|
|
2588
2620
|
data_category: Optional[str] = None,
|
|
2589
|
-
hidden:
|
|
2621
|
+
hidden: bool = False,
|
|
2590
2622
|
lineage_tag: Optional[str] = None,
|
|
2591
2623
|
source_lineage_tag: Optional[str] = None,
|
|
2592
2624
|
):
|
|
@@ -2618,8 +2650,12 @@ class TOMWrapper:
|
|
|
2618
2650
|
t.DataCategory = data_category
|
|
2619
2651
|
if lineage_tag is not None:
|
|
2620
2652
|
t.LineageTag = lineage_tag
|
|
2653
|
+
else:
|
|
2654
|
+
t.LineageTag = generate_guid()
|
|
2621
2655
|
if source_lineage_tag is not None:
|
|
2622
2656
|
t.SourceLineageTag = source_lineage_tag
|
|
2657
|
+
else:
|
|
2658
|
+
t.SourceLineagetTag = generate_guid()
|
|
2623
2659
|
t.Hidden = hidden
|
|
2624
2660
|
self.model.Tables.Add(t)
|
|
2625
2661
|
|
|
@@ -2629,9 +2665,9 @@ class TOMWrapper:
|
|
|
2629
2665
|
expression: str,
|
|
2630
2666
|
description: Optional[str] = None,
|
|
2631
2667
|
data_category: Optional[str] = None,
|
|
2632
|
-
hidden:
|
|
2633
|
-
lineage_tag: Optional[
|
|
2634
|
-
source_lineage_tag: Optional[
|
|
2668
|
+
hidden: bool = False,
|
|
2669
|
+
lineage_tag: Optional[str] = None,
|
|
2670
|
+
source_lineage_tag: Optional[str] = None,
|
|
2635
2671
|
):
|
|
2636
2672
|
"""
|
|
2637
2673
|
Adds a calculated table to the semantic model.
|
|
@@ -2670,8 +2706,12 @@ class TOMWrapper:
|
|
|
2670
2706
|
t.DataCategory = data_category
|
|
2671
2707
|
if lineage_tag is not None:
|
|
2672
2708
|
t.LineageTag = lineage_tag
|
|
2709
|
+
else:
|
|
2710
|
+
t.LineageTag = generate_guid()
|
|
2673
2711
|
if source_lineage_tag is not None:
|
|
2674
2712
|
t.SourceLineageTag = source_lineage_tag
|
|
2713
|
+
else:
|
|
2714
|
+
t.SourceLineagetTag = generate_guid()
|
|
2675
2715
|
t.Hidden = hidden
|
|
2676
2716
|
t.Partitions.Add(par)
|
|
2677
2717
|
self.model.Tables.Add(t)
|
|
@@ -3378,7 +3418,7 @@ class TOMWrapper:
|
|
|
3378
3418
|
incremental_periods: int,
|
|
3379
3419
|
rolling_window_granularity: str,
|
|
3380
3420
|
rolling_window_periods: int,
|
|
3381
|
-
only_refresh_complete_days:
|
|
3421
|
+
only_refresh_complete_days: bool = False,
|
|
3382
3422
|
detect_data_changes_column: Optional[str] = None,
|
|
3383
3423
|
):
|
|
3384
3424
|
"""
|
|
@@ -3483,7 +3523,7 @@ class TOMWrapper:
|
|
|
3483
3523
|
incremental_periods: int,
|
|
3484
3524
|
rolling_window_granularity: str,
|
|
3485
3525
|
rolling_window_periods: int,
|
|
3486
|
-
only_refresh_complete_days:
|
|
3526
|
+
only_refresh_complete_days: bool = False,
|
|
3487
3527
|
detect_data_changes_column: Optional[str] = None,
|
|
3488
3528
|
):
|
|
3489
3529
|
"""
|
|
@@ -3652,7 +3692,7 @@ class TOMWrapper:
|
|
|
3652
3692
|
self,
|
|
3653
3693
|
table_name: str,
|
|
3654
3694
|
effective_date: Optional[datetime] = None,
|
|
3655
|
-
refresh:
|
|
3695
|
+
refresh: bool = True,
|
|
3656
3696
|
max_parallelism: Optional[int] = 0,
|
|
3657
3697
|
):
|
|
3658
3698
|
"""
|
|
@@ -3970,6 +4010,8 @@ class TOMWrapper:
|
|
|
3970
4010
|
data_category: Optional[str] = None,
|
|
3971
4011
|
key: Optional[bool] = None,
|
|
3972
4012
|
summarize_by: Optional[str] = None,
|
|
4013
|
+
is_nullable: Optional[bool] = None,
|
|
4014
|
+
is_available_in_mdx: Optional[bool] = None,
|
|
3973
4015
|
):
|
|
3974
4016
|
"""
|
|
3975
4017
|
Updates a column within a semantic model.
|
|
@@ -4010,6 +4052,10 @@ class TOMWrapper:
|
|
|
4010
4052
|
summarize_by : str, default=None
|
|
4011
4053
|
Sets the value for the Summarize By property of the column.
|
|
4012
4054
|
Defaults to None which keeps the existing setting.
|
|
4055
|
+
is_nullable : bool, default=None
|
|
4056
|
+
If False, the column cannot contain nulls. Even if True, it may still not allow nulls if it's a key column.
|
|
4057
|
+
is_available_in_mdx : bool, default=None
|
|
4058
|
+
A boolean value that indicates whether the column can be excluded from usage in MDX query tools. False if the column can be excluded from usage in MDX query tools; otherwise true.
|
|
4013
4059
|
"""
|
|
4014
4060
|
|
|
4015
4061
|
import Microsoft.AnalysisServices.Tabular as TOM
|
|
@@ -4038,6 +4084,10 @@ class TOMWrapper:
|
|
|
4038
4084
|
c.DataCategory = data_category
|
|
4039
4085
|
if summarize_by is not None:
|
|
4040
4086
|
c.SummarizeBy = System.Enum.Parse(TOM.AggregateFunction, summarize_by)
|
|
4087
|
+
if is_nullable is not None:
|
|
4088
|
+
c.IsNullable = is_nullable
|
|
4089
|
+
if is_available_in_mdx is not None:
|
|
4090
|
+
c.IsAvailableInMDX = is_available_in_mdx
|
|
4041
4091
|
|
|
4042
4092
|
def update_role(
|
|
4043
4093
|
self,
|
|
@@ -4160,6 +4210,28 @@ class TOMWrapper:
|
|
|
4160
4210
|
|
|
4161
4211
|
self.model.Tables[table_name].Columns[column_name].SortByColumn = None
|
|
4162
4212
|
|
|
4213
|
+
def is_calculated_column(self, table_name: str, column_name: str):
|
|
4214
|
+
"""
|
|
4215
|
+
Identifies if a column is a calculated column.
|
|
4216
|
+
|
|
4217
|
+
Parameters
|
|
4218
|
+
----------
|
|
4219
|
+
table_name : str
|
|
4220
|
+
Name of the table in which the column resides.
|
|
4221
|
+
column_name : str
|
|
4222
|
+
Name of the column.
|
|
4223
|
+
|
|
4224
|
+
Returns
|
|
4225
|
+
-------
|
|
4226
|
+
bool
|
|
4227
|
+
A boolean value indicating whether the column is a calculated column.
|
|
4228
|
+
"""
|
|
4229
|
+
|
|
4230
|
+
import Microsoft.AnalysisServices.Tabular as TOM
|
|
4231
|
+
|
|
4232
|
+
c = self.model.Tables[table_name].Columns[column_name]
|
|
4233
|
+
return c.Type == TOM.ColumnType.Calculated
|
|
4234
|
+
|
|
4163
4235
|
def is_calculated_table(self, table_name: str):
|
|
4164
4236
|
"""
|
|
4165
4237
|
Identifies if a table is a calculated table.
|
|
@@ -4186,6 +4258,171 @@ class TOMWrapper:
|
|
|
4186
4258
|
isCalcTable = True
|
|
4187
4259
|
return isCalcTable
|
|
4188
4260
|
|
|
4261
|
+
def update_lineage_tags(self):
|
|
4262
|
+
"""
|
|
4263
|
+
Adds lineage and source lineage tags for relevant semantic model objects if they do not exist. Also updates schema name for Direct Lake (entity) partitions.
|
|
4264
|
+
"""
|
|
4265
|
+
|
|
4266
|
+
import Microsoft.AnalysisServices.Tabular as TOM
|
|
4267
|
+
|
|
4268
|
+
for t in self.model.Tables:
|
|
4269
|
+
if len(t.LineageTag) == 0:
|
|
4270
|
+
t.LineageTag = generate_guid()
|
|
4271
|
+
if len(t.SourceLineageTag) == 0:
|
|
4272
|
+
if next(p.Mode for p in t.Partitions) == TOM.ModeType.DirectLake:
|
|
4273
|
+
partition_name = next(p.Name for p in t.Partitions)
|
|
4274
|
+
entity_name = t.Partitions[partition_name].Source.EntityName
|
|
4275
|
+
schema_name = t.Partitions[partition_name].Source.SchemaName
|
|
4276
|
+
|
|
4277
|
+
# Update schema name and source lineage tag for DL (entity) partitions
|
|
4278
|
+
if len(schema_name) == 0:
|
|
4279
|
+
schema_name = icons.default_schema
|
|
4280
|
+
t.Partitions[partition_name].Source.SchemaName = (
|
|
4281
|
+
icons.default_schema
|
|
4282
|
+
)
|
|
4283
|
+
t.SourceLineageTag = f"[{schema_name}].[{entity_name}]"
|
|
4284
|
+
else:
|
|
4285
|
+
t.SourceLineageTag = generate_guid()
|
|
4286
|
+
for c in self.all_columns():
|
|
4287
|
+
if len(c.LineageTag) == 0:
|
|
4288
|
+
c.LineageTag = generate_guid()
|
|
4289
|
+
if len(c.SourceLineageTag) == 0:
|
|
4290
|
+
c.SourceLineageTag = generate_guid()
|
|
4291
|
+
for m in self.all_measures():
|
|
4292
|
+
if len(m.LineageTag) == 0:
|
|
4293
|
+
m.LineageTag = generate_guid()
|
|
4294
|
+
if len(m.SourceLineageTag) == 0:
|
|
4295
|
+
m.SourceLineageTag = generate_guid()
|
|
4296
|
+
for h in self.all_hierarchies():
|
|
4297
|
+
if len(h.LineageTag) == 0:
|
|
4298
|
+
h.LineageTag = generate_guid()
|
|
4299
|
+
if len(h.SourceLineageTag) == 0:
|
|
4300
|
+
h.SourceLineageTag = generate_guid()
|
|
4301
|
+
for lvl in self.all_levels():
|
|
4302
|
+
if len(lvl.LineageTag) == 0:
|
|
4303
|
+
lvl.LineageTag = generate_guid()
|
|
4304
|
+
if len(lvl.SourceLineageTag) == 0:
|
|
4305
|
+
lvl.SourceLineageTag = generate_guid()
|
|
4306
|
+
for e in self.model.Expressions():
|
|
4307
|
+
if len(e.LineageTag) == 0:
|
|
4308
|
+
e.LineageTag = generate_guid()
|
|
4309
|
+
if len(e.SourceLineageTag) == 0:
|
|
4310
|
+
e.SourceLineageTag = generate_guid()
|
|
4311
|
+
|
|
4312
|
+
def generate_measure_descriptions(
|
|
4313
|
+
self,
|
|
4314
|
+
measure_name: Optional[str | List[str]] = None,
|
|
4315
|
+
max_batch_size: Optional[int] = 5,
|
|
4316
|
+
):
|
|
4317
|
+
"""
|
|
4318
|
+
Auto-generates descriptions for measures using an LLM.
|
|
4319
|
+
|
|
4320
|
+
Parameters
|
|
4321
|
+
----------
|
|
4322
|
+
measure_name : str | List[str], default=None
|
|
4323
|
+
The measure name (or a list of measure names).
|
|
4324
|
+
Defaults to None which generates descriptions for all measures in the semantic model.
|
|
4325
|
+
max_batch_size : int, default=5
|
|
4326
|
+
Sets the max batch size for each API call.
|
|
4327
|
+
"""
|
|
4328
|
+
|
|
4329
|
+
# import concurrent.futures
|
|
4330
|
+
|
|
4331
|
+
if isinstance(measure_name, str):
|
|
4332
|
+
measure_name = [measure_name]
|
|
4333
|
+
|
|
4334
|
+
workspace_id = fabric.resolve_workspace_id(self._workspace)
|
|
4335
|
+
client = fabric.FabricRestClient()
|
|
4336
|
+
|
|
4337
|
+
if len(measure_name) > max_batch_size:
|
|
4338
|
+
measure_lists = [
|
|
4339
|
+
measure_name[i : i + max_batch_size]
|
|
4340
|
+
for i in range(0, len(measure_name), max_batch_size)
|
|
4341
|
+
]
|
|
4342
|
+
else:
|
|
4343
|
+
measure_lists = [measure_name]
|
|
4344
|
+
|
|
4345
|
+
# Each API call can have a max of 5 measures
|
|
4346
|
+
for measure_list in measure_lists:
|
|
4347
|
+
payload = {
|
|
4348
|
+
"scenarioDefinition": {
|
|
4349
|
+
"generateModelItemDescriptions": {
|
|
4350
|
+
"modelItems": [],
|
|
4351
|
+
},
|
|
4352
|
+
},
|
|
4353
|
+
"workspaceId": workspace_id,
|
|
4354
|
+
"artifactInfo": {"artifactType": "SemanticModel"},
|
|
4355
|
+
}
|
|
4356
|
+
for m_name in measure_list:
|
|
4357
|
+
expr, t_name = next(
|
|
4358
|
+
(ms.Expression, ms.Parent.Name)
|
|
4359
|
+
for ms in self.all_measures()
|
|
4360
|
+
if ms.Name == m_name
|
|
4361
|
+
)
|
|
4362
|
+
if t_name is None:
|
|
4363
|
+
raise ValueError(
|
|
4364
|
+
f"{icons.red_dot} The '{m_name}' measure does not exist in the '{self._dataset}' semantic model within the '{self._workspace}' workspace."
|
|
4365
|
+
)
|
|
4366
|
+
|
|
4367
|
+
new_item = {
|
|
4368
|
+
"urn": m_name,
|
|
4369
|
+
"type": 1,
|
|
4370
|
+
"name": m_name,
|
|
4371
|
+
"expression": expr,
|
|
4372
|
+
}
|
|
4373
|
+
payload["scenarioDefinition"]["generateModelItemDescriptions"][
|
|
4374
|
+
"modelItems"
|
|
4375
|
+
].append(new_item)
|
|
4376
|
+
|
|
4377
|
+
response = client.post("/explore/v202304/nl2nl/completions", json=payload)
|
|
4378
|
+
if response.status_code != 200:
|
|
4379
|
+
raise FabricHTTPException(response)
|
|
4380
|
+
|
|
4381
|
+
for item in response.json().get("modelItems", []):
|
|
4382
|
+
ms_name = item["urn"]
|
|
4383
|
+
if ms_name.startswith("urn: "):
|
|
4384
|
+
ms_name = ms_name[5:]
|
|
4385
|
+
desc = item.get("description")
|
|
4386
|
+
table_name = next(
|
|
4387
|
+
m.Parent.Name for m in self.all_measures() if m.Name == ms_name
|
|
4388
|
+
)
|
|
4389
|
+
self.model.Tables[table_name].Measures[ms_name].Description = desc
|
|
4390
|
+
|
|
4391
|
+
# def process_measure(m):
|
|
4392
|
+
# table_name = m.Parent.Name
|
|
4393
|
+
# m_name = m.Name
|
|
4394
|
+
# m_name_fixed = "1"
|
|
4395
|
+
# expr = m.Expression
|
|
4396
|
+
# if measure_name is None or m_name in measure_name:
|
|
4397
|
+
# payload = {
|
|
4398
|
+
# "scenarioDefinition": {
|
|
4399
|
+
# "generateModelItemDescriptions": {
|
|
4400
|
+
# "modelItems": [
|
|
4401
|
+
# {
|
|
4402
|
+
# "urn": f"modelobject://Table/{table_name}/Measure/{m_name_fixed}",
|
|
4403
|
+
# "type": 1,
|
|
4404
|
+
# "name": m_name,
|
|
4405
|
+
# "expression": expr,
|
|
4406
|
+
# }
|
|
4407
|
+
# ]
|
|
4408
|
+
# }
|
|
4409
|
+
# },
|
|
4410
|
+
# "workspaceId": workspace_id,
|
|
4411
|
+
# "artifactInfo": {"artifactType": "SemanticModel"},
|
|
4412
|
+
# }
|
|
4413
|
+
|
|
4414
|
+
# response = client.post(
|
|
4415
|
+
# "/explore/v202304/nl2nl/completions", json=payload
|
|
4416
|
+
# )
|
|
4417
|
+
# if response.status_code != 200:
|
|
4418
|
+
# raise FabricHTTPException(response)
|
|
4419
|
+
|
|
4420
|
+
# desc = response.json()["modelItems"][0]["description"]
|
|
4421
|
+
# m.Description = desc
|
|
4422
|
+
|
|
4423
|
+
# with concurrent.futures.ThreadPoolExecutor() as executor:
|
|
4424
|
+
# executor.map(process_measure, self.all_measures())
|
|
4425
|
+
|
|
4189
4426
|
def close(self):
|
|
4190
4427
|
if not self._readonly and self.model is not None:
|
|
4191
4428
|
self.model.SaveChanges()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|