semantic-link-labs 0.7.4__py3-none-any.whl → 0.8.1__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 (59) hide show
  1. {semantic_link_labs-0.7.4.dist-info → semantic_link_labs-0.8.1.dist-info}/METADATA +43 -7
  2. {semantic_link_labs-0.7.4.dist-info → semantic_link_labs-0.8.1.dist-info}/RECORD +59 -40
  3. {semantic_link_labs-0.7.4.dist-info → semantic_link_labs-0.8.1.dist-info}/WHEEL +1 -1
  4. sempy_labs/__init__.py +116 -58
  5. sempy_labs/_ai.py +0 -2
  6. sempy_labs/_capacities.py +39 -3
  7. sempy_labs/_capacity_migration.py +623 -0
  8. sempy_labs/_clear_cache.py +8 -8
  9. sempy_labs/_connections.py +15 -13
  10. sempy_labs/_data_pipelines.py +118 -0
  11. sempy_labs/_documentation.py +144 -0
  12. sempy_labs/_eventhouses.py +118 -0
  13. sempy_labs/_eventstreams.py +118 -0
  14. sempy_labs/_generate_semantic_model.py +3 -3
  15. sempy_labs/_git.py +23 -24
  16. sempy_labs/_helper_functions.py +140 -47
  17. sempy_labs/_icons.py +40 -0
  18. sempy_labs/_kql_databases.py +134 -0
  19. sempy_labs/_kql_querysets.py +124 -0
  20. sempy_labs/_list_functions.py +218 -421
  21. sempy_labs/_mirrored_warehouses.py +50 -0
  22. sempy_labs/_ml_experiments.py +122 -0
  23. sempy_labs/_ml_models.py +120 -0
  24. sempy_labs/_model_auto_build.py +0 -4
  25. sempy_labs/_model_bpa.py +10 -12
  26. sempy_labs/_model_bpa_bulk.py +8 -7
  27. sempy_labs/_model_dependencies.py +26 -18
  28. sempy_labs/_notebooks.py +5 -16
  29. sempy_labs/_query_scale_out.py +6 -5
  30. sempy_labs/_refresh_semantic_model.py +7 -19
  31. sempy_labs/_spark.py +40 -45
  32. sempy_labs/_sql.py +60 -15
  33. sempy_labs/_vertipaq.py +25 -25
  34. sempy_labs/_warehouses.py +132 -0
  35. sempy_labs/_workspaces.py +0 -3
  36. sempy_labs/admin/__init__.py +53 -0
  37. sempy_labs/admin/_basic_functions.py +888 -0
  38. sempy_labs/admin/_domains.py +411 -0
  39. sempy_labs/directlake/_directlake_schema_sync.py +1 -1
  40. sempy_labs/directlake/_dl_helper.py +32 -16
  41. sempy_labs/directlake/_generate_shared_expression.py +11 -14
  42. sempy_labs/directlake/_guardrails.py +7 -7
  43. sempy_labs/directlake/_update_directlake_model_lakehouse_connection.py +14 -24
  44. sempy_labs/directlake/_update_directlake_partition_entity.py +1 -1
  45. sempy_labs/directlake/_warm_cache.py +1 -1
  46. sempy_labs/lakehouse/_get_lakehouse_tables.py +3 -3
  47. sempy_labs/lakehouse/_lakehouse.py +3 -2
  48. sempy_labs/migration/_migrate_calctables_to_lakehouse.py +5 -0
  49. sempy_labs/report/__init__.py +9 -6
  50. sempy_labs/report/_generate_report.py +1 -1
  51. sempy_labs/report/_report_bpa.py +369 -0
  52. sempy_labs/report/_report_bpa_rules.py +113 -0
  53. sempy_labs/report/_report_helper.py +254 -0
  54. sempy_labs/report/_report_list_functions.py +95 -0
  55. sempy_labs/report/_report_rebind.py +0 -4
  56. sempy_labs/report/_reportwrapper.py +2037 -0
  57. sempy_labs/tom/_model.py +333 -22
  58. {semantic_link_labs-0.7.4.dist-info → semantic_link_labs-0.8.1.dist-info}/LICENSE +0 -0
  59. {semantic_link_labs-0.7.4.dist-info → semantic_link_labs-0.8.1.dist-info}/top_level.txt +0 -0
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 format_dax_object_name
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,11 +230,14 @@ class TOMWrapper:
226
230
  measure_name: str,
227
231
  expression: str,
