Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fixed add_time_intel in tom, added is_calculated_table, ran black #23

Merged
merged 3 commits into from
Jul 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
# [semantic-link-labs](https://semantic-link-labs.readthedocs.io/en/0.5.0/)
# Semantic Link Labs

[![PyPI version](https://badge.fury.io/py/semantic-link-labs.svg)](https://badge.fury.io/py/semantic-link-labs)
[![Read The Docs](https://readthedocs.org/projects/semantic-link-labs/badge/?version=0.5.0&style=flat)](https://readthedocs.org/projects/semantic-link-labs/)
[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
[![Downloads](https://static.pepy.tech/badge/semantic-link-labs)](https://pepy.tech/project/semantic-link-labs)

All functions in this library are documented [here](https://semantic-link-labs.readthedocs.io/en/0.5.0/)!
---
[Read the documentation on ReadTheDocs!](https://semantic-link-labs.readthedocs.io/en/stable/)
---

This is a python library intended to be used in [Microsoft Fabric notebooks](https://learn.microsoft.com/fabric/data-engineering/how-to-use-notebook). This library was originally intended to contain functions used for [migrating semantic models to Direct Lake mode](https://github.com/microsoft/semantic-link-labs?tab=readme-ov-file#direct-lake-migration). However, it quickly became apparent that functions within such a library could support many other useful activities in the realm of semantic models, reports, lakehouses and really anything Fabric-related. As such, this library contains a variety of functions ranging from running [Vertipaq Analyzer](https://semantic-link-labs.readthedocs.io/en/0.5.0/sempy_labs.html#sempy_labs.import_vertipaq_analyzer) or the [Best Practice Analyzer](https://semantic-link-labs.readthedocs.io/en/0.5.0/sempy_labs.html#sempy_labs.run_model_bpa) against a semantic model to seeing if any [lakehouse tables hit Direct Lake guardrails](https://semantic-link-labs.readthedocs.io/en/0.5.0/sempy_labs.lakehouse.html#sempy_labs.lakehouse.get_lakehouse_tables) or accessing the [Tabular Object Model](https://semantic-link-labs.readthedocs.io/en/0.5.0/sempy_labs.tom.html) and more!
This is a python library intended to be used in [Microsoft Fabric notebooks](https://learn.microsoft.com/fabric/data-engineering/how-to-use-notebook). This library was originally intended to solely contain functions used for [migrating semantic models to Direct Lake mode](https://github.com/microsoft/semantic-link-labs?tab=readme-ov-file#direct-lake-migration). However, it quickly became apparent that functions within such a library could support many other useful activities in the realm of semantic models, reports, lakehouses and really anything Fabric-related. As such, this library contains a variety of functions ranging from running [Vertipaq Analyzer](https://semantic-link-labs.readthedocs.io/en/stable/sempy_labs.html#sempy_labs.import_vertipaq_analyzer) or the [Best Practice Analyzer](https://semantic-link-labs.readthedocs.io/en/stable/sempy_labs.html#sempy_labs.run_model_bpa) against a semantic model to seeing if any [lakehouse tables hit Direct Lake guardrails](https://semantic-link-labs.readthedocs.io/en/stable/sempy_labs.lakehouse.html#sempy_labs.lakehouse.get_lakehouse_tables) or accessing the [Tabular Object Model](https://semantic-link-labs.readthedocs.io/en/stable/sempy_labs.tom.html) and more!

Instructions for migrating import/DirectQuery semantic models to Direct Lake mode can be found [here](https://github.com/microsoft/semantic-link-labs?tab=readme-ov-file#direct-lake-migration).

If you encounter any issues, please [raise a bug](https://github.com/microsoft/semantic-link-labs/issues/new?assignees=&labels=&projects=&template=bug_report.md&title=).

If you have ideas for new features/functions, please [request a feature](https://github.com/microsoft/semantic-link-labs/issues/new?assignees=&labels=&projects=&template=feature_request.md&title=).

## [Function documentation](https://semantic-link-labs.readthedocs.io/en/0.5.0/)

## Install the library in a Fabric notebook
```python
%pip install semantic-link-labs
Expand Down
21 changes: 11 additions & 10 deletions src/sempy_labs/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
resolve_report_name,
# language_validate
)

# from sempy_labs._model_auto_build import model_auto_build
from sempy_labs._model_bpa import model_bpa_rules, run_model_bpa
from sempy_labs._model_dependencies import (
Expand Down Expand Up @@ -125,7 +126,7 @@
#'list_sqlendpoints',
#'list_tables',
"list_warehouses",
'list_workspace_role_assignments',
"list_workspace_role_assignments",
"create_warehouse",
"update_item",
"create_abfss_path",
Expand All @@ -141,20 +142,20 @@
"resolve_report_id",
"resolve_report_name",
#'language_validate',
#"model_auto_build",
# "model_auto_build",
"model_bpa_rules",
"run_model_bpa",
"measure_dependency_tree",
"get_measure_dependencies",
"get_model_calc_dependencies",
"export_model_to_onelake",
'qso_sync',
'qso_sync_status',
'set_qso',
'list_qso_settings',
'disable_qso',
'set_semantic_model_storage_format',
'set_workspace_default_storage_format',
"qso_sync",
"qso_sync_status",
"set_qso",
"list_qso_settings",
"disable_qso",
"set_semantic_model_storage_format",
"set_workspace_default_storage_format",
"refresh_semantic_model",
"cancel_dataset_refresh",
"translate_semantic_model",
Expand All @@ -174,5 +175,5 @@
"delete_user_from_workspace",
"update_workspace_user",
"list_workspace_users",
"assign_workspace_to_dataflow_storage"
"assign_workspace_to_dataflow_storage",
]
37 changes: 26 additions & 11 deletions src/sempy_labs/_ai.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,9 @@ def generate_measure_descriptions(

validModels = ["gpt-35-turbo", "gpt-35-turbo-16k", "gpt-4"]
if gpt_model not in validModels:
raise ValueError(f"{icons.red_dot} The '{gpt_model}' model is not a valid model. Enter a gpt_model from this list: {validModels}.")
raise ValueError(
f"{icons.red_dot} The '{gpt_model}' model is not a valid model. Enter a gpt_model from this list: {validModels}."
)

dfM = fabric.list_measures(dataset=dataset, workspace=workspace)

Expand Down Expand Up @@ -114,8 +116,7 @@ def generate_measure_descriptions(
)

# Update the model to use the new descriptions
#with connect_semantic_model(dataset=dataset, workspace=workspace, readonly=False) as tom:

# with connect_semantic_model(dataset=dataset, workspace=workspace, readonly=False) as tom:

# for t in m.Tables:
# tName = t.Name
Expand Down Expand Up @@ -171,33 +172,43 @@ def generate_aggs(
numericTypes = ["Int64", "Double", "Decimal"]

if any(value not in aggTypes for value in columns.values()):
raise ValueError(f"{icons.red_dot} Invalid aggregation type(s) have been specified in the 'columns' parameter. Valid aggregation types: {aggTypes}.")
raise ValueError(
f"{icons.red_dot} Invalid aggregation type(s) have been specified in the 'columns' parameter. Valid aggregation types: {aggTypes}."
)

dfC = fabric.list_columns(dataset=dataset, workspace=workspace)
dfP = fabric.list_partitions(dataset=dataset, workspace=workspace)
dfM = fabric.list_measures(dataset=dataset, workspace=workspace)
dfR = fabric.list_relationships(dataset=dataset, workspace=workspace)
if not any(r["Mode"] == "DirectLake" for i, r in dfP.iterrows()):
raise ValueError(f"{icons.red_dot} The '{dataset}' semantic model within the '{workspace}' workspace is not in Direct Lake mode. This function is only relevant for Direct Lake semantic models.")

raise ValueError(
f"{icons.red_dot} The '{dataset}' semantic model within the '{workspace}' workspace is not in Direct Lake mode. This function is only relevant for Direct Lake semantic models."
)

dfC_filtT = dfC[dfC["Table Name"] == table_name]

if len(dfC_filtT) == 0:
raise ValueError(f"{icons.red_dot} The '{table_name}' table does not exist in the '{dataset}' semantic model within the '{workspace}' workspace.")
raise ValueError(
f"{icons.red_dot} The '{table_name}' table does not exist in the '{dataset}' semantic model within the '{workspace}' workspace."
)

dfC_filt = dfC[
(dfC["Table Name"] == table_name) & (dfC["Column Name"].isin(columnValues))
]

if len(columns) != len(dfC_filt):
raise ValueError(f"{icons.red_dot} Columns listed in '{columnValues}' do not exist in the '{table_name}' table in the '{dataset}' semantic model within the '{workspace}' workspace.")
raise ValueError(
f"{icons.red_dot} Columns listed in '{columnValues}' do not exist in the '{table_name}' table in the '{dataset}' semantic model within the '{workspace}' workspace."
)

# Check if doing sum/count/min/max etc. on a non-number column
for col, agg in columns.items():
dfC_col = dfC_filt[dfC_filt["Column Name"] == col]
dataType = dfC_col["Data Type"].iloc[0]
if agg in aggTypesAggregate and dataType not in numericTypes:
raise ValueError(f"{icons.red_dot} The '{col}' column in the '{table_name}' table is of '{dataType}' data type. Only columns of '{numericTypes}' data types can be aggregated as '{aggTypesAggregate}' aggregation types.")
raise ValueError(
f"{icons.red_dot} The '{col}' column in the '{table_name}' table is of '{dataType}' data type. Only columns of '{numericTypes}' data types can be aggregated as '{aggTypesAggregate}' aggregation types."
)

# Create/update lakehouse delta agg table
aggSuffix = "_agg"
Expand All @@ -213,7 +224,9 @@ def generate_aggs(
dfI_filt = dfI[(dfI["Id"] == sqlEndpointId)]

if len(dfI_filt) == 0:
raise ValueError(f"{icons.red_dot} The lakehouse (SQL Endpoint) used by the '{dataset}' semantic model does not reside in the '{lakehouse_workspace}' workspace. Please update the lakehouse_workspace parameter.")
raise ValueError(
f"{icons.red_dot} The lakehouse (SQL Endpoint) used by the '{dataset}' semantic model does not reside in the '{lakehouse_workspace}' workspace. Please update the lakehouse_workspace parameter."
)

lakehouseName = dfI_filt["Display Name"].iloc[0]
lakehouse_id = resolve_lakehouse_id(
Expand Down Expand Up @@ -328,7 +341,9 @@ def generate_aggs(
col.DataType = System.Enum.Parse(TOM.DataType, dType)

m.Tables[aggTableName].Columns.Add(col)
print(f"{icons.green_dot} The '{aggTableName}'[{cName}] column has been added.")
print(
f"{icons.green_dot} The '{aggTableName}'[{cName}] column has been added."
)

# Create relationships
relMap = {"m": "Many", "1": "One", "0": "None"}
Expand Down
54 changes: 36 additions & 18 deletions src/sempy_labs/_connections.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,13 +64,19 @@ def create_connection_cloud(
"Connection Id": o.get("id"),
"Connection Name": o.get("name"),
"Connectivity Type": o.get("connectivityType"),
"Connection Type": o.get("connectionDetails",{}).get("type"),
"Connection Path": o.get("connectionDetails",{}).get("path"),
"Connection Type": o.get("connectionDetails", {}).get("type"),
"Connection Path": o.get("connectionDetails", {}).get("path"),
"Privacy Level": o.get("privacyLevel"),
"Credential Type": o.get("credentialDetails",{}).get("credentialType"),
"Single Sign On Type": o.get("credentialDetails",{}).get("singleSignOnType"),
"Connection Encryption": o.get("credentialDetails",{}).get("connectionEncryption"),
"Skip Test Connection": o.get("credentialDetails",{}).get("skipTestConnection"),
"Credential Type": o.get("credentialDetails", {}).get("credentialType"),
"Single Sign On Type": o.get("credentialDetails", {}).get(
"singleSignOnType"
),
"Connection Encryption": o.get("credentialDetails", {}).get(
"connectionEncryption"
),
"Skip Test Connection": o.get("credentialDetails", {}).get(
"skipTestConnection"
),
}
df = pd.concat([df, pd.DataFrame(new_data, index=[0])], ignore_index=True)

Expand Down Expand Up @@ -140,13 +146,19 @@ def create_connection_on_prem(
"Connection Name": o.get("name"),
"Gateway ID": o.get("gatewayId"),
"Connectivity Type": o.get("connectivityType"),
"Connection Type": o.get("connectionDetails",{}).get("type"),
"Connection Path": o.get("connectionDetails",{}).get("path"),
"Connection Type": o.get("connectionDetails", {}).get("type"),
"Connection Path": o.get("connectionDetails", {}).get("path"),
"Privacy Level": o.get("privacyLevel"),
"Credential Type": o.get("credentialDetails",{}).get("credentialType"),
"Single Sign On Type": o.get("credentialDetails",{}).get("singleSignOnType"),
"Connection Encryption": o.get("credentialDetails",{}).get("connectionEncryption"),
"Skip Test Connection": o.get("credentialDetails",{}).get("skipTestConnection"),
"Credential Type": o.get("credentialDetails", {}).get("credentialType"),
"Single Sign On Type": o.get("credentialDetails", {}).get(
"singleSignOnType"
),
"Connection Encryption": o.get("credentialDetails", {}).get(
"connectionEncryption"
),
"Skip Test Connection": o.get("credentialDetails", {}).get(
"skipTestConnection"
),
}
df = pd.concat([df, pd.DataFrame(new_data, index=[0])], ignore_index=True)

Expand Down Expand Up @@ -218,13 +230,19 @@ def create_connection_vnet(
"Connection Name": o.get("name"),
"Gateway ID": o.get("gatewayId"),
"Connectivity Type": o.get("connectivityType"),
"Connection Type": o.get("connectionDetails",{}).get("type"),
"Connection Path": o.get("connectionDetails",{}).get("path"),
"Connection Type": o.get("connectionDetails", {}).get("type"),
"Connection Path": o.get("connectionDetails", {}).get("path"),
"Privacy Level": o.get("privacyLevel"),
"Credential Type": o.get("credentialDetails",{}).get("credentialType"),
"Single Sign On Type": o.get("credentialDetails",{}).get("singleSignOnType"),
"Connection Encryption": o.get("credentialDetails",{}).get("connectionEncryption"),
"Skip Test Connection": o.get("credentialDetails",{}).get("skipTestConnection"),
"Credential Type": o.get("credentialDetails", {}).get("credentialType"),
"Single Sign On Type": o.get("credentialDetails", {}).get(
"singleSignOnType"
),
"Connection Encryption": o.get("credentialDetails", {}).get(
"connectionEncryption"
),
"Skip Test Connection": o.get("credentialDetails", {}).get(
"skipTestConnection"
),
}
df = pd.concat([df, pd.DataFrame(new_data, index=[0])], ignore_index=True)

Expand Down
2 changes: 1 addition & 1 deletion src/sempy_labs/_dax.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ def evaluate_dax_impersonation(

request_body = {
"queries": [{"query": dax_query}],
"impersonatedUserName": user_name
"impersonatedUserName": user_name,
}

client = fabric.PowerBIRestClient()
Expand Down
8 changes: 6 additions & 2 deletions src/sempy_labs/_generate_semantic_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@ def create_blank_semantic_model(
min_compat = 1500

if compatibility_level < min_compat:
raise ValueError(f"{icons.red_dot} Compatiblity level must be at least {min_compat}.")
raise ValueError(
f"{icons.red_dot} Compatiblity level must be at least {min_compat}."
)

tmsl = f"""
{{
Expand Down Expand Up @@ -90,7 +92,9 @@ def create_semantic_model_from_bim(
dfI_filt = dfI[(dfI["Display Name"] == dataset)]

if len(dfI_filt) > 0:
raise ValueError(f"{icons.red_dot} '{dataset}' already exists as a semantic model in the '{workspace}' workspace.")
raise ValueError(
f"{icons.red_dot} '{dataset}' already exists as a semantic model in the '{workspace}' workspace."
)

client = fabric.FabricRestClient()
defPBIDataset = {"version": "1.0", "settings": {}}
Expand Down
18 changes: 13 additions & 5 deletions src/sempy_labs/_helper_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,9 @@ def resolve_dataset_name(dataset_id: UUID, workspace: Optional[str] = None):
return obj


def resolve_lakehouse_name(lakehouse_id: Optional[UUID] = None, workspace: Optional[str] = None):
def resolve_lakehouse_name(
lakehouse_id: Optional[UUID] = None, workspace: Optional[str] = None
):
"""
Obtains the name of the Fabric lakehouse.

Expand All @@ -223,7 +225,7 @@ def resolve_lakehouse_name(lakehouse_id: Optional[UUID] = None, workspace: Optio
if workspace is None:
workspace_id = fabric.get_workspace_id()
workspace = fabric.resolve_workspace_name(workspace_id)

if lakehouse_id is None:
lakehouse_id = fabric.get_lakehouse_id()

Expand Down Expand Up @@ -420,10 +422,14 @@ def save_as_delta_table(
write_mode = write_mode.lower()

if write_mode not in writeModes:
raise ValueError(f"{icons.red_dot} Invalid 'write_type' parameter. Choose from one of the following values: {writeModes}.")
raise ValueError(
f"{icons.red_dot} Invalid 'write_type' parameter. Choose from one of the following values: {writeModes}."
)

if " " in delta_table_name:
raise ValueError(f"{icons.red_dot} Invalid 'delta_table_name'. Delta tables in the lakehouse cannot have spaces in their names.")
raise ValueError(
f"{icons.red_dot} Invalid 'delta_table_name'. Delta tables in the lakehouse cannot have spaces in their names."
)

dataframe.columns = dataframe.columns.str.replace(" ", "_")

Expand Down Expand Up @@ -470,7 +476,9 @@ def language_validate(language: str):
elif len(df_filt2) == 1:
lang = df_filt2["Language"].iloc[0]
else:
raise ValueError(f"{icons.red_dot} The '{language}' language is not a valid language code. Please refer to this link for a list of valid language codes: {url}.")
raise ValueError(
f"{icons.red_dot} The '{language}' language is not a valid language code. Please refer to this link for a list of valid language codes: {url}."
)

return lang

Expand Down
Loading
Loading