Forum Discussion

kinsin5's avatar
kinsin5
New Member
8 months ago
Solved

Checking if semantic model is composite

Hi,

 

Do you know how can I check semantic model metadata using sempy? I want to list all composite models inside all workspaces.

  • kinsin5 As far as I know, there isn’t a first-class “IsComposite” flag in SemPy right now; you infer it from things like partition storage modes and data source types.

     

    Below is a pattern that might work in Fabric notebooks.

     

    1. Basic semantic model metadata with SemPy

    From a Fabric notebook:

     
    %pip install semantic-link -q
    %load_ext sempy
    
    import sempy.fabric as fabric

     

    Typical metadata calls for a single semantic model:

     
    dataset = "My Semantic Model"
    workspace = "My Workspace"   # or workspace ID
    # Tables / columns / measures
    tables      = fabric.list_tables(dataset=dataset, workspace=workspace)
    columns     = fabric.list_columns(dataset=dataset, workspace=workspace)
    measures    = fabric.list_measures(dataset=dataset, workspace=workspace)
    # Relationships
    relationships = fabric.list_relationships(dataset=dataset, workspace=workspace)
    # Partitions (this is where storage mode lives)
    partitions  = fabric.list_partitions(dataset=dataset, workspace=workspace, extended=True)
    # Data sources (for checking Analysis Services / other PBI models, etc.)
    datasources = fabric.list_datasources(dataset=dataset, workspace=workspace)

     

    These all come back as pandas DataFrames, so you can query them however you like.

    2. Listing all composite models across all Fabric workspaces

    A semantic model is “composite” if, for example:

    • It has different storage modes across tables/partitions (Import + DirectQuery, DirectLake + Import, etc.), or

    • It uses another semantic model / AAS as a data source (via AnalysisServices / PowerBI source types).

    SemPy gives you enough to derive that:

    • fabric.list_workspaces() -> all workspaces you can see

    • fabric.list_datasets(workspace=...) -> semantic models in a workspace

    • fabric.list_partitions(...) + fabric.list_datasources(...) -> storage modes & source types per model

    So maybe something along these lines:

    %load_ext sempy
    import sempy.fabric as fabric
    import pandas as pd
    
    def classify_composite_for_dataset(workspace_name: str, dataset_name: str) -> dict:
        # Partitions: storage mode per table
        parts = fabric.list_partitions(
            workspace=workspace_name,
            dataset=dataset_name,
            extended=True
        )
    
        if parts is None or parts.empty:
            modes = set()
        else:
            modes = set(parts["Mode"].dropna().unique())
    
        # Data sources: where the data is coming from
        try:
            ds_df = fabric.list_datasources(
                workspace=workspace_name,
                dataset=dataset_name
            )
        except Exception:
            ds_df = pd.DataFrame()
    
        if ds_df is None or ds_df.empty:
            source_types = set()
        else:
            # Column name is typically "Type" in list_datasources output
            source_types = set(ds_df["Type"].dropna().unique())
    
        # Heuristics for "composite"
        has_import      = "Import" in modes
        has_directquery = "DirectQuery" in modes
        has_directlake  = "DirectLake" in modes
        has_multiple_modes = len(modes) > 1
    
        # Remote semantic models / AAS as datasources
        uses_remote_semantic = any(
            t in source_types for t in ["AnalysisServices", "PowerBI"]
        )
    
        is_composite = (
            has_multiple_modes
            or (has_directquery and (modes - {"DirectQuery"}))   # DQ + something else
            or (has_directlake and (has_import or "Dual" in modes))
            or uses_remote_semantic
        )
    
        return {
            "Workspace": workspace_name,
            "Dataset": dataset_name,
            "Modes": ", ".join(sorted(modes)) if modes else "",
            "SourceTypes": ", ".join(sorted(source_types)) if source_types else "",
            "IsComposite": is_composite,
        }
    
    
    def list_composite_models(include_personal_workspaces: bool = False) -> pd.DataFrame:
        workspaces = fabric.list_workspaces()   # all workspaces you can access
    
        # Optional: filter out personal workspaces if the column exists
        if not include_personal_workspaces and "Type" in workspaces.columns:
            workspaces = workspaces[workspaces["Type"] != "Personal"]
    
        rows = []
    
        for _, ws in workspaces.iterrows():
            ws_name = ws["Name"]
            # could also use ws["Id"]; sempy accepts name or ID in most functions
    
            datasets = fabric.list_datasets(workspace=ws_name)
            if datasets is None or datasets.empty:
                continue
    
            for _, ds in datasets.iterrows():
                ds_name = ds["Name"]
                info = classify_composite_for_dataset(ws_name, ds_name)
                rows.append(info)
    
        return pd.DataFrame(rows)
    
    
    # Run the scan
    all_models = list_composite_models(include_personal_workspaces=False)
    
    # Only composite semantic models
    composite_models = all_models[all_models["IsComposite"]].copy()
    
    display(composite_models)

     

    That composite_models DataFrame will give you, per semantic model:

    • Workspace name

    • Dataset / semantic model name

    • All storage modes it uses (from list_partitions)

    • All data source types (from list_datasources)

    • A boolean IsComposite based on the rules above

    You can then:

    • Write it to a Lakehouse table and build a governance semantic model on top,

    • Or slice and dice it directly in the notebook.

    Notes:

    • Permissions: You’ll only see workspaces and semantic models you have rights to. For full-tenant scanning you typically combine this with admin or scanner APIs (often via Semantic Link Labs, not plain SemPy).

    • Definition of “composite”: If you want to be super strict (e.g., “DQ over PBI dataset + local tables only”), tweak the heuristic in classify_composite_for_dataset.

    • Performance: On big tenants, you may want to:

      • Filter list_workspaces() first (e.g., only dedicated capacities, only certain domains), and/or

      • Parallelize per-workspace scans with multiprocessing in Python.