228
232
  format_string: Optional[str] = None,
229
- hidden: Optional[bool] = False,
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,
237
+ data_category: Optional[str] = None,
238
+ lineage_tag: Optional[str] = None,
233
239
  source_lineage_tag: Optional[str] = None,
240
+ detail_rows_expression: Optional[str] = None,
234
241
  ):
235
242
  """
236
243
  Adds a measure to the semantic model.
@@ -253,8 +260,14 @@ class TOMWrapper:
253
260
  The display folder in which the measure will reside.
254
261
  format_string_expression : str, default=None
255
262
  The format string expression.
263
+ data_category : str, default=None
264
+ Specifies the type of data contained in the measure so that you can add custom behaviors based on measure type.
265
+ lineage_tag : str, default=None
266
+ A tag that represents the lineage of the object.
256
267
  source_lineage_tag : str, default=None
257
268
  A tag that represents the lineage of the source for the object.
269
+ detail_rows_expression : str, default=None
270
+ The detail rows expression.
258
271
  """
259
272
  import Microsoft.AnalysisServices.Tabular as TOM
260
273
 
@@ -272,8 +285,20 @@ class TOMWrapper:
272
285
  fsd = TOM.FormatStringDefinition()
273
286
  fsd.Expression = format_string_expression
274
287
  obj.FormatStringDefinition = fsd
288
+ if lineage_tag is not None:
289
+ obj.LineageTag = lineage_tag
290
+ else:
291
+ obj.LineageTag = generate_guid()
275
292
  if source_lineage_tag is not None:
276
293
  obj.SourceLineageTag = source_lineage_tag
294
+ else:
295
+ obj.SourceLineageTag = generate_guid()
296
+ if detail_rows_expression is not None:
297
+ drd = TOM.DetailRowsDefinition()
298
+ drd.Expression = detail_rows_expression
299
+ obj.DetailRowsDefinition = drd
300
+ if data_category is not None:
301
+ obj.DataCategory = data_category
277
302
 
278
303
  self.model.Tables[table_name].Measures.Add(obj)
279
304
 
@@ -284,12 +309,13 @@ class TOMWrapper:
284
309
  source_column: str,
285
310
  data_type: str,
286
311
  format_string: Optional[str] = None,
287
- hidden: Optional[bool] = False,
312
+ hidden: bool = False,
288
313
  description: Optional[str] = None,
289
314
  display_folder: Optional[str] = None,
290
315
  data_category: Optional[str] = None,
291
- key: Optional[bool] = False,
316
+ key: bool = False,
292
317
  summarize_by: Optional[str] = None,
318
+ lineage_tag: Optional[str] = None,
293
319
  source_lineage_tag: Optional[str] = None,
294
320
  ):
295
321
  """
@@ -320,6 +346,8 @@ class TOMWrapper:
320
346
  summarize_by : str, default=None
321
347
  Sets the value for the Summarize By property of the column.
322
348
  Defaults to None resolves to 'Default'.
349
+ lineage_tag : str, default=None
350
+ A tag that represents the lineage of the object.
323
351
  source_lineage_tag : str, default=None
324
352
  A tag that represents the lineage of the source for the object.
325
353
  """
@@ -354,8 +382,14 @@ class TOMWrapper:
354
382
  obj.DisplayFolder = display_folder
355
383
  if data_category is not None:
356
384
  obj.DataCategory = data_category
385
+ if lineage_tag is not None:
386
+ obj.LineageTag = lineage_tag
387
+ else:
388
+ obj.LineageTag = generate_guid()
357
389
  if source_lineage_tag is not None:
358
390
  obj.SourceLineageTag = source_lineage_tag
391
+ else:
392
+ obj.SourceLineageTag = generate_guid()
359
393
  self.model.Tables[table_name].Columns.Add(obj)
360
394
 
361
395
  def add_data_column(
@@ -365,12 +399,13 @@ class TOMWrapper:
365
399
  source_column: str,
366
400
  data_type: str,
367
401
  format_string: Optional[str] = None,
368
- hidden: Optional[bool] = False,
402
+ hidden: bool = False,
369
403
  description: Optional[str] = None,
370
404
  display_folder: Optional[str] = None,
371
405
  data_category: Optional[str] = None,
372
- key: Optional[bool] = False,
406
+ key: bool = False,
373
407
  summarize_by: Optional[str] = None,
408
+ lineage_tag: Optional[str] = None,
374
409
  source_lineage_tag: Optional[str] = None,
375
410
  ):
