Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
59a5c68
refactor: database service and param replace for process execution
aarthy-dk Jul 18, 2025
2902516
refactor: use bind parameters in app database sql for process execution
aarthy-dk Jul 18, 2025
9825592
refactor: add sqlalchemy models for entities
aarthy-dk Jul 18, 2025
6446a49
refactor: use sqlalchemy models and bind params on all ui pages
aarthy-dk Jul 18, 2025
b6fb08d
refactor: remove dead code
aarthy-dk Jul 28, 2025
a5d91fd
test(unit): fix whitespace in query assertion
aarthy-dk Jul 21, 2025
f45cb80
fix: upgrade command
aarthy-dk Jul 22, 2025
f3fbb76
Merge branch 'fix-sql-injection' into 'enterprise'
Jul 29, 2025
a2e5f39
feat(table groups): verify access to tables
luis-dk Jul 29, 2025
b5fc7cb
Merge branch 'tg-permissions-check' into 'enterprise'
Jul 30, 2025
378be3b
feat: allow dismissing items from sorting selector
luis-dk Jul 29, 2025
63871b4
Merge branch 'dismiss-sort-field' into 'enterprise'
Jul 30, 2025
1c95bef
Merge branch 'main' into 'enterprise'
aarthy-dk Jul 31, 2025
1002c09
feat(test results): add action filter
aarthy-dk Jul 29, 2025
dc29dd2
fix(sql): handle None when replacing params
aarthy-dk Jul 29, 2025
bc01a5e
fix(analytics): update query fetch
aarthy-dk Jul 30, 2025
66fce8a
fix(sql): remove unnecessary str conversion for params
aarthy-dk Jul 30, 2025
3857d14
fix(profiling): copy error when any query fails
aarthy-dk Jul 30, 2025
5a4b43e
fix: don't log full queries when upgrading system version
aarthy-dk Jul 30, 2025
3de8e8d
fix(profiling): sampling bugs in some flavors
aarthy-dk Jul 30, 2025
819c45c
fix: empty states in table groups and test suites pages
aarthy-dk Jul 31, 2025
a35eb84
fix(test suites): remove unnecessary fields, disable export option wh…
aarthy-dk Jul 31, 2025
0187c84
fix(sql): incorrect parentheses in row_ct_pct test for mssql
aarthy-dk Jul 31, 2025
2a8bf49
fix: empty states in profiling runs and test runs pages
aarthy-dk Jul 31, 2025
1cb6e6e
Merge branch 'action-filter' into 'enterprise'
Jul 31, 2025
f5c06be
fix: empty state in profiling runs
aarthy-dk Jul 31, 2025
cec1822
fix: empty state in project dashboard
aarthy-dk Jul 31, 2025
cdc4e1d
fix: empty state in quality dashboard
aarthy-dk Jul 31, 2025
21992e2
fix: empty state in score issues
aarthy-dk Jul 31, 2025
e92c114
fix(issue report): sample data not loading in score issues test reports
aarthy-dk Jul 31, 2025
c06fbd7
fix(data catalog): broken table create script
aarthy-dk Jul 31, 2025
bbc45ba
fix(data catalog): bugs in column history dialog
aarthy-dk Aug 1, 2025
960f67d
fix(data catalog): don't apple searchOptions when search is empty
aarthy-dk Aug 1, 2025
66d106a
fix(hygiene issues): error on exporting all issues
aarthy-dk Aug 1, 2025
9cee2ad
fix(test definitions): fix condition for disabling disposition buttons
aarthy-dk Aug 1, 2025
a046301
Merge branch 'aarthy/qa-fixes' into 'enterprise'
Aug 1, 2025
a865c61
misc: Add example Aggregate Balance Test to Demo Data and Quickstart
diogodk Jul 25, 2025
c7012e8
Merge branch 'TG-912' into 'enterprise'
Aug 5, 2025
2cdc51a
feat(profiling): use native sampling
cbloche Aug 1, 2025
f8b345a
Merge branch 'chip/native_sampling_20250801' into 'enterprise'
Aug 7, 2025
e6c0675
refactor: Improving how pipeline parameters are obtained
rboni-dk Aug 8, 2025
c603bd3
Merge branch 'param-fetch-refactoring' into 'enterprise'
Aug 8, 2025
fdea797
fix(nav): validate query parameters
aarthy-dk Aug 8, 2025
04a03bd
Merge branch 'validate-params' into 'enterprise'
Aug 8, 2025
900025a
fix(queries): handle None results gracefully
aarthy-dk Aug 12, 2025
d720d5a
fix(profiling): error when running in background
aarthy-dk Aug 12, 2025
ee8845f
fix(cli): generate-tests command broken
aarthy-dk Aug 12, 2025
f8d48fb
fix(redshift): remove unnecessary pre connection query
aarthy-dk Aug 12, 2025
6649939
fix(data catalog): numbers are rounding unnecessarily
aarthy-dk Aug 12, 2025
f810214
Merge branch 'aarthy/fix-queries' into 'enterprise'
Aug 12, 2025
877ede5
fix(analytics): login usage event
aarthy-dk Aug 12, 2025
60f4b5a
Merge branch 'fix-login-event' into 'enterprise'
Aug 12, 2025
b58ae20
release: 4.16.3 -> 4.20.4
aarthy-dk Aug 12, 2025
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
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "dataops-testgen"
version = "4.16.3"
version = "4.20.4"
description = "DataKitchen's Data Quality DataOps TestGen"
authors = [
{ "name" = "DataKitchen, Inc.", "email" = "info@datakitchen.io" },
Expand Down
23 changes: 15 additions & 8 deletions testgen/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,10 @@
get_tg_schema,
version_service,
)
from testgen.common.models import with_database_session
from testgen.common.models.profiling_run import ProfilingRun
from testgen.common.models.test_run import TestRun
from testgen.scheduler import register_scheduler_job, run_scheduler
from testgen.ui.queries import profiling_run_queries, test_run_queries
from testgen.utils import plugins