4 Replies

  • Hi kinsin5 

    Step 1: Install sempy (if not already installed)

    pip install sempy


    ---

    Step 2: Connect to Power BI Service

    from sempy import Workspace

    # Connect to all workspaces
    workspaces = Workspace.list_workspaces()


    ---

    Step 3: Loop through workspaces and datasets

    for ws in workspaces:
    print(f"Workspace: {ws.name}")
    datasets = ws.datasets # List all datasets in the workspace
    for ds in datasets:
    # Check if the dataset is a composite model
    if ds.is_composite:
    print(f" Composite Model: {ds.name}")

    Explanation:

    Workspace.list_workspaces() → gets all workspaces you have access to.

    ws.datasets → lists all datasets inside the workspace.

    ds.is_composite → identifies composite models (direct query + import, etc.).

     

    ---

    Step 4: Optional – Export results to CSV

    import csv

    results = []

    for ws in workspaces:
    for ds in ws.datasets:
    if ds.is_composite:
    results.append([ws.name, ds.name])

    # Save to CSV
    with open("composite_models.csv", "w", newline="") as f:
    writer = csv.writer(f)
    writer.writerow(["Workspace", "Dataset"])
    writer.writerows(results)

     

    If this solution helped you, please mark it as the accepted answer so it can help others as well.

  • v-hjannapu's avatar
    v-hjannapu
    Community Support

    Hi kinsin5,

    I would also take a moment to thank GeraldGEmerick  , for actively participating in the community forum and for the solutions you’ve been sharing in the community forum. Your contributions make a real difference.
     

    I wanted to check if you had the opportunity to review the information provided. Please feel free to contact us if you have any further questions.

    Regards,
    Community Support Team.

    • v-hjannapu's avatar
      v-hjannapu
      Community Support

      Hi kinsin5,
      I hope the information provided above assists you in resolving the issue. If you have any additional questions or concerns, please do not hesitate to contact us. We are here to support you and will be happy to help with any further assistance you may need.

      Regards,
      Community Support Team.


  • kinsin5 As far as I know, there isn’t a first-class “IsComposite” flag in SemPy right now; you infer it from things like partition storage modes and data source types.

     

    Below is a pattern that might work in Fabric notebooks.

     

    1. Basic semantic model metadata with SemPy

    From a Fabric notebook:

     
    %pip install semantic-link -q
    %load_ext sempy
    
    import sempy.fabric as fabric

     

    Typical metadata calls for a single semantic model:

     
    dataset = "My Semantic Model"
    workspace = "My Workspace"   # or workspace ID
    # Tables / columns / measures
    tables      = fabric.list_tables(dataset=dataset, workspace=workspace)
    columns     = fabric.list_columns(dataset=dataset, workspace=workspace)
    measures    = fabric.list_measures(dataset=dataset, workspace=workspace)
    # Relationships
    relationships = fabric.list_relationships(dataset=dataset, workspace=workspace)
    # Partitions (this is where storage mode lives)
    partitions  = fabric.list_partitions(dataset=dataset, workspace=workspace, extended=True)
    # Data sources (for checking Analysis Services / other PBI models, etc.)
    datasources = fabric.list_datasources(dataset=dataset, workspace=workspace)

     

    These all come back as pandas DataFrames, so you can query them however you like.

    2. Listing all composite models across all Fabric workspaces

    A semantic model is “composite” if, for example:

    • It has different storage modes across tables/partitions (Import + DirectQuery, DirectLake + Import, etc.), or

    • It uses another semantic model / AAS as a data source (via AnalysisServices / PowerBI source types).

    SemPy gives you enough to derive that:

    • fabric.list_workspaces() -> all workspaces you can see

    • fabric.list_datasets(workspace=...) -> semantic models in a workspace

    • fabric.list_partitions(...) + fabric.list_datasources(...) -> storage modes & source types per model

    So maybe something along these lines:

    %load_ext sempy
    import sempy.fabric as fabric
    import pandas as pd
    
    def classify_composite_for_dataset(workspace_name: str, dataset_name: str) -> dict:
        # Partitions: storage mode per table
        parts = fabric.list_partitions(
            workspace=workspace_name,
            dataset=dataset_name,
            extended=True
        )
    
        if parts is None or parts.empty:
            modes = set()
        else:
            modes = set(parts["Mode"].dropna().unique())
    
        # Data sources: where the data is coming from
        try:
            ds_df = fabric.list_datasources(
                workspace=workspace_name,
                dataset=dataset_name
            )
        except Exception:
            ds_df = pd.DataFrame()
    
        if ds_df is None or ds_df.empty:
            source_types = set()
        else:
            # Column name is typically "Type" in list_datasources output
            source_types = set(ds_df["Type"].dropna().unique())
    
        # Heuristics for "composite"
        has_import      = "Import" in modes
        has_directquery = "DirectQuery" in modes
        has_directlake  = "DirectLake" in modes
        has_multiple_modes = len(modes) > 1
    
        # Remote semantic models / AAS as datasources
        uses_remote_semantic = any(
            t in source_types for t in ["AnalysisServices", "PowerBI"]
        )
    
        is_composite = (
            has_multiple_modes
            or (has_directquery and (modes - {"DirectQuery"}))   # DQ + something else
            or (has_directlake and (has_import or "Dual" in modes))
            or uses_remote_semantic
        )
    
        return {
            "Workspace": workspace_name,
            "Dataset": dataset_name,
            "Modes": ", ".join(sorted(modes)) if modes else "",
            "SourceTypes": ", ".join(sorted(source_types)) if source_types else "",
            "IsComposite": is_composite,
        }
    
    
    def list_composite_models(include_personal_workspaces: bool = False) -> pd.DataFrame:
        workspaces = fabric.list_workspaces()   # all workspaces you can access
    
        # Optional: filter out personal workspaces if the column exists
        if not include_personal_workspaces and "Type" in workspaces.columns:
            workspaces = workspaces[workspaces["Type"] != "Personal"]
    
        rows = []
    
        for _, ws in workspaces.iterrows():
            ws_name = ws["Name"]
            # could also use ws["Id"]; sempy accepts name or ID in most functions
    
            datasets = fabric.list_datasets(workspace=ws_name)
            if datasets is None or datasets.empty:
                continue
    
            for _, ds in datasets.iterrows():
                ds_name = ds["Name"]
                info = classify_composite_for_dataset(ws_name, ds_name)
                rows.append(info)
    
        return pd.DataFrame(rows)
    
    
    # Run the scan
    all_models = list_composite_models(include_personal_workspaces=False)
    
    # Only composite semantic models
    composite_models = all_models[all_models["IsComposite"]].copy()
    
    display(composite_models)

     

    That composite_models DataFrame will give you, per semantic model:

    • Workspace name

    • Dataset / semantic model name

    • All storage modes it uses (from list_partitions)

    • All data source types (from list_datasources)

    • A boolean IsComposite based on the rules above

    You can then:

    • Write it to a Lakehouse table and build a governance semantic model on top,

    • Or slice and dice it directly in the notebook.

    Notes:

    • Permissions: You’ll only see workspaces and semantic models you have rights to. For full-tenant scanning you typically combine this with admin or scanner APIs (often via Semantic Link Labs, not plain SemPy).

    • Definition of “composite”: If you want to be super strict (e.g., “DQ over PBI dataset + local tables only”), tweak the heuristic in classify_composite_for_dataset.

    • Performance: On big tenants, you may want to:

      • Filter list_workspaces() first (e.g., only dedicated capacities, only certain domains), and/or

      • Parallelize per-workspace scans with multiprocessing in Python.