376
411
  """
@@ -401,6 +436,8 @@ class TOMWrapper:
401
436
  summarize_by : str, default=None
402
437
  Sets the value for the Summarize By property of the column.
403
438
  Defaults to None resolves to 'Default'.
439
+ lineage_tag : str, default=None
440
+ A tag that represents the lineage of the object.
404
441
  source_lineage_tag : str, default=None
405
442
  A tag that represents the lineage of the source for the object.
406
443
  """
@@ -435,8 +472,14 @@ class TOMWrapper:
435
472
  obj.DisplayFolder = display_folder
436
473
  if data_category is not None:
437
474
  obj.DataCategory = data_category
475
+ if lineage_tag is not None:
476
+ obj.LineageTag = lineage_tag
477
+ else:
478
+ obj.LineageTag = generate_guid()
438
479
  if source_lineage_tag is not None:
439
480
  obj.SourceLineageTag = source_lineage_tag
481
+ else:
482
+ obj.SourceLineagetTag = generate_guid()
440
483
  self.model.Tables[table_name].Columns.Add(obj)
441
484
 
442
485
  def add_calculated_column(
@@ -446,12 +489,13 @@ class TOMWrapper:
446
489
  expression: str,
447
490
  data_type: str,
448
491
  format_string: Optional[str] = None,
449
- hidden: Optional[bool] = False,
492
+ hidden: bool = False,
450
493
  description: Optional[str] = None,
451
494
  display_folder: Optional[str] = None,
452
495
  data_category: Optional[str] = None,
453
- key: Optional[bool] = False,
496
+ key: bool = False,
454
497
  summarize_by: Optional[str] = None,
498
+ lineage_tag: Optional[str] = None,
455
499
  source_lineage_tag: Optional[str] = None,
456
500
  ):
457
501
  """
@@ -482,6 +526,8 @@ class TOMWrapper:
482
526
  summarize_by : str, default=None
483
527
  Sets the value for the Summarize By property of the column.
484
528
  Defaults to None which resolves to 'Default'.
529
+ lineage_tag : str, default=None
530
+ A tag that represents the lineage of the object.
485
531
  source_lineage_tag : str, default=None
486
532
  A tag that represents the lineage of the source for the object.
487
533
  """
@@ -516,8 +562,14 @@ class TOMWrapper:
516
562
  obj.DisplayFolder = display_folder
517
563
  if data_category is not None:
518
564
  obj.DataCategory = data_category
565
+ if lineage_tag is not None:
566
+ obj.LineageTag = lineage_tag
567
+ else:
568
+ obj.LineageTag = generate_guid()
519
569
  if source_lineage_tag is not None:
520
570
  obj.SourceLineageTag = source_lineage_tag
571
+ else:
572
+ obj.SourceLineagetTag = generate_guid()
521
573
  self.model.Tables[table_name].Columns.Add(obj)
522
574
 
523
575
  def add_calculation_item(
@@ -676,7 +728,8 @@ class TOMWrapper:
676
728
  columns: List[str],
677
729
  levels: Optional[List[str]] = None,
678
730
  hierarchy_description: Optional[str] = None,
679
- hierarchy_hidden: Optional[bool] = False,
731
+ hierarchy_hidden: bool = False,
732
+ lineage_tag: Optional[str] = None,
680
733
  source_lineage_tag: Optional[str] = None,
681
734
  ):
682
735
  """
@@ -696,6 +749,8 @@ class TOMWrapper:
696
749
  A description of the hierarchy.
697
750
  hierarchy_hidden : bool, default=False
698
751
  Whether the hierarchy is visible or hidden.
752
+ lineage_tag : str, default=None
753
+ A tag that represents the lineage of the object.
699
754
  source_lineage_tag : str, default=None
700
755
  A tag that represents the lineage of the source for the object.
701
756
  """
@@ -724,8 +779,14 @@ class TOMWrapper:
724
779
  obj.IsHidden = hierarchy_hidden
725
780
  if hierarchy_description is not None:
726
781
  obj.Description = hierarchy_description
782
+ if lineage_tag is not None:
783
+ obj.LineageTag = lineage_tag
784
+ else:
785
+ obj.LineageTag = generate_guid()
727
786
  if source_lineage_tag is not None:
728
787
  obj.SourceLineageTag = source_lineage_tag
788
+ else:
789
+ obj.SourceLineagetTag = generate_guid()
729
790
  self.model.Tables[table_name].Hierarchies.Add(obj)