LOG = logging.getLogger("testgen")
Expand Down Expand Up @@ -72,9 +74,9 @@ def invoke(self, ctx: Context):
cls=CliGroup,
help=f"""
{VERSION_DATA.edition} {VERSION_DATA.current or ""}

{f"New version available! {VERSION_DATA.latest}" if VERSION_DATA.latest != VERSION_DATA.current else ""}

Schema revision: {get_schema_revision()}
"""
)
Expand Down Expand Up @@ -625,11 +627,16 @@ def run_ui():
use_ssl = os.path.isfile(settings.SSL_CERT_FILE) and os.path.isfile(settings.SSL_KEY_FILE)

patch_streamlit.patch(force=True)
try:
profiling_run_queries.cancel_all_running()
test_run_queries.cancel_all_running()
except Exception:
LOG.warning("Failed to cancel 'Running' profiling/test runs")

@with_database_session
def cancel_all_running():
try:
ProfilingRun.cancel_all_running()
TestRun.cancel_all_running()
except Exception:
LOG.warning("Failed to cancel 'Running' profiling/test runs")

cancel_all_running()

try:
app_file = os.path.join(os.path.dirname(os.path.abspath(__file__)), "ui/app.py")
Expand Down
143 changes: 72 additions & 71 deletions testgen/commands/queries/execute_cat_tests_query.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
import typing
from typing import ClassVar, TypedDict

from testgen.commands.queries.rollup_scores_query import CRollupScoresSQL
from testgen.common import date_service, read_template_sql_file
from testgen.common.database import database_service
from testgen.common.database.database_service import get_flavor_service, replace_params
from testgen.common.read_file import replace_templated_functions


class CATTestParams(TypedDict):
schema_name: str
table_name: str
cat_sequence: int
test_measures: str
test_conditions: str


class CCATExecutionSQL:
project_code = ""
flavor = ""
Expand All @@ -16,11 +24,9 @@ class CCATExecutionSQL:
table_groups_id = ""
max_query_chars = ""
exception_message = ""

