refactor(ingest): Tableau cleanup (#6131)

Co-authored-by: Shirshanka Das <shirshanka@apache.org>
This commit is contained in:
Harshal Sheth 2022-10-10 22:52:34 +00:00 committed by GitHub
parent 879894d702
commit d569734193
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 447 additions and 4768 deletions

View File

@ -7,7 +7,7 @@ from typing import Any, Dict, Iterable, List, Optional, Tuple, Union
import dateutil.parser as dp
import tableauserverclient as TSC
from pydantic import root_validator, validator
from pydantic import validator
from pydantic.fields import Field
from tableauserverclient import (
PersonalAccessTokenAuth,
@ -17,7 +17,7 @@ from tableauserverclient import (
)
import datahub.emitter.mce_builder as builder
from datahub.configuration.common import ConfigurationError
from datahub.configuration.common import ConfigModel, ConfigurationError
from datahub.configuration.source_common import DatasetLineageProviderConfigBase
from datahub.emitter.mcp import MetadataChangeProposalWrapper
from datahub.emitter.mcp_builder import (
@ -96,7 +96,7 @@ logger: logging.Logger = logging.getLogger(__name__)
REPLACE_SLASH_CHAR = "|"
class TableauConfig(DatasetLineageProviderConfigBase):
class TableauConnectionConfig(ConfigModel):
connect_uri: str = Field(description="Tableau host URL.")
username: Optional[str] = Field(
default=None,
@ -117,9 +117,44 @@ class TableauConfig(DatasetLineageProviderConfigBase):
site: str = Field(
default="",
description="Tableau Site. Always required for Tableau Online. Use emptystring "
" to connect with Default site on Tableau Server.",
description="Tableau Site. Always required for Tableau Online. Use emptystring to connect with Default site on Tableau Server.",
)
@validator("connect_uri")
def remove_trailing_slash(cls, v):
return config_clean.remove_trailing_slashes(v)
def make_tableau_client(self) -> Server:
# https://tableau.github.io/server-client-python/docs/api-ref#authentication
authentication: Union[TableauAuth, PersonalAccessTokenAuth]
if self.username and self.password:
authentication = TableauAuth(
username=self.username,
password=self.password,
site_id=self.site,
)
elif self.token_name and self.token_value:
authentication = PersonalAccessTokenAuth(
self.token_name, self.token_value, self.site
)
else:
raise ConfigurationError(
"Tableau Source: Either username/password or token_name/token_value must be set"
)
try:
server = Server(self.connect_uri, use_server_version=True)
server.auth.sign_in(authentication)
return server
except ServerResponseError as e:
raise ValueError(
f"Unable to login with credentials provided: {str(e)}"
) from e
except Exception as e:
raise ValueError(f"Unable to login: {str(e)}") from e
class TableauConfig(DatasetLineageProviderConfigBase, TableauConnectionConfig):
projects: Optional[List[str]] = Field(
default=["default"], description="List of projects"
)
@ -139,11 +174,6 @@ class TableauConfig(DatasetLineageProviderConfigBase):
description="Ingest details for tables external to (not embedded in) tableau as entities.",
)
workbooks_page_size: Optional[int] = Field(
default=None,
description="@deprecated(use page_size instead) Number of workbooks to query at a time using Tableau api.",
)
page_size: int = Field(
default=10,
description="Number of metadata objects (e.g. CustomSQLTable, PublishedDatasource, etc) to query at a time using Tableau api.",
@ -164,21 +194,6 @@ class TableauConfig(DatasetLineageProviderConfigBase):
description="[experimental] Extract usage statistics for dashboards and charts.",
)
@validator("connect_uri")
def remove_trailing_slash(cls, v):
return config_clean.remove_trailing_slashes(v)
@root_validator()
def show_warning_for_deprecated_config_field(
cls, values: Dict[str, Any]
) -> Dict[str, Any]:
if values.get("workbooks_page_size") is not None:
logger.warn(
"Config workbooks_page_size is deprecated. Please use config page_size instead."
)
return values
class WorkbookKey(PlatformKey):
workbook_id: str
@ -198,7 +213,6 @@ class UsageStat:
supported=False,
)
@capability(SourceCapability.DOMAINS, "Requires transformer", supported=False)
@capability(SourceCapability.DATA_PROFILING, "", supported=False)
@capability(SourceCapability.DESCRIPTIONS, "Enabled by default")
@capability(
SourceCapability.USAGE_STATS,
@ -207,9 +221,6 @@ class UsageStat:
@capability(SourceCapability.DELETION_DETECTION, "", supported=False)
@capability(SourceCapability.OWNERSHIP, "Requires recipe configuration")
@capability(SourceCapability.TAGS, "Requires recipe configuration")
@capability(
SourceCapability.PARTITION_SUPPORT, "Not applicable to source", supported=False
)
@capability(SourceCapability.LINEAGE_COARSE, "Enabled by default")
class TableauSource(Source):
config: TableauConfig
@ -258,39 +269,16 @@ class TableauSource(Source):
logger.debug("Tableau stats %s", self.tableau_stat_registry)
def _authenticate(self):
# https://tableau.github.io/server-client-python/docs/api-ref#authentication
authentication: Optional[Union[TableauAuth, PersonalAccessTokenAuth]] = None
if self.config.username and self.config.password:
authentication = TableauAuth(
username=self.config.username,
password=self.config.password,
site_id=self.config.site,
)
elif self.config.token_name and self.config.token_value:
authentication = PersonalAccessTokenAuth(
self.config.token_name, self.config.token_value, self.config.site
)
else:
raise ConfigurationError(
"Tableau Source: Either username/password or token_name/token_value must be set"
)
try:
self.server = Server(self.config.connect_uri, use_server_version=True)
self.server.auth.sign_in(authentication)
except ServerResponseError as e:
logger.error(e)
self.server = self.config.make_tableau_client()
# Note that we're not catching ConfigurationError, since we want that to throw.
except ValueError as e:
self.report.report_failure(
key="tableau-login",
reason=f"Unable to Login with credentials provided" f"Reason: {str(e)}",
)
except Exception as e:
logger.error(e)
self.report.report_failure(
key="tableau-login", reason=f"Unable to Login" f"Reason: {str(e)}"
reason=str(e),
)
def get_connection_object(
def get_connection_object_page(
self,
query: str,
connection_type: str,
@ -305,10 +293,11 @@ class TableauSource(Source):
self.server, query, connection_type, count, current_count, query_filter
)
if "errors" in query_data:
self.report.report_warning(
key="tableau-metadata",
reason=f"Connection: {connection_type} Error: {query_data['errors']}",
)
errors = query_data["errors"]
if all(error["extensions"]["severity"] == "WARNING" for error in errors):
self.report.report_warning(key=connection_type, reason=f"{errors}")
else:
raise RuntimeError(f"Query {connection_type} error: {errors}")
connection_object = (
query_data.get("data").get(connection_type, {})
@ -320,23 +309,19 @@ class TableauSource(Source):
has_next_page = connection_object.get("pageInfo", {}).get("hasNextPage", False)
return connection_object, total_count, has_next_page
def emit_workbooks(self) -> Iterable[MetadataWorkUnit]:
count_on_query = (
self.config.page_size
if self.config.workbooks_page_size is None
else self.config.workbooks_page_size
)
def get_connection_objects(
self,
query: str,
connection_type: str,
query_filter: str,
) -> Iterable[dict]:
# Calls the get_connection_object_page function to get the objects,
# and automatically handles pagination.
projects = (
f"projectNameWithin: {json.dumps(self.config.projects)}"
if self.config.projects
else ""
)
workbook_connection, total_count, has_next_page = self.get_connection_object(
workbook_graphql_query, "workbooksConnection", projects
)
count_on_query = self.config.page_size
total_count = count_on_query
has_next_page = 1
current_count = 0
while has_next_page:
count = (
@ -345,25 +330,37 @@ class TableauSource(Source):
else total_count - current_count
)
(
workbook_connection,
connection_objects,
total_count,
has_next_page,
) = self.get_connection_object(
workbook_graphql_query,
"workbooksConnection",
projects,
) = self.get_connection_object_page(
query,
connection_type,
query_filter,
count,
current_count,
)
current_count += count
for workbook in workbook_connection.get("nodes", []):
yield from self.emit_workbook_as_container(workbook)
yield from self.emit_sheets_as_charts(workbook)
yield from self.emit_dashboards(workbook)
for ds in workbook.get("embeddedDatasources", []):
self.embedded_datasource_ids_being_used.append(ds["id"])
for obj in connection_objects.get("nodes", []):
yield obj
def emit_workbooks(self) -> Iterable[MetadataWorkUnit]:
projects = (
f"projectNameWithin: {json.dumps(self.config.projects)}"
if self.config.projects
else ""
)
for workbook in self.get_connection_objects(
workbook_graphql_query, "workbooksConnection", projects
):
yield from self.emit_workbook_as_container(workbook)
yield from self.emit_sheets_as_charts(workbook)
yield from self.emit_dashboards(workbook)
for ds in workbook.get("embeddedDatasources", []):
self.embedded_datasource_ids_being_used.append(ds["id"])
def _track_custom_sql_ids(self, field: dict) -> None:
# Tableau shows custom sql datasource as a table in ColumnField.
@ -484,118 +481,98 @@ class TableauSource(Source):
return upstream_tables
def emit_custom_sql_datasources(self) -> Iterable[MetadataWorkUnit]:
count_on_query = self.config.page_size
custom_sql_filter = f"idWithin: {json.dumps(self.custom_sql_ids_being_used)}"
custom_sql_connection, total_count, has_next_page = self.get_connection_object(
custom_sql_graphql_query, "customSQLTablesConnection", custom_sql_filter
)
current_count = 0
while has_next_page:
count = (
count_on_query
if current_count + count_on_query < total_count
else total_count - current_count
)
(
custom_sql_connection,
total_count,
has_next_page,
) = self.get_connection_object(
custom_sql_connection = list(
self.get_connection_objects(
custom_sql_graphql_query,
"customSQLTablesConnection",
custom_sql_filter,
count,
current_count,
)
current_count += count
)
unique_custom_sql = get_unique_custom_sql(
custom_sql_connection.get("nodes", [])
unique_custom_sql = get_unique_custom_sql(custom_sql_connection)
for csql in unique_custom_sql:
csql_id: str = csql["id"]
csql_urn = builder.make_dataset_urn(self.platform, csql_id, self.config.env)
dataset_snapshot = DatasetSnapshot(
urn=csql_urn,
aspects=[],
)
for csql in unique_custom_sql:
csql_id: str = csql["id"]
csql_urn = builder.make_dataset_urn(
self.platform, csql_id, self.config.env
)
dataset_snapshot = DatasetSnapshot(
urn=csql_urn,
aspects=[],
datasource_name = None
project = None
if len(csql["datasources"]) > 0:
yield from self._create_lineage_from_csql_datasource(
csql_urn, csql["datasources"]
)
datasource_name = None
project = None
if len(csql["datasources"]) > 0:
yield from self._create_lineage_from_csql_datasource(
csql_urn, csql["datasources"]
# CustomSQLTable id owned by exactly one tableau data source
logger.debug(
f"Number of datasources referencing CustomSQLTable: {len(csql['datasources'])}"
)
datasource = csql["datasources"][0]
datasource_name = datasource.get("name")
if datasource.get(
"__typename"
) == "EmbeddedDatasource" and datasource.get("workbook"):
datasource_name = (
f"{datasource.get('workbook').get('name')}/{datasource_name}"
if datasource_name and datasource.get("workbook").get("name")
else None
)
# CustomSQLTable id owned by exactly one tableau data source
logger.debug(
f"Number of datasources referencing CustomSQLTable: {len(csql['datasources'])}"
workunits = add_entity_to_container(
self.gen_workbook_key(datasource["workbook"]),
"dataset",
dataset_snapshot.urn,
)
for wu in workunits:
self.report.report_workunit(wu)
yield wu
project = self._get_project(datasource)
datasource = csql["datasources"][0]
datasource_name = datasource.get("name")
if datasource.get(
"__typename"
) == "EmbeddedDatasource" and datasource.get("workbook"):
datasource_name = (
f"{datasource.get('workbook').get('name')}/{datasource_name}"
if datasource_name
and datasource.get("workbook").get("name")
else None
)
workunits = add_entity_to_container(
self.gen_workbook_key(datasource["workbook"]),
"dataset",
dataset_snapshot.urn,
)
for wu in workunits:
self.report.report_workunit(wu)
yield wu
project = self._get_project(datasource)
# lineage from custom sql -> datasets/tables #
columns = csql.get("columns", [])
yield from self._create_lineage_to_upstream_tables(csql_urn, columns)
# lineage from custom sql -> datasets/tables #
columns = csql.get("columns", [])
yield from self._create_lineage_to_upstream_tables(csql_urn, columns)
# Schema Metadata
schema_metadata = self.get_schema_metadata_for_custom_sql(columns)
if schema_metadata is not None:
dataset_snapshot.aspects.append(schema_metadata)
# Schema Metadata
schema_metadata = self.get_schema_metadata_for_custom_sql(columns)
if schema_metadata is not None:
dataset_snapshot.aspects.append(schema_metadata)
# Browse path
# Browse path
if project and datasource_name:
browse_paths = BrowsePathsClass(
paths=[
f"/{self.config.env.lower()}/{self.platform}/{project}/{datasource['name']}"
]
)
dataset_snapshot.aspects.append(browse_paths)
else:
logger.debug(f"Browse path not set for Custom SQL table {csql_id}")
dataset_properties = DatasetPropertiesClass(
name=csql.get("name"), description=csql.get("description")
if project and datasource_name:
browse_paths = BrowsePathsClass(
paths=[
f"/{self.config.env.lower()}/{self.platform}/{project}/{datasource['name']}"
]
)
dataset_snapshot.aspects.append(browse_paths)
else:
logger.debug(f"Browse path not set for Custom SQL table {csql_id}")
dataset_snapshot.aspects.append(dataset_properties)
dataset_properties = DatasetPropertiesClass(
name=csql.get("name"), description=csql.get("description")
)
view_properties = ViewPropertiesClass(
materialized=False,
viewLanguage="SQL",
viewLogic=clean_query(csql.get("query", "")),
)
dataset_snapshot.aspects.append(view_properties)
dataset_snapshot.aspects.append(dataset_properties)
yield self.get_metadata_change_event(dataset_snapshot)
yield self.get_metadata_change_proposal(
dataset_snapshot.urn,
aspect_name="subTypes",
aspect=SubTypesClass(typeNames=["view", "Custom SQL"]),
)
view_properties = ViewPropertiesClass(
materialized=False,
viewLanguage="SQL",
viewLogic=clean_query(csql.get("query", "")),
)
dataset_snapshot.aspects.append(view_properties)
yield self.get_metadata_change_event(dataset_snapshot)
yield self.get_metadata_change_proposal(
dataset_snapshot.urn,
aspect_name="subTypes",
aspect=SubTypesClass(typeNames=["view", "Custom SQL"]),
)
def get_schema_metadata_for_custom_sql(
self, columns: List[dict]
@ -863,40 +840,14 @@ class TableauSource(Source):
yield wu
def emit_published_datasources(self) -> Iterable[MetadataWorkUnit]:
count_on_query = self.config.page_size
datasource_filter = f"idWithin: {json.dumps(self.datasource_ids_being_used)}"
(
published_datasource_conn,
total_count,
has_next_page,
) = self.get_connection_object(
for datasource in self.get_connection_objects(
published_datasource_graphql_query,
"publishedDatasourcesConnection",
datasource_filter,
)
current_count = 0
while has_next_page:
count = (
count_on_query
if current_count + count_on_query < total_count
else total_count - current_count
)
(
published_datasource_conn,
total_count,
has_next_page,
) = self.get_connection_object(
published_datasource_graphql_query,
"publishedDatasourcesConnection",
datasource_filter,
count,
current_count,
)
current_count += count
for datasource in published_datasource_conn.get("nodes", []):
yield from self.emit_datasource(datasource)
):
yield from self.emit_datasource(datasource)
def emit_upstream_tables(self) -> Iterable[MetadataWorkUnit]:
for (table_urn, (columns, path, is_embedded)) in self.upstream_tables.items():
@ -1056,7 +1007,10 @@ class TableauSource(Source):
lastModified=last_modified,
externalUrl=sheet_external_url,
inputs=sorted(datasource_urn),
customProperties=fields,
customProperties={
"luid": sheet.get("luid") or "",
**{f"field: {k}": v for k, v in fields.items()},
},
)
chart_snapshot.aspects.append(chart_info)
# chart_snapshot doesn't support the stat aspect as list element and hence need to emit MCP
@ -1229,7 +1183,7 @@ class TableauSource(Source):
charts=chart_urns,
lastModified=last_modified,
dashboardUrl=dashboard_external_url,
customProperties={},
customProperties={"luid": dashboard.get("luid") or ""},
)
dashboard_snapshot.aspects.append(dashboard_info_class)
@ -1267,43 +1221,18 @@ class TableauSource(Source):
yield wu
def emit_embedded_datasources(self) -> Iterable[MetadataWorkUnit]:
count_on_query = self.config.page_size
datasource_filter = (
f"idWithin: {json.dumps(self.embedded_datasource_ids_being_used)}"
)
(
embedded_datasource_conn,
total_count,
has_next_page,
) = self.get_connection_object(
for datasource in self.get_connection_objects(
embedded_datasource_graphql_query,
"embeddedDatasourcesConnection",
datasource_filter,
)
current_count = 0
while has_next_page:
count = (
count_on_query
if current_count + count_on_query < total_count
else total_count - current_count
):
yield from self.emit_datasource(
datasource, datasource.get("workbook"), is_embedded_ds=True
)
(
embedded_datasource_conn,
total_count,
has_next_page,
) = self.get_connection_object(
embedded_datasource_graphql_query,
"embeddedDatasourcesConnection",
datasource_filter,
count,
current_count,
)
current_count += count
for datasource in embedded_datasource_conn.get("nodes", []):
yield from self.emit_datasource(
datasource, datasource.get("workbook"), is_embedded_ds=True
)
@lru_cache(maxsize=None)
def _get_schema(self, schema_provided: str, database: str, fullName: str) -> str:

View File

@ -1,12 +0,0 @@
{
"data": {
"customSQLTablesConnection": {
"nodes": [],
"pageInfo": {
"hasNextPage": true,
"endCursor": null
},
"totalCount": 2
}
}
}

View File

@ -1,12 +0,0 @@
{
"data": {
"embeddedDatasourcesConnection": {
"nodes": [],
"pageInfo": {
"hasNextPage": true,
"endCursor": null
},
"totalCount": 8
}
}
}

View File

@ -1,12 +0,0 @@
{
"data": {
"publishedDatasourcesConnection": {
"nodes": [],
"pageInfo": {
"hasNextPage": true,
"endCursor": null
},
"totalCount": 2
}
}
}

View File

@ -1,12 +0,0 @@
{
"data": {
"workbooksConnection": {
"nodes": [],
"pageInfo": {
"hasNextPage": true,
"endCursor": null
},
"totalCount": 3
}
}
}

View File

@ -21,6 +21,7 @@
"path": "",
"createdAt": "2021-12-22T19:10:34Z",
"updatedAt": "2021-12-22T19:10:34Z",
"luid": "f0779f9d-6765-47a9-a8f6-c740cfd27783",
"tags": [],
"containedInDashboards": [
{
@ -957,6 +958,7 @@
"path": "EmailPerformancebyCampaign/EmailPerformancebyCampaign",
"createdAt": "2021-12-22T19:10:34Z",
"updatedAt": "2021-12-22T19:10:34Z",
"luid": "fc9ea488-f810-4fa8-ac19-aa96018b5d66",
"sheets": [
{
"id": "222d1406-de0e-cd8d-0b94-9b45a0007e59",

View File

@ -1,22 +0,0 @@
import pytest
import test_tableau_common
from freezegun import freeze_time
FROZEN_TIME = "2021-12-07 07:00:00"
@freeze_time(FROZEN_TIME)
@pytest.mark.slow_unit
def test_tableau_usage_stat(pytestconfig, tmp_path):
output_file_name: str = "tableau_stat_mces.json"
golden_file_name: str = "tableau_state_mces_golden.json"
side_effect_query_metadata = test_tableau_common.define_query_metadata_func(
"workbooksConnection_0.json", "workbooksConnection_state_all.json"
)
test_tableau_common.tableau_ingest_common(
pytestconfig,
tmp_path,
side_effect_query_metadata,
golden_file_name,
output_file_name,
)

View File

@ -69,6 +69,20 @@
"runId": "tableau-test"
}
},
{
"entityType": "chart",
"entityUrn": "urn:li:chart:(tableau,222d1406-de0e-cd8d-0b94-9b45a0007e59)",
"changeType": "UPSERT",
"aspectName": "chartUsageStatistics",
"aspect": {
"value": "{\"timestampMillis\": 1638860400000, \"partitionSpec\": {\"type\": \"FULL_TABLE\", \"partition\": \"FULL_TABLE_SNAPSHOT\"}, \"viewsCount\": 5}",
"contentType": "application/json"
},
"systemMetadata": {
"lastObserved": 1638860400000,
"runId": "tableau-test"
}
},
{
"proposedSnapshot": {
"com.linkedin.pegasus2avro.metadata.snapshot.ChartSnapshot": {
@ -77,12 +91,13 @@
{
"com.linkedin.pegasus2avro.chart.ChartInfo": {
"customProperties": {
"Name": "",
"Activity Date": "",
"ID": "",
"Program Name": "",
"Active": "",
"Id": ""
"luid": "f0779f9d-6765-47a9-a8f6-c740cfd27783",
"field: Name": "",
"field: Activity Date": "",
"field: ID": "",
"field: Program Name": "",
"field: Active": "",
"field: Id": ""
},
"externalUrl": "https://do-not-connect/t/acryl/authoring/EmailPerformancebyCampaign/EmailPerformancebyCampaign/Timeline - Sent",
"title": "Timeline - Sent",
@ -155,23 +170,24 @@
{
"com.linkedin.pegasus2avro.chart.ChartInfo": {
"customProperties": {
"Delivery Rate": "formula: ZN([Delivered Email]/[Sent Email])",
"Name": "",
"Id (Activity - Click Email)": "",
"Activity Date": "",
"Clickthrough Rate": "formula: [Clickthrough Emails]/[Delivered Email]",
"Open Rate": "formula: ZN([Opened Email]/[Delivered Email])",
"Sent Email": "formula: COUNTD([Id])",
"Delivered Email": "formula: COUNTD([Id (Activity - Email Delivered)])",
"ID": "",
"Id (Activity - Open Email)": "",
"Clickthrough Emails": "formula: COUNTD([Id (Activity - Click Email)])",
"Click-to-Open": "formula: ZN([Clickthrough Emails]\r\n/ \r\n[Opened Email])",
"Opened Email": "formula: COUNTD([Id (Activity - Open Email)])",
"Id (Activity - Email Delivered)": "",
"Program Name": "",
"Active": "",
"Id": ""
"luid": "",
"field: Delivery Rate": "formula: ZN([Delivered Email]/[Sent Email])",
"field: Name": "",
"field: Id (Activity - Click Email)": "",
"field: Activity Date": "",
"field: Clickthrough Rate": "formula: [Clickthrough Emails]/[Delivered Email]",
"field: Open Rate": "formula: ZN([Opened Email]/[Delivered Email])",
"field: Sent Email": "formula: COUNTD([Id])",
"field: Delivered Email": "formula: COUNTD([Id (Activity - Email Delivered)])",
"field: ID": "",
"field: Id (Activity - Open Email)": "",
"field: Clickthrough Emails": "formula: COUNTD([Id (Activity - Click Email)])",
"field: Click-to-Open": "formula: ZN([Clickthrough Emails]\r\n/ \r\n[Opened Email])",
"field: Opened Email": "formula: COUNTD([Id (Activity - Open Email)])",
"field: Id (Activity - Email Delivered)": "",
"field: Program Name": "",
"field: Active": "",
"field: Id": ""
},
"externalUrl": "https://do-not-connect/t/acryl/authoring/EmailPerformancebyCampaign/EmailPerformancebyCampaign/Campaign List",
"title": "Campaign List",
@ -244,25 +260,26 @@
{
"com.linkedin.pegasus2avro.chart.ChartInfo": {
"customProperties": {
"Delivery Rate": "formula: ZN([Delivered Email]/[Sent Email])",
"Name": "",
"Id (Activity - Click Email)": "",
"Activity Date": "",
"Clickthrough Rate": "formula: [Clickthrough Emails]/[Delivered Email]",
"Open Rate": "formula: ZN([Opened Email]/[Delivered Email])",
"Measure Names": "",
"Sent Email": "formula: COUNTD([Id])",
"Delivered Email": "formula: COUNTD([Id (Activity - Email Delivered)])",
"ID": "",
"Id (Activity - Open Email)": "",
"Clickthrough Emails": "formula: COUNTD([Id (Activity - Click Email)])",
"Click-to-Open": "formula: ZN([Clickthrough Emails]\r\n/ \r\n[Opened Email])",
"Opened Email": "formula: COUNTD([Id (Activity - Open Email)])",
"Id (Activity - Email Delivered)": "",
"Program Name": "",
"Measure Values": "",
"Active": "",
"Id": ""
"luid": "",
"field: Delivery Rate": "formula: ZN([Delivered Email]/[Sent Email])",
"field: Name": "",
"field: Id (Activity - Click Email)": "",
"field: Activity Date": "",
"field: Clickthrough Rate": "formula: [Clickthrough Emails]/[Delivered Email]",
"field: Open Rate": "formula: ZN([Opened Email]/[Delivered Email])",
"field: Measure Names": "",
"field: Sent Email": "formula: COUNTD([Id])",
"field: Delivered Email": "formula: COUNTD([Id (Activity - Email Delivered)])",
"field: ID": "",
"field: Id (Activity - Open Email)": "",
"field: Clickthrough Emails": "formula: COUNTD([Id (Activity - Click Email)])",
"field: Click-to-Open": "formula: ZN([Clickthrough Emails]\r\n/ \r\n[Opened Email])",
"field: Opened Email": "formula: COUNTD([Id (Activity - Open Email)])",
"field: Id (Activity - Email Delivered)": "",
"field: Program Name": "",
"field: Measure Values": "",
"field: Active": "",
"field: Id": ""
},
"externalUrl": "https://do-not-connect/t/acryl/authoring/EmailPerformancebyCampaign/EmailPerformancebyCampaign/Summary",
"title": "Summary",
@ -335,21 +352,22 @@
{
"com.linkedin.pegasus2avro.chart.ChartInfo": {
"customProperties": {
"Delivery Rate": "formula: ZN([Delivered Email]/[Sent Email])",
"Name": "",
"Id (Activity - Click Email)": "",
"Activity Date": "",
"Open Rate": "formula: ZN([Opened Email]/[Delivered Email])",
"Sent Email": "formula: COUNTD([Id])",
"Delivered Email": "formula: COUNTD([Id (Activity - Email Delivered)])",
"ID": "",
"Id (Activity - Open Email)": "",
"Clickthrough Emails": "formula: COUNTD([Id (Activity - Click Email)])",
"Click-to-Open": "formula: ZN([Clickthrough Emails]\r\n/ \r\n[Opened Email])",
"Opened Email": "formula: COUNTD([Id (Activity - Open Email)])",
"Id (Activity - Email Delivered)": "",
"Program Name": "",
"Id": ""
"luid": "",
"field: Delivery Rate": "formula: ZN([Delivered Email]/[Sent Email])",
"field: Name": "",
"field: Id (Activity - Click Email)": "",
"field: Activity Date": "",
"field: Open Rate": "formula: ZN([Opened Email]/[Delivered Email])",
"field: Sent Email": "formula: COUNTD([Id])",
"field: Delivered Email": "formula: COUNTD([Id (Activity - Email Delivered)])",
"field: ID": "",
"field: Id (Activity - Open Email)": "",
"field: Clickthrough Emails": "formula: COUNTD([Id (Activity - Click Email)])",
"field: Click-to-Open": "formula: ZN([Clickthrough Emails]\r\n/ \r\n[Opened Email])",
"field: Opened Email": "formula: COUNTD([Id (Activity - Open Email)])",
"field: Id (Activity - Email Delivered)": "",
"field: Program Name": "",
"field: Id": ""
},
"externalUrl": "https://do-not-connect/t/acryl/authoring/EmailPerformancebyCampaign/EmailPerformancebyCampaign/Mobile - Sent by Campaign",
"title": "Mobile - Sent by Campaign",
@ -414,6 +432,20 @@
"runId": "tableau-test"
}
},
{
"entityType": "dashboard",
"entityUrn": "urn:li:dashboard:(tableau,8f7dd564-36b6-593f-3c6f-687ad06cd40b)",
"changeType": "UPSERT",
"aspectName": "dashboardUsageStatistics",
"aspect": {
"value": "{\"timestampMillis\": 1638860400000, \"partitionSpec\": {\"type\": \"FULL_TABLE\", \"partition\": \"FULL_TABLE_SNAPSHOT\"}, \"viewsCount\": 3}",
"contentType": "application/json"
},
"systemMetadata": {
"lastObserved": 1638860400000,
"runId": "tableau-test"
}
},
{
"proposedSnapshot": {
"com.linkedin.pegasus2avro.metadata.snapshot.DashboardSnapshot": {
@ -421,7 +453,9 @@
"aspects": [
{
"com.linkedin.pegasus2avro.dashboard.DashboardInfo": {
"customProperties": {},
"customProperties": {
"luid": "fc9ea488-f810-4fa8-ac19-aa96018b5d66"
},
"title": "Email Performance by Campaign",
"description": "",
"charts": [
@ -579,7 +613,8 @@
{
"com.linkedin.pegasus2avro.chart.ChartInfo": {
"customProperties": {
"Custom SQL Query": ""
"luid": "",
"field: Custom SQL Query": ""
},
"externalUrl": "https://do-not-connect/#/site/acryl/views/dvdrental/Sheet1",
"title": "Sheet 1",
@ -652,13 +687,14 @@
{
"com.linkedin.pegasus2avro.chart.ChartInfo": {
"customProperties": {
"payment_date": "",
"amount": "",
"Custom SQL Query": "",
"First Name": "",
"customer_id": "",
"rental_id": "",
"Last Name": ""
"luid": "",
"field: payment_date": "",
"field: amount": "",
"field: Custom SQL Query": "",
"field: First Name": "",
"field: customer_id": "",
"field: rental_id": "",
"field: Last Name": ""
},
"externalUrl": "https://do-not-connect/#/site/acryl/views/dvdrental/Sheet2",
"title": "Sheet 2",
@ -731,9 +767,10 @@
{
"com.linkedin.pegasus2avro.chart.ChartInfo": {
"customProperties": {
"Category": "",
"Segment": "",
"Customer Name": ""
"luid": "",
"field: Category": "",
"field: Segment": "",
"field: Customer Name": ""
},
"externalUrl": "https://do-not-connect/#/site/acryl/views/dvdrental/Sheet3",
"title": "Sheet 3",
@ -814,7 +851,9 @@
"aspects": [
{
"com.linkedin.pegasus2avro.dashboard.DashboardInfo": {
"customProperties": {},
"customProperties": {
"luid": ""
},
"title": "dvd Rental Dashboard",
"description": "",
"charts": [
@ -884,7 +923,9 @@
"aspects": [
{
"com.linkedin.pegasus2avro.dashboard.DashboardInfo": {
"customProperties": {},
"customProperties": {
"luid": ""
},
"title": "Story 1",
"description": "",
"charts": [],
@ -1023,17 +1064,18 @@
{
"com.linkedin.pegasus2avro.chart.ChartInfo": {
"customProperties": {
"Opened": "",
"Total # Request": "formula: // This is a calculated field\r\n// It shows the total number of problems ignoring opened date\r\n\r\n{ EXCLUDE [Opened]: COUNTD([Number])}",
"Due date": "",
"Priority": "",
"Overdue": "formula: // This is a calculated field\r\n// It shows if an accident is overdue\r\n\r\n//Check overdue cases among inactive incidents\r\nIF [Active]=FALSE \r\nAND\r\n[Closed]>[Due date]\r\n\r\nOR\r\n//Check overdue cases among active incidents\r\n[Active]=TRUE \r\nAND NOW()>[Due date]\r\n\r\nTHEN \"Overdue\"\r\nELSE \"Non Overdue\"\r\nEND",
"Active": "",
"Current Year Total Cases": "formula: // This is a calculated field\r\n// It counts each distinct request made in the last year. The \"exclude\" is used to avoid filters interfering in the final result\r\n\r\n{EXCLUDE [Opened], [Overdue], [Max Year?]: \r\nCOUNTD(IF [Max Year?]=TRUE\r\nTHEN [Number]\r\nEND)\r\n}",
"Max Year?": "formula: // This is a calculated field\r\n// It shows TRUE/FALSE if the year of opened is equal the maximum year in the dataset\r\n\r\nDATEPART(\"year\", [Opened]) = DATEPART(\"year\", {MAX([Opened])})",
"Total Active Requests": "formula: // This is a calculated field\r\n// It counts each distinct active request. The \"exclude\" is used to avoid filters interfering with the final result \r\n\r\n{EXCLUDE [Opened], [Overdue], [Max Year?]: \r\nCOUNTD(IF [Active]=TRUE\r\nTHEN [Number]\r\nEND)\r\n}",
"Number": "",
"Closed": ""
"luid": "",
"field: Opened": "",
"field: Total # Request": "formula: // This is a calculated field\r\n// It shows the total number of problems ignoring opened date\r\n\r\n{ EXCLUDE [Opened]: COUNTD([Number])}",
"field: Due date": "",
"field: Priority": "",
"field: Overdue": "formula: // This is a calculated field\r\n// It shows if an accident is overdue\r\n\r\n//Check overdue cases among inactive incidents\r\nIF [Active]=FALSE \r\nAND\r\n[Closed]>[Due date]\r\n\r\nOR\r\n//Check overdue cases among active incidents\r\n[Active]=TRUE \r\nAND NOW()>[Due date]\r\n\r\nTHEN \"Overdue\"\r\nELSE \"Non Overdue\"\r\nEND",
"field: Active": "",
"field: Current Year Total Cases": "formula: // This is a calculated field\r\n// It counts each distinct request made in the last year. The \"exclude\" is used to avoid filters interfering in the final result\r\n\r\n{EXCLUDE [Opened], [Overdue], [Max Year?]: \r\nCOUNTD(IF [Max Year?]=TRUE\r\nTHEN [Number]\r\nEND)\r\n}",
"field: Max Year?": "formula: // This is a calculated field\r\n// It shows TRUE/FALSE if the year of opened is equal the maximum year in the dataset\r\n\r\nDATEPART(\"year\", [Opened]) = DATEPART(\"year\", {MAX([Opened])})",
"field: Total Active Requests": "formula: // This is a calculated field\r\n// It counts each distinct active request. The \"exclude\" is used to avoid filters interfering with the final result \r\n\r\n{EXCLUDE [Opened], [Overdue], [Max Year?]: \r\nCOUNTD(IF [Active]=TRUE\r\nTHEN [Number]\r\nEND)\r\n}",
"field: Number": "",
"field: Closed": ""
},
"externalUrl": "https://do-not-connect/t/acryl/authoring/ExecutiveDashboard/ExecutiveDashboard/Opened Requests",
"title": "Opened Requests",
@ -1109,16 +1151,17 @@
{
"com.linkedin.pegasus2avro.chart.ChartInfo": {
"customProperties": {
"Opened": "",
"Due date": "",
"Priority": "",
"Name": "",
"Overdue": "formula: // This is a calculated field\r\n// It shows if an accident is overdue\r\n\r\n//Check overdue cases among inactive incidents\r\nIF [Active]=FALSE \r\nAND\r\n[Closed]>[Due date]\r\n\r\nOR\r\n//Check overdue cases among active incidents\r\n[Active]=TRUE \r\nAND NOW()>[Due date]\r\n\r\nTHEN \"Overdue\"\r\nELSE \"Non Overdue\"\r\nEND",
"Active": "",
"Current Year Total Cases": "formula: // This is a calculated field\r\n// It counts each distinct request made in the last year. The \"exclude\" is used to avoid filters interfering in the final result\r\n\r\n{EXCLUDE [Opened], [Overdue], [Max Year?]: \r\nCOUNTD(IF [Max Year?]=TRUE\r\nTHEN [Number]\r\nEND)\r\n}",
"Max Year?": "formula: // This is a calculated field\r\n// It shows TRUE/FALSE if the year of opened is equal the maximum year in the dataset\r\n\r\nDATEPART(\"year\", [Opened]) = DATEPART(\"year\", {MAX([Opened])})",
"Number": "",
"Closed": ""
"luid": "",
"field: Opened": "",
"field: Due date": "",
"field: Priority": "",
"field: Name": "",
"field: Overdue": "formula: // This is a calculated field\r\n// It shows if an accident is overdue\r\n\r\n//Check overdue cases among inactive incidents\r\nIF [Active]=FALSE \r\nAND\r\n[Closed]>[Due date]\r\n\r\nOR\r\n//Check overdue cases among active incidents\r\n[Active]=TRUE \r\nAND NOW()>[Due date]\r\n\r\nTHEN \"Overdue\"\r\nELSE \"Non Overdue\"\r\nEND",
"field: Active": "",
"field: Current Year Total Cases": "formula: // This is a calculated field\r\n// It counts each distinct request made in the last year. The \"exclude\" is used to avoid filters interfering in the final result\r\n\r\n{EXCLUDE [Opened], [Overdue], [Max Year?]: \r\nCOUNTD(IF [Max Year?]=TRUE\r\nTHEN [Number]\r\nEND)\r\n}",
"field: Max Year?": "formula: // This is a calculated field\r\n// It shows TRUE/FALSE if the year of opened is equal the maximum year in the dataset\r\n\r\nDATEPART(\"year\", [Opened]) = DATEPART(\"year\", {MAX([Opened])})",
"field: Number": "",
"field: Closed": ""
},
"externalUrl": "https://do-not-connect/t/acryl/authoring/ExecutiveDashboard/ExecutiveDashboard/Top 10 Items by Requests and YoY Change",
"title": "Top 10 Items by Requests and YoY Change",
@ -1194,17 +1237,18 @@
{
"com.linkedin.pegasus2avro.chart.ChartInfo": {
"customProperties": {
"Overdue": "formula: // This is a calculated field\r\n// It checks if an incident is overdue\r\n\r\n//Check overdue cases among inactive incidents\r\n{ FIXED [Number]:\r\n\r\nIF MAX([Active]=FALSE) \r\nAND\r\nmax([Closed])> max([Due date])\r\n\r\nOR\r\n//Check overdue cases among active incidents\r\nMAX([Active]=TRUE) \r\nAND NOW()> MAX([Due date]) \r\n\r\nTHEN \"Overdue\"\r\nEND\r\n}",
"Number": "",
"Priority": "",
"Due date": "",
"Total # Problems": "formula: // This is a calculated field using a level of detail calculation (LOD)\r\n// It counts each distinct problems ignoring date\r\n\r\n{ EXCLUDE [Opened]: COUNTD([Number])}",
"Opened": "",
"Max Year?": "formula: // This is a calculated field\r\n// It shows TRUE/FALSE if the year of opened is equal the max year of the dataset \r\n\r\nDATEPART(\"year\", [Opened]) = DATEPART(\"year\", {MAX([Opened])})",
"Closed": "",
"Active": "",
"Current Year Total Cases": "formula: // This is a calculated field\r\n// It counts each disctinct problem. The \"exclude\" is used to avoid filters interfering with the result\r\n\r\n{EXCLUDE [Opened], [Overdue], [Max Year?]: \r\nCOUNTD(IF [Max Year?]=TRUE\r\nTHEN [Number]\r\nEND)\r\n}",
"Total Active Problems": "formula: // This is a calculated field using a level of detail calculation (LOD)\r\n// It counts each distinct active problem. The \"exclude\" is used to avoid filters interfering with the result \r\n\r\n{EXCLUDE [Opened], [Overdue], [Max Year?]: \r\nCOUNTD(IF [Active]=TRUE\r\nTHEN [Number]\r\nEND)\r\n}"
"luid": "",
"field: Overdue": "formula: // This is a calculated field\r\n// It checks if an incident is overdue\r\n\r\n//Check overdue cases among inactive incidents\r\n{ FIXED [Number]:\r\n\r\nIF MAX([Active]=FALSE) \r\nAND\r\nmax([Closed])> max([Due date])\r\n\r\nOR\r\n//Check overdue cases among active incidents\r\nMAX([Active]=TRUE) \r\nAND NOW()> MAX([Due date]) \r\n\r\nTHEN \"Overdue\"\r\nEND\r\n}",
"field: Number": "",
"field: Priority": "",
"field: Due date": "",
"field: Total # Problems": "formula: // This is a calculated field using a level of detail calculation (LOD)\r\n// It counts each distinct problems ignoring date\r\n\r\n{ EXCLUDE [Opened]: COUNTD([Number])}",
"field: Opened": "",
"field: Max Year?": "formula: // This is a calculated field\r\n// It shows TRUE/FALSE if the year of opened is equal the max year of the dataset \r\n\r\nDATEPART(\"year\", [Opened]) = DATEPART(\"year\", {MAX([Opened])})",
"field: Closed": "",
"field: Active": "",
"field: Current Year Total Cases": "formula: // This is a calculated field\r\n// It counts each disctinct problem. The \"exclude\" is used to avoid filters interfering with the result\r\n\r\n{EXCLUDE [Opened], [Overdue], [Max Year?]: \r\nCOUNTD(IF [Max Year?]=TRUE\r\nTHEN [Number]\r\nEND)\r\n}",
"field: Total Active Problems": "formula: // This is a calculated field using a level of detail calculation (LOD)\r\n// It counts each distinct active problem. The \"exclude\" is used to avoid filters interfering with the result \r\n\r\n{EXCLUDE [Opened], [Overdue], [Max Year?]: \r\nCOUNTD(IF [Active]=TRUE\r\nTHEN [Number]\r\nEND)\r\n}"
},
"externalUrl": "https://do-not-connect/t/acryl/authoring/ExecutiveDashboard/ExecutiveDashboard/Opened Problems",
"title": "Opened Problems",
@ -1280,16 +1324,17 @@
{
"com.linkedin.pegasus2avro.chart.ChartInfo": {
"customProperties": {
"% of Overdue": "formula: // This is a calculated field\r\n// It show the percentage incidents which are overdue\r\n\r\n(IF ATTR([Overdue]=\"Overdue\")\r\nTHEN COUNTD([Number])\r\nEND)\r\n/\r\nATTR({ EXCLUDE [Overdue]:\r\nCOUNTD([Number])})",
"Priority": "",
"Category (Incident)": "",
"Overdue": "formula: // This is a calculated field\r\n// It shows if an incident is overdue\r\n\r\n//Check overdue cases among inactive incidents\r\n{ FIXED [Number]:\r\n\r\nIF MAX([Active]=FALSE) \r\nAND\r\nmax([Closed])>max([Due date])\r\n\r\nOR\r\n//Check overdue cases among active incidents\r\nMAX([Active] = TRUE) \r\nAND NOW() > MAX([Due date]) \r\n\r\nTHEN \"Overdue\"\r\nEND\r\n}",
"Number": "",
"Opened": "",
"Max Year?": "formula: // This is a calculated field\r\n// It shows TRUE/FALSE if opened date equals maximum year in the dataset\r\n\r\nDATEPART(\"year\", [Opened]) = DATEPART(\"year\", {MAX([Opened])})",
"Due date": "",
"Active": "",
"Closed": ""
"luid": "",
"field: % of Overdue": "formula: // This is a calculated field\r\n// It show the percentage incidents which are overdue\r\n\r\n(IF ATTR([Overdue]=\"Overdue\")\r\nTHEN COUNTD([Number])\r\nEND)\r\n/\r\nATTR({ EXCLUDE [Overdue]:\r\nCOUNTD([Number])})",
"field: Priority": "",
"field: Category (Incident)": "",
"field: Overdue": "formula: // This is a calculated field\r\n// It shows if an incident is overdue\r\n\r\n//Check overdue cases among inactive incidents\r\n{ FIXED [Number]:\r\n\r\nIF MAX([Active]=FALSE) \r\nAND\r\nmax([Closed])>max([Due date])\r\n\r\nOR\r\n//Check overdue cases among active incidents\r\nMAX([Active] = TRUE) \r\nAND NOW() > MAX([Due date]) \r\n\r\nTHEN \"Overdue\"\r\nEND\r\n}",
"field: Number": "",
"field: Opened": "",
"field: Max Year?": "formula: // This is a calculated field\r\n// It shows TRUE/FALSE if opened date equals maximum year in the dataset\r\n\r\nDATEPART(\"year\", [Opened]) = DATEPART(\"year\", {MAX([Opened])})",
"field: Due date": "",
"field: Active": "",
"field: Closed": ""
},
"externalUrl": "https://do-not-connect/t/acryl/authoring/ExecutiveDashboard/ExecutiveDashboard/Overdue",
"title": "Overdue",
@ -1362,12 +1407,13 @@
{
"com.linkedin.pegasus2avro.chart.ChartInfo": {
"customProperties": {
"Number": "",
"Priority": "",
"Opened": "",
"Max Year?": "formula: // This is a calculated field\r\n// It shows TRUE/FALSE if the year of opened is equal the max year of the dataset \r\n\r\nDATEPART(\"year\", [Opened]) = DATEPART(\"year\", {MAX([Opened])})",
"% of critical and high priority": "formula: // This is a calculated field\r\n// It shows the percentage of critical or high priority problems\r\n\r\nCOUNTD(IF [Priority]=1\r\nOR [Priority]=2\r\nTHEN [Number]\r\nEND)\r\n/\r\nCOUNTD([Number])",
"Active": ""
"luid": "",
"field: Number": "",
"field: Priority": "",
"field: Opened": "",
"field: Max Year?": "formula: // This is a calculated field\r\n// It shows TRUE/FALSE if the year of opened is equal the max year of the dataset \r\n\r\nDATEPART(\"year\", [Opened]) = DATEPART(\"year\", {MAX([Opened])})",
"field: % of critical and high priority": "formula: // This is a calculated field\r\n// It shows the percentage of critical or high priority problems\r\n\r\nCOUNTD(IF [Priority]=1\r\nOR [Priority]=2\r\nTHEN [Number]\r\nEND)\r\n/\r\nCOUNTD([Number])",
"field: Active": ""
},
"externalUrl": "https://do-not-connect/t/acryl/authoring/ExecutiveDashboard/ExecutiveDashboard/High and Critical Priority Problems",
"title": "High and Critical Priority Problems",
@ -1443,16 +1489,17 @@
{
"com.linkedin.pegasus2avro.chart.ChartInfo": {
"customProperties": {
"Priority": "",
"Current Year Total Cases": "formula: // This is a calculated field using level of detail calculation\r\n// It counts each distinct incident. The exclude is used to guarantee that filters will not interfere in the result\r\n\r\n\r\n{EXCLUDE [Opened], [Overdue], [Max Year?]: \r\nCOUNTD(IF [Max Year?]=TRUE\r\nTHEN [Number]\r\nEND)\r\n}",
"Category (Incident)": "",
"Overdue": "formula: // This is a calculated field\r\n// It shows if an incident is overdue\r\n\r\n//Check overdue cases among inactive incidents\r\n{ FIXED [Number]:\r\n\r\nIF MAX([Active]=FALSE) \r\nAND\r\nmax([Closed])>max([Due date])\r\n\r\nOR\r\n//Check overdue cases among active incidents\r\nMAX([Active] = TRUE) \r\nAND NOW() > MAX([Due date]) \r\n\r\nTHEN \"Overdue\"\r\nEND\r\n}",
"Number": "",
"Opened": "",
"Max Year?": "formula: // This is a calculated field\r\n// It shows TRUE/FALSE if opened date equals maximum year in the dataset\r\n\r\nDATEPART(\"year\", [Opened]) = DATEPART(\"year\", {MAX([Opened])})",
"Due date": "",
"Active": "",
"Closed": ""
"luid": "",
"field: Priority": "",
"field: Current Year Total Cases": "formula: // This is a calculated field using level of detail calculation\r\n// It counts each distinct incident. The exclude is used to guarantee that filters will not interfere in the result\r\n\r\n\r\n{EXCLUDE [Opened], [Overdue], [Max Year?]: \r\nCOUNTD(IF [Max Year?]=TRUE\r\nTHEN [Number]\r\nEND)\r\n}",
"field: Category (Incident)": "",
"field: Overdue": "formula: // This is a calculated field\r\n// It shows if an incident is overdue\r\n\r\n//Check overdue cases among inactive incidents\r\n{ FIXED [Number]:\r\n\r\nIF MAX([Active]=FALSE) \r\nAND\r\nmax([Closed])>max([Due date])\r\n\r\nOR\r\n//Check overdue cases among active incidents\r\nMAX([Active] = TRUE) \r\nAND NOW() > MAX([Due date]) \r\n\r\nTHEN \"Overdue\"\r\nEND\r\n}",
"field: Number": "",
"field: Opened": "",
"field: Max Year?": "formula: // This is a calculated field\r\n// It shows TRUE/FALSE if opened date equals maximum year in the dataset\r\n\r\nDATEPART(\"year\", [Opened]) = DATEPART(\"year\", {MAX([Opened])})",
"field: Due date": "",
"field: Active": "",
"field: Closed": ""
},
"externalUrl": "https://do-not-connect/t/acryl/authoring/ExecutiveDashboard/ExecutiveDashboard/Total Incidents by Category and YoY Change",
"title": "Total Incidents by Category and YoY Change",
@ -1525,15 +1572,16 @@
{
"com.linkedin.pegasus2avro.chart.ChartInfo": {
"customProperties": {
"Number": "",
"Priority": "",
"Problems with Related Incidents": "formula: // This is a calculated field\r\n// It counts each distinct problems which has a related incident\r\n\r\nCOUNT(IF ([Related Incidents]>0\r\nAND NOT ISNULL([Related Incidents]))=true\r\nTHEN [Number]\r\nEND)",
"Related Incidents": "",
"Opened": "",
"% of known error": "formula: // This is a calculated field\r\n// It shows the percentage of problems that are known errors\r\n\r\nCOUNTD(IF [Known error]=TRUE\r\nTHEN [Number] END)\r\n/\r\nCOUNTD([Number])",
"Max Year?": "formula: // This is a calculated field\r\n// It shows TRUE/FALSE if the year of opened is equal the max year of the dataset \r\n\r\nDATEPART(\"year\", [Opened]) = DATEPART(\"year\", {MAX([Opened])})",
"Known error": "",
"Active": ""
"luid": "",
"field: Number": "",
"field: Priority": "",
"field: Problems with Related Incidents": "formula: // This is a calculated field\r\n// It counts each distinct problems which has a related incident\r\n\r\nCOUNT(IF ([Related Incidents]>0\r\nAND NOT ISNULL([Related Incidents]))=true\r\nTHEN [Number]\r\nEND)",
"field: Related Incidents": "",
"field: Opened": "",
"field: % of known error": "formula: // This is a calculated field\r\n// It shows the percentage of problems that are known errors\r\n\r\nCOUNTD(IF [Known error]=TRUE\r\nTHEN [Number] END)\r\n/\r\nCOUNTD([Number])",
"field: Max Year?": "formula: // This is a calculated field\r\n// It shows TRUE/FALSE if the year of opened is equal the max year of the dataset \r\n\r\nDATEPART(\"year\", [Opened]) = DATEPART(\"year\", {MAX([Opened])})",
"field: Known error": "",
"field: Active": ""
},
"externalUrl": "https://do-not-connect/t/acryl/authoring/ExecutiveDashboard/ExecutiveDashboard/Known Errors",
"title": "Known Errors",
@ -1609,14 +1657,15 @@
{
"com.linkedin.pegasus2avro.chart.ChartInfo": {
"customProperties": {
"Opened": "",
"Due date": "",
"Priority": "",
"Overdue": "formula: // This is a calculated field\r\n// It shows if an accident is overdue\r\n\r\n//Check overdue cases among inactive incidents\r\nIF [Active]=FALSE \r\nAND\r\n[Closed]>[Due date]\r\n\r\nOR\r\n//Check overdue cases among active incidents\r\n[Active]=TRUE \r\nAND NOW()>[Due date]\r\n\r\nTHEN \"Overdue\"\r\nELSE \"Non Overdue\"\r\nEND",
"Active": "",
"% of Overdue": "formula: // This is a calculated field\r\n// It shows the percentage of incidents which are overdue\r\n\r\n(IF ATTR([Overdue]= \"Overdue\")\r\nTHEN COUNTD([Number])\r\nEND)\r\n/\r\nATTR({ EXCLUDE [Overdue]:\r\nCOUNTD([Number])})",
"Number": "",
"Closed": ""
"luid": "",
"field: Opened": "",
"field: Due date": "",
"field: Priority": "",
"field: Overdue": "formula: // This is a calculated field\r\n// It shows if an accident is overdue\r\n\r\n//Check overdue cases among inactive incidents\r\nIF [Active]=FALSE \r\nAND\r\n[Closed]>[Due date]\r\n\r\nOR\r\n//Check overdue cases among active incidents\r\n[Active]=TRUE \r\nAND NOW()>[Due date]\r\n\r\nTHEN \"Overdue\"\r\nELSE \"Non Overdue\"\r\nEND",
"field: Active": "",
"field: % of Overdue": "formula: // This is a calculated field\r\n// It shows the percentage of incidents which are overdue\r\n\r\n(IF ATTR([Overdue]= \"Overdue\")\r\nTHEN COUNTD([Number])\r\nEND)\r\n/\r\nATTR({ EXCLUDE [Overdue]:\r\nCOUNTD([Number])})",
"field: Number": "",
"field: Closed": ""
},
"externalUrl": "https://do-not-connect/t/acryl/authoring/ExecutiveDashboard/ExecutiveDashboard/Overdue Requests",
"title": "Overdue Requests",
@ -1692,14 +1741,15 @@
{
"com.linkedin.pegasus2avro.chart.ChartInfo": {
"customProperties": {
"Priority": "",
"Category (Incident)": "",
"Time to Close an Incident (seconds)": "formula: // This is a calculated field\r\n// It calculates the difference in seconds between opening and closing an incident\r\n\r\n// Check if closed date is valid\r\nIF [Closed]>[Opened]\r\nTHEN\r\n//Calculate difference\r\nDATEDIFF('second', [Opened], [Closed])\r\nEND",
"Opened": "",
"Max Year?": "formula: // This is a calculated field\r\n// It shows TRUE/FALSE if opened date equals maximum year in the dataset\r\n\r\nDATEPART(\"year\", [Opened]) = DATEPART(\"year\", {MAX([Opened])})",
"Active": "",
"Closed": "",
"Time to Close an Incident": "formula: // This is a calculated field\r\n// It transforms time in seconds into DD:HH:MM:SS format\r\nSTR(INT(AVG([Time to Close an Incident (seconds)])/86400)) \r\n \r\n+ \" day(s) \" + \r\n \r\nIF (INT(AVG([Time to Close an Incident (seconds)])%86400/3600)) \r\n< 10 THEN \"0\" ELSE \"\" END + STR(INT(AVG([Time to Close an Incident (seconds)])%86400/3600))\r\n \r\n+ \":\" + \r\n \r\nIF INT(AVG([Time to Close an Incident (seconds)])%3600/60) \r\n< 10 THEN \"0\" ELSE \"\" END + STR(INT(AVG([Time to Close an Incident (seconds)])%3600/60)) \r\n \r\n \r\n+ \":\" + \r\n \r\nIF INT(AVG([Time to Close an Incident (seconds)]) %3600 %60) \r\n< 10 THEN \"0\" ELSE \"\" END + STR(INT(AVG([Time to Close an Incident (seconds)]) %3600 %60))"
"luid": "",
"field: Priority": "",
"field: Category (Incident)": "",
"field: Time to Close an Incident (seconds)": "formula: // This is a calculated field\r\n// It calculates the difference in seconds between opening and closing an incident\r\n\r\n// Check if closed date is valid\r\nIF [Closed]>[Opened]\r\nTHEN\r\n//Calculate difference\r\nDATEDIFF('second', [Opened], [Closed])\r\nEND",
"field: Opened": "",
"field: Max Year?": "formula: // This is a calculated field\r\n// It shows TRUE/FALSE if opened date equals maximum year in the dataset\r\n\r\nDATEPART(\"year\", [Opened]) = DATEPART(\"year\", {MAX([Opened])})",
"field: Active": "",
"field: Closed": "",
"field: Time to Close an Incident": "formula: // This is a calculated field\r\n// It transforms time in seconds into DD:HH:MM:SS format\r\nSTR(INT(AVG([Time to Close an Incident (seconds)])/86400)) \r\n \r\n+ \" day(s) \" + \r\n \r\nIF (INT(AVG([Time to Close an Incident (seconds)])%86400/3600)) \r\n< 10 THEN \"0\" ELSE \"\" END + STR(INT(AVG([Time to Close an Incident (seconds)])%86400/3600))\r\n \r\n+ \":\" + \r\n \r\nIF INT(AVG([Time to Close an Incident (seconds)])%3600/60) \r\n< 10 THEN \"0\" ELSE \"\" END + STR(INT(AVG([Time to Close an Incident (seconds)])%3600/60)) \r\n \r\n \r\n+ \":\" + \r\n \r\nIF INT(AVG([Time to Close an Incident (seconds)]) %3600 %60) \r\n< 10 THEN \"0\" ELSE \"\" END + STR(INT(AVG([Time to Close an Incident (seconds)]) %3600 %60))"
},
"externalUrl": "https://do-not-connect/t/acryl/authoring/ExecutiveDashboard/ExecutiveDashboard/AVG Time to Solve an Incident",
"title": "AVG Time to Solve an Incident",
@ -1772,12 +1822,13 @@
{
"com.linkedin.pegasus2avro.chart.ChartInfo": {
"customProperties": {
"Opened": "",
"% made SLA": "formula: // This is a calculated field\r\n// It shows the percentage of requests which made SLA\r\n\r\nCOUNTD(IF [Made SLA]= TRUE\r\nTHEN [Number]\r\nEND)\r\n/\r\nCOUNTD([Number])",
"Priority": "",
"Active": "",
"Made SLA": "",
"Number": ""
"luid": "",
"field: Opened": "",
"field: % made SLA": "formula: // This is a calculated field\r\n// It shows the percentage of requests which made SLA\r\n\r\nCOUNTD(IF [Made SLA]= TRUE\r\nTHEN [Number]\r\nEND)\r\n/\r\nCOUNTD([Number])",
"field: Priority": "",
"field: Active": "",
"field: Made SLA": "",
"field: Number": ""
},
"externalUrl": "https://do-not-connect/t/acryl/authoring/ExecutiveDashboard/ExecutiveDashboard/Made SLA?",
"title": "Made SLA?",
@ -1853,11 +1904,12 @@
{
"com.linkedin.pegasus2avro.chart.ChartInfo": {
"customProperties": {
"Priority": "",
"Number": "",
"Opened": "",
"Max Year?": "formula: // This is a calculated field\r\n// It shows TRUE/FALSE if opened date equals maximum year in the dataset\r\n\r\nDATEPART(\"year\", [Opened]) = DATEPART(\"year\", {MAX([Opened])})",
"Active": ""
"luid": "",
"field: Priority": "",
"field: Number": "",
"field: Opened": "",
"field: Max Year?": "formula: // This is a calculated field\r\n// It shows TRUE/FALSE if opened date equals maximum year in the dataset\r\n\r\nDATEPART(\"year\", [Opened]) = DATEPART(\"year\", {MAX([Opened])})",
"field: Active": ""
},
"externalUrl": "https://do-not-connect/#/site/acryl/views/ExecutiveDashboard/Tooltip-IncidentBreakdownbyPriority",
"title": "Tooltip - Incident Breakdown by Priority",
@ -1930,15 +1982,16 @@
{
"com.linkedin.pegasus2avro.chart.ChartInfo": {
"customProperties": {
"Overdue": "formula: // This is a calculated field\r\n// It checks if an incident is overdue\r\n\r\n//Check overdue cases among inactive incidents\r\n{ FIXED [Number]:\r\n\r\nIF MAX([Active]=FALSE) \r\nAND\r\nmax([Closed])> max([Due date])\r\n\r\nOR\r\n//Check overdue cases among active incidents\r\nMAX([Active]=TRUE) \r\nAND NOW()> MAX([Due date]) \r\n\r\nTHEN \"Overdue\"\r\nEND\r\n}",
"Number": "",
"Priority": "",
"Due date": "",
"% of Overdue": "formula: // This is a calculated field\r\n// It shows the percentage of incidents that are overdue\r\n\r\nIFNULL((IF ATTR([Overdue]= \"Overdue\")\r\nTHEN COUNTD([Number])\r\nEND),0)\r\n/\r\nATTR({ EXCLUDE [Overdue]:\r\nCOUNTD([Number])})",
"Opened": "",
"Max Year?": "formula: // This is a calculated field\r\n// It shows TRUE/FALSE if the year of opened is equal the max year of the dataset \r\n\r\nDATEPART(\"year\", [Opened]) = DATEPART(\"year\", {MAX([Opened])})",
"Closed": "",
"Active": ""
"luid": "",
"field: Overdue": "formula: // This is a calculated field\r\n// It checks if an incident is overdue\r\n\r\n//Check overdue cases among inactive incidents\r\n{ FIXED [Number]:\r\n\r\nIF MAX([Active]=FALSE) \r\nAND\r\nmax([Closed])> max([Due date])\r\n\r\nOR\r\n//Check overdue cases among active incidents\r\nMAX([Active]=TRUE) \r\nAND NOW()> MAX([Due date]) \r\n\r\nTHEN \"Overdue\"\r\nEND\r\n}",
"field: Number": "",
"field: Priority": "",
"field: Due date": "",
"field: % of Overdue": "formula: // This is a calculated field\r\n// It shows the percentage of incidents that are overdue\r\n\r\nIFNULL((IF ATTR([Overdue]= \"Overdue\")\r\nTHEN COUNTD([Number])\r\nEND),0)\r\n/\r\nATTR({ EXCLUDE [Overdue]:\r\nCOUNTD([Number])})",
"field: Opened": "",
"field: Max Year?": "formula: // This is a calculated field\r\n// It shows TRUE/FALSE if the year of opened is equal the max year of the dataset \r\n\r\nDATEPART(\"year\", [Opened]) = DATEPART(\"year\", {MAX([Opened])})",
"field: Closed": "",
"field: Active": ""
},
"externalUrl": "https://do-not-connect/t/acryl/authoring/ExecutiveDashboard/ExecutiveDashboard/Overdue Problems",
"title": "Overdue Problems",
@ -2014,11 +2067,12 @@
{
"com.linkedin.pegasus2avro.chart.ChartInfo": {
"customProperties": {
"Number": "",
"Priority": "",
"Opened": "",
"Max Year?": "formula: // This is a calculated field\r\n// It shows TRUE/FALSE if the year of opened is equal the max year of the dataset \r\n\r\nDATEPART(\"year\", [Opened]) = DATEPART(\"year\", {MAX([Opened])})",
"Active": ""
"luid": "",
"field: Number": "",
"field: Priority": "",
"field: Opened": "",
"field: Max Year?": "formula: // This is a calculated field\r\n// It shows TRUE/FALSE if the year of opened is equal the max year of the dataset \r\n\r\nDATEPART(\"year\", [Opened]) = DATEPART(\"year\", {MAX([Opened])})",
"field: Active": ""
},
"externalUrl": "https://do-not-connect/#/site/acryl/views/ExecutiveDashboard/Tooltip-ProblemBreakdownbyPriority",
"title": "Tooltip - Problem Breakdown by Priority",
@ -2094,11 +2148,12 @@
{
"com.linkedin.pegasus2avro.chart.ChartInfo": {
"customProperties": {
"Opened": "",
"Priority": "",
"Active": "",
"Max Year?": "formula: // This is a calculated field\r\n// It shows TRUE/FALSE if opened date equals maximum year in the dataset\r\n\r\nDATEPART(\"year\", [Opened]) = DATEPART(\"year\", {MAX([Opened])})",
"Number": ""
"luid": "",
"field: Opened": "",
"field: Priority": "",
"field: Active": "",
"field: Max Year?": "formula: // This is a calculated field\r\n// It shows TRUE/FALSE if opened date equals maximum year in the dataset\r\n\r\nDATEPART(\"year\", [Opened]) = DATEPART(\"year\", {MAX([Opened])})",
"field: Number": ""
},
"externalUrl": "https://do-not-connect/#/site/acryl/views/ExecutiveDashboard/Tooltip-RequestBreakdownbyPriority",
"title": "Tooltip - Request Breakdown by Priority",
@ -2174,13 +2229,14 @@
{
"com.linkedin.pegasus2avro.chart.ChartInfo": {
"customProperties": {
"Number": "",
"Priority": "",
"Time Span Breakdown": "formula: // This is a calculated field\r\n// It groups problems accordingly with their ages\r\n\r\nIF [Age of Problems]< 2592000\r\nTHEN \"Under 30 d\"\r\nELSEIF [Age of Problems] > 7776000\r\nTHEN \"+ than 90d\"\r\nELSE \" 30-90 d\"\r\nEND",
"Opened": "",
"Max Year?": "formula: // This is a calculated field\r\n// It shows TRUE/FALSE if the year of opened is equal the max year of the dataset \r\n\r\nDATEPART(\"year\", [Opened]) = DATEPART(\"year\", {MAX([Opened])})",
"Age of Problems": "formula: //Calculates the age of active problems since opened until now\r\n\r\nDATEDIFF('second', [Opened], NOW())",
"Active": ""
"luid": "",
"field: Number": "",
"field: Priority": "",
"field: Time Span Breakdown": "formula: // This is a calculated field\r\n// It groups problems accordingly with their ages\r\n\r\nIF [Age of Problems]< 2592000\r\nTHEN \"Under 30 d\"\r\nELSEIF [Age of Problems] > 7776000\r\nTHEN \"+ than 90d\"\r\nELSE \" 30-90 d\"\r\nEND",
"field: Opened": "",
"field: Max Year?": "formula: // This is a calculated field\r\n// It shows TRUE/FALSE if the year of opened is equal the max year of the dataset \r\n\r\nDATEPART(\"year\", [Opened]) = DATEPART(\"year\", {MAX([Opened])})",
"field: Age of Problems": "formula: //Calculates the age of active problems since opened until now\r\n\r\nDATEDIFF('second', [Opened], NOW())",
"field: Active": ""
},
"externalUrl": "https://do-not-connect/t/acryl/authoring/ExecutiveDashboard/ExecutiveDashboard/Age of Active Problems",
"title": "Age of Active Problems",
@ -2256,18 +2312,19 @@
{
"com.linkedin.pegasus2avro.chart.ChartInfo": {
"customProperties": {
"Priority": "",
"Current Year Total Cases": "formula: // This is a calculated field using level of detail calculation\r\n// It counts each distinct incident. The exclude is used to guarantee that filters will not interfere in the result\r\n\r\n\r\n{EXCLUDE [Opened], [Overdue], [Max Year?]: \r\nCOUNTD(IF [Max Year?]=TRUE\r\nTHEN [Number]\r\nEND)\r\n}",
"Category (Incident)": "",
"Overdue": "formula: // This is a calculated field\r\n// It shows if an incident is overdue\r\n\r\n//Check overdue cases among inactive incidents\r\n{ FIXED [Number]:\r\n\r\nIF MAX([Active]=FALSE) \r\nAND\r\nmax([Closed])>max([Due date])\r\n\r\nOR\r\n//Check overdue cases among active incidents\r\nMAX([Active] = TRUE) \r\nAND NOW() > MAX([Due date]) \r\n\r\nTHEN \"Overdue\"\r\nEND\r\n}",
"Total Active Incidents": "formula: // This is a calculated field\r\n// It counts each distinct incident. The \"exclude\" is used to avoid filters interfering in the final result\r\n\r\n{EXCLUDE [Opened], [Overdue], [Max Year?]: \r\nCOUNTD(IF [Active]=TRUE\r\nTHEN [Number]\r\nEND)\r\n}",
"Number": "",
"Opened": "",
"Max Year?": "formula: // This is a calculated field\r\n// It shows TRUE/FALSE if opened date equals maximum year in the dataset\r\n\r\nDATEPART(\"year\", [Opened]) = DATEPART(\"year\", {MAX([Opened])})",
"Opened Month Tooltip": "formula: // This is an IF statment using the opened field to allow for different formats\r\n// original used on columns for line charts are formatted in starting letter of month i.e. J for January\r\n// This version formats to full month name for the tooltip \r\n\r\n\r\n// The IF statement names the month based on the month number of the datefield\r\nIF DATEPART('month', [Opened]) = 1 THEN 'January'\r\nELSEIF DATEPART('month', [Opened]) = 2 THEN 'February'\r\nELSEIF DATEPART('month', [Opened]) = 3 THEN 'March'\r\nELSEIF DATEPART('month', [Opened]) = 4 THEN 'April'\r\nELSEIF DATEPART('month', [Opened]) = 5 THEN 'May'\r\nELSEIF DATEPART('month', [Opened]) = 6 THEN 'June'\r\nELSEIF DATEPART('month', [Opened]) = 7 THEN 'July'\r\nELSEIF DATEPART('month', [Opened]) = 8 THEN 'August'\r\nELSEIF DATEPART('month', [Opened]) = 9 THEN 'September'\r\nELSEIF DATEPART('month', [Opened]) = 10 THEN 'October'\r\nELSEIF DATEPART('month', [Opened]) = 11 THEN 'Novemeber'\r\nELSEIF DATEPART('month', [Opened]) = 12 THEN 'December'\r\nELSE NULL\r\nEND",
"Due date": "",
"Active": "",
"Closed": ""
"luid": "",
"field: Priority": "",
"field: Current Year Total Cases": "formula: // This is a calculated field using level of detail calculation\r\n// It counts each distinct incident. The exclude is used to guarantee that filters will not interfere in the result\r\n\r\n\r\n{EXCLUDE [Opened], [Overdue], [Max Year?]: \r\nCOUNTD(IF [Max Year?]=TRUE\r\nTHEN [Number]\r\nEND)\r\n}",
"field: Category (Incident)": "",
"field: Overdue": "formula: // This is a calculated field\r\n// It shows if an incident is overdue\r\n\r\n//Check overdue cases among inactive incidents\r\n{ FIXED [Number]:\r\n\r\nIF MAX([Active]=FALSE) \r\nAND\r\nmax([Closed])>max([Due date])\r\n\r\nOR\r\n//Check overdue cases among active incidents\r\nMAX([Active] = TRUE) \r\nAND NOW() > MAX([Due date]) \r\n\r\nTHEN \"Overdue\"\r\nEND\r\n}",
"field: Total Active Incidents": "formula: // This is a calculated field\r\n// It counts each distinct incident. The \"exclude\" is used to avoid filters interfering in the final result\r\n\r\n{EXCLUDE [Opened], [Overdue], [Max Year?]: \r\nCOUNTD(IF [Active]=TRUE\r\nTHEN [Number]\r\nEND)\r\n}",
"field: Number": "",
"field: Opened": "",
"field: Max Year?": "formula: // This is a calculated field\r\n// It shows TRUE/FALSE if opened date equals maximum year in the dataset\r\n\r\nDATEPART(\"year\", [Opened]) = DATEPART(\"year\", {MAX([Opened])})",
"field: Opened Month Tooltip": "formula: // This is an IF statment using the opened field to allow for different formats\r\n// original used on columns for line charts are formatted in starting letter of month i.e. J for January\r\n// This version formats to full month name for the tooltip \r\n\r\n\r\n// The IF statement names the month based on the month number of the datefield\r\nIF DATEPART('month', [Opened]) = 1 THEN 'January'\r\nELSEIF DATEPART('month', [Opened]) = 2 THEN 'February'\r\nELSEIF DATEPART('month', [Opened]) = 3 THEN 'March'\r\nELSEIF DATEPART('month', [Opened]) = 4 THEN 'April'\r\nELSEIF DATEPART('month', [Opened]) = 5 THEN 'May'\r\nELSEIF DATEPART('month', [Opened]) = 6 THEN 'June'\r\nELSEIF DATEPART('month', [Opened]) = 7 THEN 'July'\r\nELSEIF DATEPART('month', [Opened]) = 8 THEN 'August'\r\nELSEIF DATEPART('month', [Opened]) = 9 THEN 'September'\r\nELSEIF DATEPART('month', [Opened]) = 10 THEN 'October'\r\nELSEIF DATEPART('month', [Opened]) = 11 THEN 'Novemeber'\r\nELSEIF DATEPART('month', [Opened]) = 12 THEN 'December'\r\nELSE NULL\r\nEND",
"field: Due date": "",
"field: Active": "",
"field: Closed": ""
},
"externalUrl": "https://do-not-connect/t/acryl/authoring/ExecutiveDashboard/ExecutiveDashboard/Opened Incidents",
"title": "Opened Incidents",
@ -2339,7 +2396,9 @@
"aspects": [
{
"com.linkedin.pegasus2avro.dashboard.DashboardInfo": {
"customProperties": {},
"customProperties": {
"luid": ""
},
"title": "Executive Dashboard",
"description": "",
"charts": [
@ -2492,9 +2551,10 @@
{
"com.linkedin.pegasus2avro.chart.ChartInfo": {
"customProperties": {
"staff_last_name": "",
"amount": "",
"customer_first_name": ""
"luid": "",
"field: staff_last_name": "",
"field: amount": "",
"field: customer_first_name": ""
},
"externalUrl": "https://do-not-connect/#/site/acryl/views/Workbookpublishedds/Sheet1",
"title": "published sheet ds",

View File

@ -26,32 +26,22 @@ def _read_response(file_name):
return data
def define_query_metadata_func(workbook_0: str, workbook_all: str): # type: ignore
def define_query_metadata_func(workbook_all: str): # type: ignore
def side_effect_query_metadata(query):
if "workbooksConnection (first:0" in query:
return _read_response(workbook_0)
if "workbooksConnection (first:3" in query:
if "workbooksConnection (first:10," in query:
return _read_response(workbook_all)
if "embeddedDatasourcesConnection (first:0" in query:
return _read_response("embeddedDatasourcesConnection_0.json")
if "embeddedDatasourcesConnection (first:8" in query:
if "embeddedDatasourcesConnection (first:10," in query:
return _read_response("embeddedDatasourcesConnection_all.json")
if "publishedDatasourcesConnection (first:0" in query:
return _read_response("publishedDatasourcesConnection_0.json")
if "publishedDatasourcesConnection (first:2" in query:
if "publishedDatasourcesConnection (first:10," in query:
return _read_response("publishedDatasourcesConnection_all.json")
if "customSQLTablesConnection (first:0" in query:
return _read_response("customSQLTablesConnection_0.json")
if "customSQLTablesConnection (first:2" in query:
if "customSQLTablesConnection (first:10," in query:
return _read_response("customSQLTablesConnection_all.json")
raise Exception(f"Missing mock for query: {query}")
return side_effect_query_metadata
@ -146,7 +136,7 @@ def test_tableau_ingest(pytestconfig, tmp_path):
output_file_name: str = "tableau_mces.json"
golden_file_name: str = "tableau_mces_golden.json"
side_effect_query_metadata = define_query_metadata_func(
"workbooksConnection_0.json", "workbooksConnection_all.json"
"workbooksConnection_all.json"
)
tableau_ingest_common(
pytestconfig,
@ -157,23 +147,6 @@ def test_tableau_ingest(pytestconfig, tmp_path):
)
@freeze_time(FROZEN_TIME)
@pytest.mark.slow_unit
def test_tableau_usage_stat(pytestconfig, tmp_path):
output_file_name: str = "tableau_stat_mces.json"
golden_file_name: str = "tableau_state_mces_golden.json"
func = define_query_metadata_func(
"workbooksConnection_0.json", "workbooksConnection_state_all.json"
)
tableau_ingest_common(
pytestconfig,
tmp_path,
func,
golden_file_name,
output_file_name,
)
def test_lineage_overrides():
# Simple - specify platform instance to presto table
assert (