730
791
 
731
792
  for col in columns:
@@ -733,6 +794,8 @@ class TOMWrapper:
733
794
  lvl.Column = self.model.Tables[table_name].Columns[col]
734
795
  lvl.Name = levels[columns.index(col)]
735
796
  lvl.Ordinal = columns.index(col)
797
+ lvl.LineageTag = generate_guid()
798
+ lvl.SourceLineageTag = generate_guid()
736
799
  self.model.Tables[table_name].Hierarchies[hierarchy_name].Levels.Add(lvl)
737
800
 
738
801
  def add_relationship(
@@ -744,9 +807,9 @@ class TOMWrapper:
744
807
  from_cardinality: str,
745
808
  to_cardinality: str,
746
809
  cross_filtering_behavior: Optional[str] = None,
747
- is_active: Optional[bool] = True,
810
+ is_active: bool = True,
748
811
  security_filtering_behavior: Optional[str] = None,
749
- rely_on_referential_integrity: Optional[bool] = False,
812
+ rely_on_referential_integrity: bool = False,
750
813
  ):
751
814
  """
752
815
  Adds a `relationship <https://learn.microsoft.com/dotnet/api/microsoft.analysisservices.tabular.singlecolumnrelationship?view=analysisservices-dotnet>`_ to a semantic model.
@@ -818,7 +881,7 @@ class TOMWrapper:
818
881
  name: str,
819
882
  precedence: int,
820
883
  description: Optional[str] = None,
821
- hidden: Optional[bool] = False,
884
+ hidden: bool = False,
822
885
  ):
823
886
  """
824
887
  Adds a `calculation group <https://learn.microsoft.com/dotnet/api/microsoft.analysisservices.tabular.calculationgroup?view=analysisservices-dotnet>`_ to a semantic model.
@@ -875,6 +938,7 @@ class TOMWrapper:
875
938
  name: str,
876
939
  expression: str,
877
940
  description: Optional[str] = None,
941
+ lineage_tag: Optional[str] = None,
878
942
  source_lineage_tag: Optional[str] = None,
879
943
  ):
880
944
  """
@@ -888,6 +952,8 @@ class TOMWrapper:
888
952
  The M expression of the expression.
889
953
  description : str, default=None
890
954
  A description of the expression.
955
+ lineage_tag : str, default=None
956
+ A tag that represents the lineage of the object.
891
957
  source_lineage_tag : str, default=None
892
958
  A tag that represents the lineage of the source for the object.
893
959
  """
@@ -897,8 +963,14 @@ class TOMWrapper:
897
963
  exp.Name = name
898
964
  if description is not None:
899
965
  exp.Description = description
966
+ if lineage_tag is not None:
967
+ exp.LineageTag = lineage_tag
968
+ else:
969
+ exp.LineageTag = generate_guid()
900
970
  if source_lineage_tag is not None:
901
971
  exp.SourceLineageTag = source_lineage_tag
972
+ else:
973
+ exp.SourceLineageTag = generate_guid()
902
974
  exp.Kind = TOM.ExpressionKind.M
903
975
  exp.Expression = expression
904
976
 
@@ -992,6 +1064,7 @@ class TOMWrapper:
992
1064
  entity_name: str,
993
1065
  expression: Optional[str] = None,
994
1066
  description: Optional[str] = None,
1067
+ schema_name: str = "dbo",
995
1068
  ):
996
1069
  """
997
1070
  Adds an entity partition to a table within a semantic model.
@@ -1001,12 +1074,14 @@ class TOMWrapper:
1001
1074
  table_name : str
1002
1075
  Name of the table.
1003
1076
  entity_name : str
1004
- Name of the lakehouse table.
1077
+ Name of the lakehouse/warehouse table.
1005
1078
  expression : TOM Object, default=None
1006
1079
  The expression used by the table.
1007
1080
  Defaults to None which resolves to the 'DatabaseQuery' expression.
1008
1081
  description : str, default=None
1009
1082
  A description for the partition.
1083
+ schema_name : str, default="dbo"
1084
+ The schema name.
1010
1085
  """
1011
1086
  import Microsoft.AnalysisServices.Tabular as TOM
1012
1087
 
@@ -1017,6 +1092,7 @@ class TOMWrapper:
1017
1092
  ep.ExpressionSource = self.model.Expressions["DatabaseQuery"]
1018
1093
  else:
1019
1094
  ep.ExpressionSource = self.model.Expressions[expression]