# Test Set Parameters
target_schema = ""
target_table = ""
dctTestParms: typing.ClassVar = {}
cat_test_params: ClassVar[CATTestParams] = {}

_rollup_scores_sql: CRollupScoresSQL = None

Expand All @@ -29,7 +35,7 @@ def __init__(self, strProjectCode, strTestSuiteId, strTestSuite, strSQLFlavor, m
self.test_suite_id = strTestSuiteId
self.test_suite = strTestSuite
self.project_code = strProjectCode
flavor_service = database_service.get_flavor_service(strSQLFlavor)
flavor_service = get_flavor_service(strSQLFlavor)
self.concat_operator = flavor_service.get_concat_operator()
self.flavor = strSQLFlavor
self.max_query_chars = max_query_chars
Expand All @@ -41,83 +47,78 @@ def _get_rollup_scores_sql(self) -> CRollupScoresSQL:
self._rollup_scores_sql = CRollupScoresSQL(self.test_run_id, self.table_groups_id)

return self._rollup_scores_sql

def _ReplaceParms(self, strInputString):
strInputString = strInputString.replace("{MAX_QUERY_CHARS}", str(self.max_query_chars))
strInputString = strInputString.replace("{TEST_RUN_ID}", self.test_run_id)
strInputString = strInputString.replace("{PROJECT_CODE}", self.project_code)
strInputString = strInputString.replace("{TEST_SUITE}", self.test_suite)
strInputString = strInputString.replace("{TEST_SUITE_ID}", self.test_suite_id)
strInputString = strInputString.replace("{TABLE_GROUPS_ID}", self.table_groups_id)

strInputString = strInputString.replace("{SQL_FLAVOR}", self.flavor)
strInputString = strInputString.replace("{ID_SEPARATOR}", "`" if self.flavor == "databricks" else '"')
strInputString = strInputString.replace("{CONCAT_OPERATOR}", self.concat_operator)

strInputString = strInputString.replace("{SCHEMA_NAME}", self.target_schema)
strInputString = strInputString.replace("{TABLE_NAME}", self.target_table)

strInputString = strInputString.replace("{RUN_DATE}", self.run_date)
strInputString = strInputString.replace("{NOW_DATE}", "GETDATE()")
strInputString = strInputString.replace("{START_TIME}", self.today)
strInputString = strInputString.replace(
"{NOW}", date_service.get_now_as_string_with_offset(self.minutes_offset)
)
strInputString = strInputString.replace("{EXCEPTION_MESSAGE}", self.exception_message.strip())

for parm, value in self.dctTestParms.items():
strInputString = strInputString.replace("{" + parm.upper() + "}", str(value))

strInputString = strInputString.replace("{RUN_DATE}", self.run_date)

strInputString = replace_templated_functions(strInputString, self.flavor)

if self.flavor != "databricks":

def _get_query(self, template_file_name: str, sub_directory: str | None = "exec_cat_tests", no_bind: bool = False) -> tuple[str, dict | None]:
query = read_template_sql_file(template_file_name, sub_directory)
params = {
"MAX_QUERY_CHARS": self.max_query_chars,
"TEST_RUN_ID": self.test_run_id,
"PROJECT_CODE": self.project_code,
"TEST_SUITE": self.test_suite,
"TEST_SUITE_ID": self.test_suite_id,
"TABLE_GROUPS_ID": self.table_groups_id,
"SQL_FLAVOR": self.flavor,
"ID_SEPARATOR": "`" if self.flavor == "databricks" else '"',
"CONCAT_OPERATOR": self.concat_operator,
"SCHEMA_NAME": self.target_schema,
"TABLE_NAME": self.target_table,
"NOW_DATE": "GETDATE()",
"START_TIME": self.today,
"NOW_TIMESTAMP": date_service.get_now_as_string_with_offset(self.minutes_offset),
"EXCEPTION_MESSAGE": self.exception_message.strip(),
**{key.upper(): value for key, value in self.cat_test_params.items()},
# This has to be replaced at the end
"RUN_DATE": self.run_date,
}
query = replace_params(query, params)
query = replace_templated_functions(query, self.flavor)

if no_bind and self.flavor != "databricks":
# Adding escape character where ':' is referenced
strInputString = strInputString.replace(":", "\\:")
query = query.replace(":", "\\:")

return strInputString
return query, None if no_bind else params

def GetDistinctTablesSQL(self):
# Runs on DK DB
strQ = self._ReplaceParms(read_template_sql_file("ex_cat_get_distinct_tables.sql", "exec_cat_tests"))
return strQ
def GetDistinctTablesSQL(self) -> tuple[str, dict]:
# Runs on App database
return self._get_query("ex_cat_get_distinct_tables.sql")

def GetAggregateTableTestSQL(self):
# Runs on DK DB
strQ = self._ReplaceParms(read_template_sql_file("ex_cat_build_agg_table_tests.sql", "exec_cat_tests"))
return strQ
def GetAggregateTableTestSQL(self) -> tuple[str, None]:
# Runs on App database
return self._get_query("ex_cat_build_agg_table_tests.sql", no_bind=True)

def GetAggregateTestParmsSQL(self):
# Runs on DK DB
strQ = self._ReplaceParms(read_template_sql_file("ex_cat_retrieve_agg_test_parms.sql", "exec_cat_tests"))
return strQ
def GetAggregateTestParmsSQL(self) -> tuple[str, dict]:
# Runs on App database
return self._get_query("ex_cat_retrieve_agg_test_parms.sql")

def PrepCATQuerySQL(self):
strQ = self._ReplaceParms(read_template_sql_file("ex_cat_test_query.sql", "exec_cat_tests"))
return strQ
def PrepCATQuerySQL(self) -> tuple[str, None]:
# Runs on Target database
return self._get_query("ex_cat_test_query.sql", no_bind=True)

def GetCATResultsParseSQL(self):
strQ = self._ReplaceParms(read_template_sql_file("ex_cat_results_parse.sql", "exec_cat_tests"))
return strQ
def GetCATResultsParseSQL(self) -> tuple[str, dict]:
# Runs on App database
return self._get_query("ex_cat_results_parse.sql")

def FinalizeTestResultsSQL(self):
strQ = self._ReplaceParms(read_template_sql_file("ex_finalize_test_run_results.sql", "execution"))
return strQ
def FinalizeTestResultsSQL(self) -> tuple[str, dict]:
# Runs on App database
return self._get_query("ex_finalize_test_run_results.sql", "execution")

def PushTestRunStatusUpdateSQL(self):
strQ = self._ReplaceParms(read_template_sql_file("ex_update_test_record_in_testrun_table.sql", "execution"))
return strQ
def PushTestRunStatusUpdateSQL(self) -> tuple[str, dict]:
# Runs on App database
return self._get_query("ex_update_test_record_in_testrun_table.sql", "execution")

def FinalizeTestSuiteUpdateSQL(self):
strQ = self._ReplaceParms(read_template_sql_file("ex_update_test_suite.sql", "execution"))
return strQ
def FinalizeTestSuiteUpdateSQL(self) -> tuple[str, dict]:
# Runs on App database
return self._get_query("ex_update_test_suite.sql", "execution")

def CalcPrevalenceTestResultsSQL(self):
return self._ReplaceParms(read_template_sql_file("ex_calc_prevalence_test_results.sql", "execution"))
def CalcPrevalenceTestResultsSQL(self) -> tuple[str, None]:
# Runs on App database
return self._get_query("ex_calc_prevalence_test_results.sql", "execution", no_bind=True)

def TestScoringRollupRunSQL(self):
def TestScoringRollupRunSQL(self) -> tuple[str, dict]:
# Runs on App database
return self._get_rollup_scores_sql().GetRollupScoresTestRunQuery()

def TestScoringRollupTableGroupSQL(self):
def TestScoringRollupTableGroupSQL(self) -> tuple[str, dict]:
# Runs on App database
return self._get_rollup_scores_sql().GetRollupScoresTestTableGroupQuery()
Loading
Loading