1095
+ ep.SchemaName = schema_name
1020
1096
  p = TOM.Partition()
1021
1097
  p.Name = table_name
1022
1098
  p.Source = ep
@@ -1025,6 +1101,9 @@ class TOMWrapper:
1025
1101
  p.Description = description
1026
1102
 
1027
1103
  self.model.Tables[table_name].Partitions.Add(p)
1104
+ self.model.Tables[table_name].SourceLineageTag = (
1105
+ f"[{schema_name}].[{entity_name}]"
1106
+ )
1028
1107
 
1029
1108
  def set_alternate_of(
1030
1109
  self,
@@ -1605,13 +1684,25 @@ class TOMWrapper:
1605
1684
  objType = object.ObjectType
1606
1685
 
1607
1686
  # Have to remove translations and perspectives on the object before removing it.
1608
- if objType in ["Table", "Column", "Measure", "Hierarchy", "Level"]:
1687
+ if objType in [
1688
+ TOM.ObjectType.Table,
1689
+ TOM.ObjectType.Column,
1690
+ TOM.ObjectType.Measure,
1691
+ TOM.ObjectType.Hierarchy,
1692
+ TOM.ObjectType.Level,
1693
+ ]:
1609
1694
  for lang in object.Model.Cultures:
1610
1695
  try:
1611
1696
  self.remove_translation(object=object, language=lang.Name)
1612
1697
  except Exception:
1613
1698
  pass
1614
- if objType in ["Table", "Column", "Measure", "Hierarchy"]:
1699
+ if objType in [
1700
+ TOM.ObjectType.Table,
1701
+ TOM.ObjectType.Column,
1702
+ TOM.ObjectType.Measure,
1703
+ TOM.ObjectType.Hierarchy,
1704
+ TOM.ObjectType.Level,
1705
+ ]:
1615
1706
  for persp in object.Model.Perspectives:
1616
1707
  try:
1617
1708
  self.remove_from_perspective(
@@ -2424,7 +2515,7 @@ class TOMWrapper:
2424
2515
  )
2425
2516
 
2426
2517
  def set_is_available_in_mdx(
2427
- self, table_name: str, column_name: str, value: Optional[bool] = False
2518
+ self, table_name: str, column_name: str, value: bool = False
2428
2519
  ):
2429
2520
  """
2430
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.
@@ -2527,7 +2618,8 @@ class TOMWrapper:
2527
2618
  name: str,
2528
2619
  description: Optional[str] = None,
2529
2620
  data_category: Optional[str] = None,
2530
- hidden: Optional[bool] = False,
2621
+ hidden: bool = False,
2622
+ lineage_tag: Optional[str] = None,
2531
2623
  source_lineage_tag: Optional[str] = None,
2532
2624
  ):
2533
2625
  """
@@ -2543,6 +2635,8 @@ class TOMWrapper:
2543
2635
  The data category for the table.
2544
2636
  hidden : bool, default=False
2545
2637
  Whether the table is hidden or visible.
2638
+ lineage_tag : str, default=None
2639
+ A tag that represents the lineage of the object.
2546
2640
  source_lineage_tag : str, default=None
2547
2641
  A tag that represents the lineage of the source for the object.
2548
2642
  """
@@ -2554,8 +2648,14 @@ class TOMWrapper:
2554
2648
  t.Description = description
2555
2649
  if data_category is not None:
2556
2650
  t.DataCategory = data_category
2651
+ if lineage_tag is not None:
2652
+ t.LineageTag = lineage_tag
2653
+ else:
2654
+ t.LineageTag = generate_guid()
2557
2655
  if source_lineage_tag is not None:
2558
2656
  t.SourceLineageTag = source_lineage_tag
2657
+ else:
2658
+ t.SourceLineagetTag = generate_guid()
2559
2659
  t.Hidden = hidden
2560
2660
  self.model.Tables.Add(t)
2561
2661
 
@@ -2565,7 +2665,9 @@ class TOMWrapper:
2565
2665
  expression: str,
2566
2666
  description: Optional[str] = None,
2567
2667
  data_category: Optional[str] = None,
2568
- hidden: Optional[bool] = False,
2668
+ hidden: bool = False,
2669
+ lineage_tag: Optional[str] = None,
2670
+ source_lineage_tag: Optional[str] = None,
2569
2671
  ):
2570
2672
  """
2571
2673
  Adds a calculated table to the semantic model.
@@ -2582,6 +2684,10 @@ class TOMWrapper:
2582
2684
  The data category for the table.
2583
2685
  hidden : bool, default=False
2584
2686
  Whether the table is hidden or visible.
2687
+ lineage_tag : str, default=None
2688
+ A tag that represents the lineage of the object.
2689
+ source_lineage_tag : str, default=None
2690
+ A tag that represents the lineage of the source for the object.
2585
2691
  """
2586
2692
  import Microsoft.AnalysisServices.Tabular as TOM
2587
2693
 
@@ -2598,6 +2704,14 @@ class TOMWrapper:
2598
2704
  t.Description = description
2599
2705
  if data_category is not None:
2600
2706
  t.DataCategory = data_category
2707
+ if lineage_tag is not None:
2708
+ t.LineageTag = lineage_tag
2709
+ else:
2710
+ t.LineageTag = generate_guid()
2711
+ if source_lineage_tag is not None:
2712
+ t.SourceLineageTag = source_lineage_tag
2713
+ else:
2714
+ t.SourceLineagetTag = generate_guid()
2601
2715
  t.Hidden = hidden
2602
2716
  t.Partitions.Add(par)
2603
2717
  self.model.Tables.Add(t)
@@ -3304,7 +3418,7 @@ class TOMWrapper:
3304
3418
  incremental_periods: int,
3305
3419
  rolling_window_granularity: str,
3306
3420
  rolling_window_periods: int,
3307
- only_refresh_complete_days: Optional[bool] = False,
3421
+ only_refresh_complete_days: bool = False,
3308
3422
  detect_data_changes_column: Optional[str] = None,
3309
3423
  ):
3310
3424
  """
@@ -3409,7 +3523,7 @@ class TOMWrapper:
3409
3523
  incremental_periods: int,
3410
3524
  rolling_window_granularity: str,
3411
3525
  rolling_window_periods: int,
3412
- only_refresh_complete_days: Optional[bool] = False,
3526
+ only_refresh_complete_days: bool = False,
3413
3527
  detect_data_changes_column: Optional[str] = None,
3414
3528
  ):
3415
3529
  """
@@ -3578,7 +3692,7 @@ class TOMWrapper:
3578
3692
  self,
3579
3693
  table_name: str,
3580
3694
  effective_date: Optional[datetime] = None,
3581
- refresh: Optional[bool] = True,
3695
+ refresh: bool = True,
3582
3696
  max_parallelism: Optional[int] = 0,
3583
3697
  ):
3584
3698
  """
@@ -3896,6 +4010,8 @@ class TOMWrapper:
3896
4010
  data_category: Optional[str] = None,
3897
4011
  key: Optional[bool] = None,
3898
4012
  summarize_by: Optional[str] = None,
4013
+ is_nullable: Optional[bool] = None,
4014
+ is_available_in_mdx: Optional[bool] = None,
3899
4015
  ):
3900
4016
  """
3901
4017
  Updates a column within a semantic model.
@@ -3936,12 +4052,16 @@ class TOMWrapper:
3936
4052
  summarize_by : str, default=None
3937
4053
  Sets the value for the Summarize By property of the column.
3938
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.
3939
4059
  """
3940
4060
 
3941
4061
  import Microsoft.AnalysisServices.Tabular as TOM
3942
4062
  import System
3943
4063
 
3944
- c = self.model.Tables[table_name].Measures[column_name]
4064
+ c = self.model.Tables[table_name].Columns[column_name]
3945
4065
  if c.Type == TOM.ColumnType.Data:
3946
4066
  if source_column is not None:
3947
4067
  c.SourceColumn = source_column
@@ -3964,6 +4084,10 @@ class TOMWrapper:
3964
4084
  c.DataCategory = data_category
3965
4085
  if summarize_by is not None:
3966
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
3967
4091
 
3968
4092
  def update_role(
3969
4093
  self,
@@ -4086,6 +4210,28 @@ class TOMWrapper:
4086
4210
 
4087
4211
  self.model.Tables[table_name].Columns[column_name].SortByColumn = None
4088
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
+
4089
4235
  def is_calculated_table(self, table_name: str):
4090
4236
  """
4091
4237
  Identifies if a table is a calculated table.
@@ -4112,6 +4258,171 @@ class TOMWrapper:
4112
4258
  isCalcTable = True
4113
4259
  return isCalcTable
4114
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
+
4115
4426
  def close(self):
4116
4427
  if not self._readonly and self.model is not None:
4117
4428
  self.model.SaveChanges()