dataops-testgen 2.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (270) hide show
  1. dataops_testgen-2.2.0.dist-info/LICENSE +203 -0
  2. dataops_testgen-2.2.0.dist-info/METADATA +287 -0
  3. dataops_testgen-2.2.0.dist-info/NOTICE +5 -0
  4. dataops_testgen-2.2.0.dist-info/RECORD +270 -0
  5. dataops_testgen-2.2.0.dist-info/WHEEL +5 -0
  6. dataops_testgen-2.2.0.dist-info/entry_points.txt +2 -0
  7. dataops_testgen-2.2.0.dist-info/top_level.txt +1 -0
  8. testgen/__init__.py +0 -0
  9. testgen/__main__.py +770 -0
  10. testgen/commands/__init__.py +0 -0
  11. testgen/commands/queries/__init__.py +0 -0
  12. testgen/commands/queries/execute_cat_tests_query.py +95 -0
  13. testgen/commands/queries/execute_tests_query.py +160 -0
  14. testgen/commands/queries/generate_tests_query.py +94 -0
  15. testgen/commands/queries/profiling_query.py +366 -0
  16. testgen/commands/queries/test_parameter_validation_query.py +88 -0
  17. testgen/commands/run_execute_cat_tests.py +162 -0
  18. testgen/commands/run_execute_tests.py +168 -0
  19. testgen/commands/run_generate_tests.py +107 -0
  20. testgen/commands/run_get_entities.py +122 -0
  21. testgen/commands/run_launch_db_config.py +84 -0
  22. testgen/commands/run_observability_exporter.py +330 -0
  23. testgen/commands/run_profiling_bridge.py +495 -0
  24. testgen/commands/run_quick_start.py +168 -0
  25. testgen/commands/run_setup_profiling_tools.py +96 -0
  26. testgen/commands/run_test_definition.py +146 -0
  27. testgen/commands/run_test_parameter_validation.py +135 -0
  28. testgen/commands/run_upgrade_db_config.py +156 -0
  29. testgen/common/__init__.py +8 -0
  30. testgen/common/clean_sql.py +53 -0
  31. testgen/common/credentials.py +25 -0
  32. testgen/common/database/__init__.py +0 -0
  33. testgen/common/database/database_service.py +629 -0
  34. testgen/common/database/flavor/__init__.py +0 -0
  35. testgen/common/database/flavor/flavor_service.py +75 -0
  36. testgen/common/database/flavor/mssql_flavor_service.py +34 -0
  37. testgen/common/database/flavor/postgresql_flavor_service.py +5 -0
  38. testgen/common/database/flavor/redshift_flavor_service.py +22 -0
  39. testgen/common/database/flavor/snowflake_flavor_service.py +69 -0
  40. testgen/common/database/flavor/trino_flavor_service.py +21 -0
  41. testgen/common/date_service.py +68 -0
  42. testgen/common/display_service.py +85 -0
  43. testgen/common/docker_service.py +76 -0
  44. testgen/common/encrypt.py +55 -0
  45. testgen/common/get_pipeline_parms.py +57 -0
  46. testgen/common/logs.py +79 -0
  47. testgen/common/process_service.py +62 -0
  48. testgen/common/read_file.py +69 -0
  49. testgen/settings.py +440 -0
  50. testgen/template/dbsetup/010_create_base_schema.sql +2 -0
  51. testgen/template/dbsetup/020_create_standard_functions_sprocs.sql +179 -0
  52. testgen/template/dbsetup/030_initialize_new_schema_structure.sql +735 -0
  53. testgen/template/dbsetup/040_populate_new_schema_project.sql +59 -0
  54. testgen/template/dbsetup/050_populate_new_schema_metadata.sql +1517 -0
  55. testgen/template/dbsetup/060_create_standard_views.sql +248 -0
  56. testgen/template/dbsetup/070_create_default_users.sql +17 -0
  57. testgen/template/dbsetup/075_grant_role_rights.sql +43 -0
  58. testgen/template/dbsetup/080_set_current_revision.sql +5 -0
  59. testgen/template/dbupgrade/0100_incremental_upgrade.sql +5 -0
  60. testgen/template/dbupgrade/0101_incremental_upgrade.sql +15 -0
  61. testgen/template/dbupgrade/0102_incremental_upgrade.sql +4 -0
  62. testgen/template/dbupgrade/0103_incremental_upgrade.sql +22 -0
  63. testgen/template/dbupgrade/0104_incremental_upgrade.sql +44 -0
  64. testgen/template/dbupgrade/0105_incremental_upgrade.sql +1 -0
  65. testgen/template/dbupgrade/0106_incremental_upgrade.sql +5 -0
  66. testgen/template/dbupgrade/0107_incremental_upgrade.sql +3 -0
  67. testgen/template/dbupgrade_helpers/get_tg_revision.sql +2 -0
  68. testgen/template/exec_cat_tests/ex_cat_build_agg_table_tests.sql +116 -0
  69. testgen/template/exec_cat_tests/ex_cat_get_distinct_tables.sql +11 -0
  70. testgen/template/exec_cat_tests/ex_cat_results_parse.sql +69 -0
  71. testgen/template/exec_cat_tests/ex_cat_retrieve_agg_test_parms.sql +6 -0
  72. testgen/template/exec_cat_tests/ex_cat_test_query.sql +8 -0
  73. testgen/template/execution/ex_finalize_test_run_results.sql +37 -0
  74. testgen/template/execution/ex_get_tests_non_cat.sql +47 -0
  75. testgen/template/execution/ex_update_test_record_in_testrun_table.sql +27 -0
  76. testgen/template/execution/ex_write_test_record_to_testrun_table.sql +6 -0
  77. testgen/template/flavors/generic/exec_query_tests/ex_aggregate_match_no_drops_generic.sql +48 -0
  78. testgen/template/flavors/generic/exec_query_tests/ex_aggregate_match_num_incr_generic.sql +34 -0
  79. testgen/template/flavors/generic/exec_query_tests/ex_aggregate_match_percent_above_generic.sql +49 -0
  80. testgen/template/flavors/generic/exec_query_tests/ex_aggregate_match_percent_within_generic.sql +49 -0
  81. testgen/template/flavors/generic/exec_query_tests/ex_aggregate_match_same_generic.sql +49 -0
  82. testgen/template/flavors/generic/exec_query_tests/ex_custom_query_generic.sql +39 -0
  83. testgen/template/flavors/generic/exec_query_tests/ex_data_match_2way_generic.sql +58 -0
  84. testgen/template/flavors/generic/exec_query_tests/ex_data_match_generic.sql +44 -0
  85. testgen/template/flavors/generic/exec_query_tests/ex_prior_match_generic.sql +37 -0
  86. testgen/template/flavors/generic/exec_query_tests/ex_relative_entropy_generic.sql +53 -0
  87. testgen/template/flavors/generic/exec_query_tests/ex_window_match_no_drops_generic.sql +46 -0
  88. testgen/template/flavors/generic/exec_query_tests/ex_window_match_same_generic.sql +59 -0
  89. testgen/template/flavors/generic/profiling/contingency_counts.sql +3 -0
  90. testgen/template/flavors/generic/validate_tests/ex_get_project_column_list_generic.sql +3 -0
  91. testgen/template/flavors/mssql/exec_query_tests/ex_relative_entropy_mssql.sql +53 -0
  92. testgen/template/flavors/mssql/profiling/project_ddf_query_mssql.sql +35 -0
  93. testgen/template/flavors/mssql/profiling/project_profiling_query_mssql.yaml +246 -0
  94. testgen/template/flavors/mssql/profiling/project_secondary_profiling_query_mssql.sql +36 -0
  95. testgen/template/flavors/mssql/setup_profiling_tools/00_drop_existing_functions_mssql.sql +8 -0
  96. testgen/template/flavors/mssql/setup_profiling_tools/01_create_functions_mssql.sql +12 -0
  97. testgen/template/flavors/mssql/setup_profiling_tools/02_create_functions_mssql.sql +54 -0
  98. testgen/template/flavors/mssql/setup_profiling_tools/create_qc_schema_mssql.sql +4 -0
  99. testgen/template/flavors/mssql/setup_profiling_tools/grant_execute_privileges_mssql.sql +1 -0
  100. testgen/template/flavors/postgresql/exec_query_tests/ex_window_match_no_drops_postgresql.sql +46 -0
  101. testgen/template/flavors/postgresql/exec_query_tests/ex_window_match_same_postgresql.sql +59 -0
  102. testgen/template/flavors/postgresql/profiling/project_ddf_query_postgresql.sql +42 -0
  103. testgen/template/flavors/postgresql/profiling/project_profiling_query_postgresql.yaml +225 -0
  104. testgen/template/flavors/postgresql/profiling/project_secondary_profiling_query_postgresql.sql +28 -0
  105. testgen/template/flavors/postgresql/setup_profiling_tools/create_functions_postgresql.sql +157 -0
  106. testgen/template/flavors/postgresql/setup_profiling_tools/create_qc_schema_postgresql.sql +1 -0
  107. testgen/template/flavors/postgresql/setup_profiling_tools/grant_execute_privileges_postgresql.sql +2 -0
  108. testgen/template/flavors/redshift/profiling/project_ddf_query_redshift.sql +38 -0
  109. testgen/template/flavors/redshift/profiling/project_profiling_query_redshift.yaml +221 -0
  110. testgen/template/flavors/redshift/profiling/project_secondary_profiling_query_redshift.sql +29 -0
  111. testgen/template/flavors/redshift/setup_profiling_tools/create_functions_redshift.sql +115 -0
  112. testgen/template/flavors/redshift/setup_profiling_tools/create_qc_schema_redshift.sql +1 -0
  113. testgen/template/flavors/redshift/setup_profiling_tools/grant_execute_privileges_redshift.sql +2 -0
  114. testgen/template/flavors/snowflake/profiling/project_ddf_query_snowflake.sql +38 -0
  115. testgen/template/flavors/snowflake/profiling/project_profiling_query_snowflake.yaml +220 -0
  116. testgen/template/flavors/snowflake/profiling/project_secondary_profiling_query_snowflake.sql +29 -0
  117. testgen/template/flavors/snowflake/setup_profiling_tools/create_functions_snowflake.sql +69 -0
  118. testgen/template/flavors/snowflake/setup_profiling_tools/create_qc_schema_snowflake.sql +1 -0
  119. testgen/template/flavors/snowflake/setup_profiling_tools/grant_execute_privileges_snowflake.sql +6 -0
  120. testgen/template/flavors/trino/profiling/project_profiling_query_trino.yaml +219 -0
  121. testgen/template/flavors/trino/setup_profiling_tools/create_functions_trino.sql +92 -0
  122. testgen/template/flavors/trino/setup_profiling_tools/create_qc_schema_trino.sql +1 -0
  123. testgen/template/gen_funny_cat_tests/gen_test_constant.sql +104 -0
  124. testgen/template/gen_funny_cat_tests/gen_test_distinct_value_ct.sql +98 -0
  125. testgen/template/gen_funny_cat_tests/gen_test_row_ct.sql +57 -0
  126. testgen/template/gen_funny_cat_tests/gen_test_row_ct_pct.sql +59 -0
  127. testgen/template/generation/gen_delete_old_tests.sql +5 -0
  128. testgen/template/generation/gen_insert_test_suite.sql +5 -0
  129. testgen/template/generation/gen_retrieve_or_insert_test_suite.sql +58 -0
  130. testgen/template/generation/gen_standard_test_type_list.sql +13 -0
  131. testgen/template/generation/gen_standard_tests.sql +48 -0
  132. testgen/template/get_entities/get_connection.sql +21 -0
  133. testgen/template/get_entities/get_connections_list.sql +9 -0
  134. testgen/template/get_entities/get_latest.sql +4 -0
  135. testgen/template/get_entities/get_profile.sql +12 -0
  136. testgen/template/get_entities/get_profile_info.sql +17 -0
  137. testgen/template/get_entities/get_profile_list.sql +17 -0
  138. testgen/template/get_entities/get_profile_screen.sql +275 -0
  139. testgen/template/get_entities/get_project_list.sql +6 -0
  140. testgen/template/get_entities/get_table_group_list.sql +10 -0
  141. testgen/template/get_entities/get_test_generation_list.sql +18 -0
  142. testgen/template/get_entities/get_test_info.sql +41 -0
  143. testgen/template/get_entities/get_test_results_for_run_cli.sql +16 -0
  144. testgen/template/get_entities/get_test_run_list.sql +24 -0
  145. testgen/template/get_entities/get_test_suite.sql +13 -0
  146. testgen/template/get_entities/get_test_suite_list.sql +18 -0
  147. testgen/template/get_entities/list_test_types.sql +4 -0
  148. testgen/template/observability/get_event_data.sql +23 -0
  149. testgen/template/observability/get_test_results.sql +41 -0
  150. testgen/template/observability/update_test_results_exported_to_observability.sql +12 -0
  151. testgen/template/parms/parms_profiling.sql +34 -0
  152. testgen/template/parms/parms_test_execution.sql +13 -0
  153. testgen/template/parms/parms_test_gen.sql +23 -0
  154. testgen/template/profiling/contingency_columns.sql +7 -0
  155. testgen/template/profiling/datatype_suggestions.sql +56 -0
  156. testgen/template/profiling/functional_datatype.sql +523 -0
  157. testgen/template/profiling/functional_tabletype_stage.sql +48 -0
  158. testgen/template/profiling/functional_tabletype_update.sql +8 -0
  159. testgen/template/profiling/pii_flag.sql +133 -0
  160. testgen/template/profiling/profile_anomalies_screen_column.sql +22 -0
  161. testgen/template/profiling/profile_anomalies_screen_multi_column.sql +58 -0
  162. testgen/template/profiling/profile_anomalies_screen_table.sql +22 -0
  163. testgen/template/profiling/profile_anomalies_screen_table_dates.sql +30 -0
  164. testgen/template/profiling/profile_anomalies_screen_variants.sql +40 -0
  165. testgen/template/profiling/profile_anomaly_types_get.sql +3 -0
  166. testgen/template/profiling/project_get_table_sample_count.sql +22 -0
  167. testgen/template/profiling/project_profile_run_record_insert.sql +8 -0
  168. testgen/template/profiling/project_profile_run_record_update.sql +5 -0
  169. testgen/template/profiling/project_profile_run_record_update_status.sql +5 -0
  170. testgen/template/profiling/project_update_profile_results_to_estimates.sql +32 -0
  171. testgen/template/profiling/refresh_anomalies.sql +33 -0
  172. testgen/template/profiling/refresh_data_chars_from_profiling.sql +156 -0
  173. testgen/template/profiling/secondary_profiling_columns.sql +12 -0
  174. testgen/template/profiling/secondary_profiling_delete.sql +4 -0
  175. testgen/template/profiling/secondary_profiling_update.sql +18 -0
  176. testgen/template/quick_start/populate_target_data.sql +1077 -0
  177. testgen/template/quick_start/recreate_target_data_schema.sql +167 -0
  178. testgen/template/quick_start/update_target_data.sql +100 -0
  179. testgen/template/updates/create_tmp_test_definition.sql +19 -0
  180. testgen/template/updates/get_test_def_parms.sql +38 -0
  181. testgen/template/updates/populate_stg_test_definitions.sql +184 -0
  182. testgen/template/validate_tests/ex_disable_tests_test_definitions.sql +5 -0
  183. testgen/template/validate_tests/ex_flag_tests_test_definitions.sql +64 -0
  184. testgen/template/validate_tests/ex_get_project_column_list_generic.sql +3 -0
  185. testgen/template/validate_tests/ex_get_test_column_list_tg.sql +65 -0
  186. testgen/template/validate_tests/ex_write_test_val_errors.sql +22 -0
  187. testgen/ui/__init__.py +0 -0
  188. testgen/ui/app.py +98 -0
  189. testgen/ui/assets/dk_logo.svg +46 -0
  190. testgen/ui/assets/question_mark.png +0 -0
  191. testgen/ui/assets/scripts.js +68 -0
  192. testgen/ui/assets/style.css +140 -0
  193. testgen/ui/bootstrap.py +109 -0
  194. testgen/ui/components/__init__.py +0 -0
  195. testgen/ui/components/frontend/css/KFOlCnqEu92Fr1MmEU9fBBc4.woff2 +0 -0
  196. testgen/ui/components/frontend/css/KFOlCnqEu92Fr1MmEU9fChc4EsA.woff2 +0 -0
  197. testgen/ui/components/frontend/css/KFOmCnqEu92Fr1Mu4mxK.woff2 +0 -0
  198. testgen/ui/components/frontend/css/KFOmCnqEu92Fr1Mu7GxKOzY.woff2 +0 -0
  199. testgen/ui/components/frontend/css/material-symbols-rounded.css +24 -0
  200. testgen/ui/components/frontend/css/material-symbols-rounded.woff2 +0 -0
  201. testgen/ui/components/frontend/css/roboto-font-faces.css +35 -0
  202. testgen/ui/components/frontend/css/shared.css +36 -0
  203. testgen/ui/components/frontend/img/dk_logo.svg +46 -0
  204. testgen/ui/components/frontend/index.html +17 -0
  205. testgen/ui/components/frontend/js/components/breadcrumbs.js +86 -0
  206. testgen/ui/components/frontend/js/components/button.js +66 -0
  207. testgen/ui/components/frontend/js/components/location.js +62 -0
  208. testgen/ui/components/frontend/js/components/select.js +75 -0
  209. testgen/ui/components/frontend/js/components/sidebar.js +358 -0
  210. testgen/ui/components/frontend/js/main.js +99 -0
  211. testgen/ui/components/frontend/js/streamlit.js +19 -0
  212. testgen/ui/components/frontend/js/van.min.js +1 -0
  213. testgen/ui/components/utils/__init__.py +0 -0
  214. testgen/ui/components/utils/callbacks.py +51 -0
  215. testgen/ui/components/utils/component.py +13 -0
  216. testgen/ui/components/widgets/__init__.py +6 -0
  217. testgen/ui/components/widgets/breadcrumbs.py +32 -0
  218. testgen/ui/components/widgets/location.py +65 -0
  219. testgen/ui/components/widgets/modal.py +97 -0
  220. testgen/ui/components/widgets/sidebar.py +69 -0
  221. testgen/ui/navigation/__init__.py +0 -0
  222. testgen/ui/navigation/menu.py +42 -0
  223. testgen/ui/navigation/page.py +20 -0
  224. testgen/ui/navigation/router.py +63 -0
  225. testgen/ui/queries/__init__.py +0 -0
  226. testgen/ui/queries/authentication_queries.py +47 -0
  227. testgen/ui/queries/connection_queries.py +121 -0
  228. testgen/ui/queries/profiling_queries.py +148 -0
  229. testgen/ui/queries/project_queries.py +9 -0
  230. testgen/ui/queries/table_group_queries.py +186 -0
  231. testgen/ui/queries/test_definition_queries.py +270 -0
  232. testgen/ui/queries/test_run_queries.py +32 -0
  233. testgen/ui/queries/test_suite_queries.py +145 -0
  234. testgen/ui/scripts/__init__.py +0 -0
  235. testgen/ui/scripts/patch_streamlit.py +111 -0
  236. testgen/ui/services/__init__.py +0 -0
  237. testgen/ui/services/authentication_service.py +119 -0
  238. testgen/ui/services/connection_service.py +220 -0
  239. testgen/ui/services/database_service.py +282 -0
  240. testgen/ui/services/form_service.py +1008 -0
  241. testgen/ui/services/javascript_service.py +44 -0
  242. testgen/ui/services/query_service.py +316 -0
  243. testgen/ui/services/string_service.py +12 -0
  244. testgen/ui/services/table_group_service.py +130 -0
  245. testgen/ui/services/test_definition_service.py +117 -0
  246. testgen/ui/services/test_run_service.py +13 -0
  247. testgen/ui/services/test_suite_service.py +76 -0
  248. testgen/ui/services/toolbar_service.py +77 -0
  249. testgen/ui/session.py +46 -0
  250. testgen/ui/views/__init__.py +0 -0
  251. testgen/ui/views/app_log_modal.py +92 -0
  252. testgen/ui/views/connections.py +72 -0
  253. testgen/ui/views/connections_base.py +367 -0
  254. testgen/ui/views/login.py +40 -0
  255. testgen/ui/views/not_found.py +16 -0
  256. testgen/ui/views/overview.py +34 -0
  257. testgen/ui/views/profiling_anomalies.py +501 -0
  258. testgen/ui/views/profiling_details.py +335 -0
  259. testgen/ui/views/profiling_modal.py +40 -0
  260. testgen/ui/views/profiling_results.py +206 -0
  261. testgen/ui/views/profiling_summary.py +177 -0
  262. testgen/ui/views/project_settings.py +74 -0
  263. testgen/ui/views/table_groups.py +530 -0
  264. testgen/ui/views/test_definitions.py +1020 -0
  265. testgen/ui/views/test_results.py +908 -0
  266. testgen/ui/views/test_runs.py +195 -0
  267. testgen/ui/views/test_suites.py +545 -0
  268. testgen/utils/__init__.py +0 -0
  269. testgen/utils/plugins.py +17 -0
  270. testgen/utils/singleton.py +14 -0
@@ -0,0 +1,44 @@
1
+ import logging
2
+
3
+ from streamlit_javascript import st_javascript
4
+
5
+ from testgen.ui.services.authentication_service import AUTH_TOKEN_COOKIE_NAME
6
+
7
+ LOG = logging.getLogger("testgen")
8
+
9
+
10
+ def copy_to_clipboard(text):
11
+ script = f"""await (async function () {{
12
+ window.parent.postMessage({{ type: 'TestgenCopyToClipboard', text: '{text}' }}, '*');
13
+ return 0;
14
+ }})()
15
+ """
16
+ execute_javascript(script)
17
+
18
+
19
+ def clear_component_states():
20
+ execute_javascript(
21
+ f"""await (async function () {{
22
+ window.parent.postMessage({{ type: 'TestgenLogout', cookie: '{AUTH_TOKEN_COOKIE_NAME}' }}, '*');
23
+ return 0;
24
+ }})()
25
+ """
26
+ )
27
+
28
+
29
+ def execute_javascript(script):
30
+ return_value = st_javascript(script)
31
+ if return_value != 0:
32
+ LOG.warning(f"execute_javascript returned with non zero value: {return_value}, script: {script}")
33
+
34
+
35
+ def get_browser_locale_timezone():
36
+ from streamlit_javascript import st_javascript
37
+
38
+ return st_javascript(
39
+ """await (async () => {
40
+ const userTimezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
41
+ console.log(userTimezone)
42
+ return userTimezone
43
+ })().then(returnValue => returnValue)"""
44
+ )
@@ -0,0 +1,316 @@
1
+ import testgen.ui.services.database_service as db
2
+
3
+ """
4
+ Shared queries for standard lookups
5
+ - should be called by cached functions within page
6
+ """
7
+
8
+
9
+ def run_project_lookup_query(str_schema):
10
+ str_sql = f"""
11
+ SELECT
12
+ id::VARCHAR(50),
13
+ project_code,
14
+ project_name,
15
+ effective_from_date,
16
+ observability_api_url,
17
+ observability_api_key
18
+ FROM {str_schema}.projects
19
+ ORDER BY project_name
20
+ """
21
+ return db.retrieve_data(str_sql)
22
+
23
+
24
+ def get_project_by_code(schema: str, project_code: str):
25
+ str_sql = f"""
26
+ SELECT
27
+ id::VARCHAR(50),
28
+ project_code,
29
+ project_name,
30
+ effective_from_date,
31
+ observability_api_url,
32
+ observability_api_key
33
+ FROM {schema}.projects
34
+ WHERE project_code = {db.make_value_db_friendly(project_code)};
35
+ """
36
+ results = db.retrieve_data(str_sql)
37
+ if results.size <= 0:
38
+ return None
39
+ return results.iloc[0]
40
+
41
+
42
+ def run_test_type_lookup_query(str_schema, str_test_type=None, boo_show_referential=True, boo_show_table=True,
43
+ boo_show_column=True, boo_show_custom=True):
44
+ if str_test_type:
45
+ str_criteria = f" AND tt.test_type = '{str_test_type}'"
46
+ else:
47
+ str_criteria = ""
48
+
49
+ if (boo_show_referential and boo_show_table and boo_show_column and boo_show_custom) == False:
50
+ str_scopes = ""
51
+ str_scopes += "'referential'," if boo_show_referential else ""
52
+ str_scopes += "'table'," if boo_show_table else ""
53
+ str_scopes += "'column'," if boo_show_column else ""
54
+ str_scopes += "'custom'," if boo_show_custom else ""
55
+ if str_scopes > "":
56
+ str_criteria += f"AND tt.test_scope in ({str_scopes[:-1]})"
57
+
58
+ str_sql = f"""
59
+ SELECT tt.id, tt.test_type, tt.id as cat_test_id,
60
+ tt.test_name_short, tt.test_name_long, tt.test_description,
61
+ tt.measure_uom, COALESCE(tt.measure_uom_description, '') as measure_uom_description,
62
+ tt.default_parm_columns, tt.default_severity,
63
+ tt.run_type, tt.test_scope, tt.dq_dimension, tt.threshold_description,
64
+ tt.column_name_prompt, tt.column_name_help,
65
+ tt.default_parm_prompts, tt.default_parm_help, tt.usage_notes,
66
+ CASE tt.test_scope WHEN 'referential' THEN '⧉ ' WHEN 'custom' THEN '⛭ ' WHEN 'table' THEN '⊞ ' WHEN 'column' THEN '≣ ' ELSE '? ' END
67
+ || tt.test_name_short || ': ' || lower(tt.test_name_long)
68
+ || CASE WHEN tt.selection_criteria > '' THEN ' [auto-generated]' ELSE '' END as select_name
69
+ FROM {str_schema}.test_types tt
70
+ WHERE tt.active = 'Y' {str_criteria}
71
+ ORDER BY CASE tt.test_scope WHEN 'referential' THEN 1 WHEN 'custom' THEN 2 WHEN 'table' THEN 3 WHEN 'column' THEN 4 ELSE 5 END,
72
+ tt.test_name_short;
73
+ """
74
+ return db.retrieve_data(str_sql)
75
+
76
+
77
+ def run_connections_lookup_query(str_schema, str_project_code):
78
+ str_sql = f"""
79
+ SELECT c.id::VARCHAR(50), c.connection_id, c.connection_name
80
+ FROM {str_schema}.connections c
81
+ WHERE c.project_code = '{str_project_code}'
82
+ ORDER BY connection_name
83
+ """
84
+ return db.retrieve_data(str_sql)
85
+
86
+
87
+ def run_table_groups_lookup_query(str_schema, str_project_code, connection_id=None, table_group_id=None):
88
+ str_sql = f"""
89
+ SELECT tg.id::VARCHAR(50), tg.table_groups_name, tg.connection_id, tg.table_group_schema
90
+ FROM {str_schema}.table_groups tg
91
+ """
92
+
93
+ if connection_id:
94
+ str_sql += f"""
95
+ inner join {str_schema}.connections c on c.connection_id = tg.connection_id
96
+ """
97
+
98
+ str_sql += f"""
99
+ WHERE tg.project_code = '{str_project_code}'
100
+ """
101
+
102
+ if table_group_id:
103
+ str_sql += f"""
104
+ AND tg.id = '{table_group_id}'::UUID
105
+ """
106
+
107
+ if connection_id:
108
+ str_sql += f"""
109
+ AND c.id = '{connection_id}'::UUID
110
+ """
111
+
112
+ str_sql += """
113
+ ORDER BY table_groups_name
114
+ """
115
+ return db.retrieve_data(str_sql)
116
+
117
+
118
+ def run_table_lookup_query(str_schema, str_table_groups_id):
119
+ str_sql = f"""
120
+ SELECT table_name
121
+ FROM {str_schema}.data_table_chars
122
+ WHERE table_groups_id = '{str_table_groups_id}'::UUID
123
+ AND drop_date IS NULL
124
+ ORDER BY table_name
125
+ """
126
+ return db.retrieve_data(str_sql)
127
+
128
+
129
+ def run_column_lookup_query(str_schema, str_table_groups_id, str_table_name):
130
+ str_sql = f"""
131
+ SELECT column_name
132
+ FROM {str_schema}.data_column_chars
133
+ WHERE table_groups_id = '{str_table_groups_id}'::UUID
134
+ AND table_name = '{str_table_name}'
135
+ AND drop_date IS NULL
136
+ ORDER BY column_name
137
+ """
138
+ return db.retrieve_data(str_sql)
139
+
140
+
141
+ def run_test_suite_lookup_by_tgroup_query(str_schema, str_table_groups_id, test_suite_name=None):
142
+ str_sql = f"""
143
+ SELECT id::VARCHAR(50), test_suite, test_suite_schema, severity, export_to_observability
144
+ FROM {str_schema}.test_suites
145
+ WHERE table_groups_id = '{str_table_groups_id}'
146
+ """
147
+
148
+ if test_suite_name:
149
+ str_sql += f"""
150
+ AND test_suite = '{test_suite_name}'
151
+ """
152
+
153
+ str_sql += """
154
+ ORDER BY test_suite
155
+ """
156
+
157
+ return db.retrieve_data(str_sql)
158
+
159
+
160
+ def run_test_suite_lookup_by_project_query(str_schema, str_project):
161
+ str_sql = f"""
162
+ SELECT s.id::VARCHAR(50), s.test_suite, s.test_suite_schema,
163
+ s.test_suite
164
+ || CASE
165
+ WHEN tg.table_groups_name IS NULL THEN ''
166
+ ELSE '(' || tg.table_groups_name || ')'
167
+ END as test_suite_with_tg,
168
+ s.test_suite_description
169
+ FROM {str_schema}.test_suites s
170
+ LEFT JOIN {str_schema}.table_groups tg
171
+ ON (s.table_groups_id = tg.id)
172
+ WHERE s.project_code = '{str_project}'
173
+ ORDER BY s.test_suite
174
+ """
175
+ return db.retrieve_data(str_sql)
176
+
177
+
178
+ def run_last_test_run(str_schema, str_project_code):
179
+ str_sql = f"""
180
+ SELECT MIN(EXTRACT(DAY FROM (CURRENT_DATE - r.test_starttime))) as days_back
181
+ FROM {str_schema}.test_runs r
182
+ WHERE r.project_code = '{str_project_code}'
183
+ AND r.status = 'Complete';
184
+ """
185
+ return db.retrieve_data(str_sql)
186
+
187
+
188
+ def run_test_run_lookup_by_date(str_schema, str_project_code, str_run_date):
189
+ str_sql = f"""
190
+ SELECT r.id::VARCHAR(50),
191
+ r.test_starttime::VARCHAR || ' - ' || s.test_suite as test_run_desc
192
+ FROM {str_schema}.test_runs r
193
+ LEFT JOIN {str_schema}.test_suites s
194
+ ON (r.project_code = s.project_code
195
+ AND r.test_suite = s.test_suite)
196
+ WHERE r.project_code = '{str_project_code}'
197
+ AND r.test_starttime::DATE = '{str_run_date}'
198
+ ORDER BY r.test_starttime DESC
199
+ """
200
+ return db.retrieve_data(str_sql)
201
+
202
+
203
+ def update_anomaly_disposition_old(selected, str_schema, str_new_status):
204
+ int_batch_size = 50
205
+
206
+ def finalize_query(status, ids):
207
+ return f"""UPDATE {str_schema}.profile_anomaly_results
208
+ SET disposition = NULLIF('{status}', 'No Decision')
209
+ WHERE id IN ({ids.rstrip(',')});"""
210
+
211
+ lst_ids = [row["id"] for row in selected if "id" in row]
212
+ lst_updates = []
213
+ i = 0
214
+ str_ids = ""
215
+
216
+ for my_id in lst_ids:
217
+ i += 1
218
+ str_ids += f" '{my_id}'::UUID,"
219
+ if i >= int_batch_size:
220
+ lst_updates.append(finalize_query(str_new_status, str_ids))
221
+ # reset for next batch
222
+ i = 0
223
+ if i > 0:
224
+ lst_updates.append(finalize_query(str_new_status, str_ids))
225
+ for upd in lst_updates:
226
+ db.execute_sql(upd)
227
+
228
+ return True
229
+
230
+
231
+ def update_anomaly_disposition(selected, str_schema, str_new_status):
232
+ def finalize_small_update(status, ids):
233
+ return f"""UPDATE {str_schema}.profile_anomaly_results
234
+ SET disposition = NULLIF('{status}', 'No Decision')
235
+ WHERE id IN ({ids});"""
236
+
237
+ def finalize_big_update(status, ids):
238
+ return f"""WITH selects
239
+ as ( SELECT UNNEST(ARRAY [{ids}]) AS selected_id )
240
+ UPDATE {str_schema}.profile_anomaly_results
241
+ SET disposition = NULLIF('{status}', 'No Decision')
242
+ FROM {str_schema}.profile_anomaly_results r
243
+ INNER JOIN selects s
244
+ ON (r.id = s.selected_id)
245
+ WHERE r.id = profile_anomaly_results.id;"""
246
+
247
+ lst_ids = [row["id"] for row in selected if "id" in row]
248
+ lst_updates = []
249
+ str_ids = ""
250
+
251
+ if len(lst_ids) > 0:
252
+ for my_id in lst_ids:
253
+ str_ids += f" '{my_id}'::UUID,"
254
+ str_ids = str_ids.rstrip(",")
255
+ if len(lst_ids) > 4:
256
+ lst_updates.append(finalize_big_update(str_new_status, str_ids))
257
+ else:
258
+ lst_updates.append(finalize_small_update(str_new_status, str_ids))
259
+
260
+ for q in lst_updates:
261
+ db.execute_sql_raw(q)
262
+
263
+ return True
264
+
265
+
266
+ def update_result_disposition(selected, str_schema, str_new_status):
267
+ active_yn = "N" if str_new_status == "Inactive" else "Y"
268
+
269
+ def finalize_small_update(status, ids):
270
+ return f"""UPDATE {str_schema}.test_results
271
+ SET disposition = NULLIF('{status}', 'No Decision')
272
+ WHERE id IN ({ids});"""
273
+
274
+ def finalize_big_update(status, ids):
275
+ return f"""WITH selects
276
+ as ( SELECT UNNEST(ARRAY [{ids}]) AS selected_id )
277
+ UPDATE {str_schema}.test_results
278
+ SET disposition = NULLIF('{status}', 'No Decision')
279
+ FROM {str_schema}.test_results r
280
+ INNER JOIN selects s
281
+ ON (r.id = s.selected_id)
282
+ WHERE r.id = test_results.id;"""
283
+
284
+ def finalize_test_update(ids):
285
+ str_lock_test = ", lock_refresh = 'N'" if active_yn == "Y" else ", lock_refresh = 'Y'"
286
+ return f"""WITH selects
287
+ as ( SELECT UNNEST(ARRAY [{ids}]) AS selected_id )
288
+ UPDATE {str_schema}.test_definitions
289
+ SET test_active = '{active_yn}',
290
+ last_manual_update = CURRENT_TIMESTAMP AT TIME ZONE 'UTC' {str_lock_test}
291
+ FROM {str_schema}.test_definitions d
292
+ INNER JOIN {str_schema}.test_results r
293
+ ON (d.id = r.test_definition_id)
294
+ INNER JOIN selects s
295
+ ON (r.id = s.selected_id)
296
+ WHERE d.id = test_definitions.id"""
297
+
298
+ lst_ids = [row["test_result_id"] for row in selected if "test_result_id" in row]
299
+ lst_updates = []
300
+ str_ids = ""
301
+
302
+ for my_id in lst_ids:
303
+ str_ids += f" '{my_id}'::UUID,"
304
+ str_ids = str_ids.rstrip(",")
305
+
306
+ if len(lst_ids) > 0:
307
+ if len(lst_ids) > 4:
308
+ lst_updates.append(finalize_big_update(str_new_status, str_ids))
309
+ else:
310
+ lst_updates.append(finalize_small_update(str_new_status, str_ids))
311
+ lst_updates.append(finalize_test_update(str_ids))
312
+
313
+ for q in lst_updates:
314
+ db.execute_sql_raw(q)
315
+
316
+ return True
@@ -0,0 +1,12 @@
1
+ def empty_if_null(item):
2
+ if item is None:
3
+ return ""
4
+ else:
5
+ return item
6
+
7
+
8
+ def snake_case_to_title_case(snake_case):
9
+ words = snake_case.split("_")
10
+ title_case_words = [word.capitalize() for word in words]
11
+ title_case = " ".join(title_case_words)
12
+ return title_case
@@ -0,0 +1,130 @@
1
+ import streamlit as st
2
+
3
+ import testgen.ui.queries.table_group_queries as table_group_queries
4
+ import testgen.ui.services.connection_service as connection_service
5
+ import testgen.ui.services.test_suite_service as test_suite_service
6
+ from testgen.common.database.database_service import RetrieveDBResultsToDictList
7
+
8
+
9
+ def get_by_id(table_group_id: str):
10
+ schema = st.session_state["dbschema"]
11
+ return table_group_queries.get_by_id(schema, table_group_id)
12
+
13
+
14
+ def get_by_connection(project_code, connection_id):
15
+ schema = st.session_state["dbschema"]
16
+ return table_group_queries.get_by_connection(schema, project_code, connection_id)
17
+
18
+
19
+ def edit(table_group):
20
+ schema = st.session_state["dbschema"]
21
+ table_group_queries.edit(schema, table_group)
22
+
23
+
24
+ def add(table_group):
25
+ schema = st.session_state["dbschema"]
26
+ table_group_queries.add(schema, table_group)
27
+
28
+
29
+ def cascade_delete(table_group_names, dry_run=False):
30
+ schema = st.session_state["dbschema"]
31
+ test_suite_names = get_test_suite_names_by_table_group_names(table_group_names)
32
+ can_be_deleted = not table_group_has_dependencies(schema, table_group_names, test_suite_names)
33
+ if not dry_run:
34
+ test_suite_service.cascade_delete(test_suite_names)
35
+ table_group_queries.cascade_delete(schema, table_group_names)
36
+ return can_be_deleted
37
+
38
+
39
+ def table_group_has_dependencies(schema, table_group_names, test_suite_names):
40
+ test_suite_usage_result = test_suite_service.has_test_suite_dependencies(schema, test_suite_names)
41
+ if not table_group_names:
42
+ table_group_usage_result = False
43
+ else:
44
+ table_group_usage_result = not table_group_queries.get_table_group_dependencies(schema, table_group_names).empty
45
+ return test_suite_usage_result or table_group_usage_result
46
+
47
+
48
+ def are_table_groups_in_use(table_group_names):
49
+ if not table_group_names:
50
+ return False
51
+
52
+ schema = st.session_state["dbschema"]
53
+
54
+ test_suite_names = get_test_suite_names_by_table_group_names(table_group_names)
55
+ test_suites_in_use = test_suite_service.are_test_suites_in_use(test_suite_names)
56
+
57
+ table_groups_in_use_result = table_group_queries.get_table_group_usage(schema, table_group_names)
58
+ table_groups_in_use = not table_groups_in_use_result.empty
59
+
60
+ return test_suites_in_use or table_groups_in_use
61
+
62
+
63
+ def get_test_suite_names_by_table_group_names(table_group_names):
64
+ if not table_group_names:
65
+ return []
66
+ schema = st.session_state["dbschema"]
67
+ test_suite_names = table_group_queries.get_test_suite_names_by_table_group_names(schema, table_group_names)
68
+ return test_suite_names.to_dict()["test_suite"].values()
69
+
70
+
71
+ def test_table_group(table_group, connection_id, project_code):
72
+ # get connection data
73
+ connection = connection_service.get_by_id(connection_id, hide_passwords=False)
74
+ connection_id = str(connection["connection_id"])
75
+
76
+ # get table group data
77
+ table_group_schema = table_group["table_group_schema"]
78
+ table_group_id = table_group["id"]
79
+ project_qc_schema = connection["project_qc_schema"]
80
+ profiling_table_set = table_group["profiling_table_set"]
81
+ profiling_include_mask = table_group["profiling_include_mask"]
82
+ profiling_exclude_mask = table_group["profiling_exclude_mask"]
83
+ profile_id_column_mask = table_group["profile_id_column_mask"]
84
+ profile_sk_column_mask = table_group["profile_sk_column_mask"]
85
+ profile_use_sampling = "Y" if table_group["profile_use_sampling"] else "N"
86
+ profile_sample_percent = table_group["profile_sample_percent"]
87
+ profile_sample_min_count = table_group["profile_sample_min_count"]
88
+
89
+ clsProfiling = connection_service.init_profiling_sql(project_code, connection, table_group_schema)
90
+
91
+ # Set General Parms
92
+ clsProfiling.table_groups_id = table_group_id
93
+ clsProfiling.connection_id = connection_id
94
+ clsProfiling.parm_do_sample = "N"
95
+ clsProfiling.parm_sample_size = 0
96
+ clsProfiling.parm_vldb_flag = "N"
97
+ clsProfiling.parm_do_freqs = "Y"
98
+ clsProfiling.parm_max_freq_length = 25
99
+ clsProfiling.parm_do_patterns = "Y"
100
+ clsProfiling.parm_max_pattern_length = 25
101
+ clsProfiling.profile_run_id = ""
102
+ clsProfiling.data_qc_schema = project_qc_schema
103
+ clsProfiling.data_schema = table_group_schema
104
+ clsProfiling.parm_table_set = get_profiling_table_set_with_quotes(profiling_table_set)
105
+ clsProfiling.parm_table_include_mask = profiling_include_mask
106
+ clsProfiling.parm_table_exclude_mask = profiling_exclude_mask
107
+ clsProfiling.profile_id_column_mask = profile_id_column_mask
108
+ clsProfiling.profile_sk_column_mask = profile_sk_column_mask
109
+ clsProfiling.profile_use_sampling = profile_use_sampling
110
+ clsProfiling.profile_sample_percent = profile_sample_percent
111
+ clsProfiling.profile_sample_min_count = profile_sample_min_count
112
+
113
+ query = clsProfiling.GetDDFQuery()
114
+ table_group_results = RetrieveDBResultsToDictList("PROJECT", query)
115
+
116
+ qc_results = connection_service.test_qc_connection(project_code, connection, init_profiling=False)
117
+
118
+ return table_group_results, qc_results
119
+
120
+
121
+ def get_profiling_table_set_with_quotes(profiling_table_set):
122
+ if not profiling_table_set:
123
+ return profiling_table_set
124
+
125
+ aux_list = []
126
+ split = profiling_table_set.split(",")
127
+ for item in split:
128
+ aux_list.append(f"'{item}'")
129
+ profiling_table_set = ",".join(aux_list)
130
+ return profiling_table_set
@@ -0,0 +1,117 @@
1
+ import streamlit as st
2
+
3
+ import testgen.ui.queries.test_definition_queries as test_definition_queries
4
+ import testgen.ui.services.connection_service as connection_service
5
+ import testgen.ui.services.database_service as database_service
6
+ import testgen.ui.services.table_group_service as table_group_service
7
+ import testgen.ui.services.test_run_service as test_run_service
8
+
9
+
10
+ def update_attribute(test_definition_ids, attribute, value):
11
+ schema = st.session_state["dbschema"]
12
+ raw_value = "Y" if value else "N"
13
+ test_definition_queries.update_attribute(schema, test_definition_ids, attribute, raw_value)
14
+
15
+
16
+ def get_test_definitions(
17
+ project_code=None, test_suite=None, table_name=None, column_name=None, test_definition_ids=None
18
+ ):
19
+ schema = st.session_state["dbschema"]
20
+ return test_definition_queries.get_test_definitions(
21
+ schema, project_code, test_suite, table_name, column_name, test_definition_ids
22
+ )
23
+
24
+
25
+ def delete(test_definition_ids, dry_run=False):
26
+ schema = st.session_state["dbschema"]
27
+ usage_result = test_definition_queries.get_test_definition_usage(schema, test_definition_ids)
28
+ can_be_deleted = usage_result.empty
29
+ if not dry_run and can_be_deleted:
30
+ test_definition_queries.delete(schema, test_definition_ids)
31
+ return can_be_deleted
32
+
33
+
34
+ def cascade_delete(test_suite_names):
35
+ schema = st.session_state["dbschema"]
36
+ test_run_service.cascade_delete(test_suite_names)
37
+ test_definition_queries.cascade_delete(schema, test_suite_names)
38
+
39
+
40
+ def add(test_definition):
41
+ schema = st.session_state["dbschema"]
42
+ prepare_to_persist(test_definition)
43
+ test_definition_queries.add(schema, test_definition)
44
+
45
+
46
+ def update(test_definition):
47
+ schema = st.session_state["dbschema"]
48
+ prepare_to_persist(test_definition)
49
+ return test_definition_queries.update(schema, test_definition)
50
+
51
+
52
+ def prepare_to_persist(test_definition):
53
+ # severity
54
+ if test_definition["severity"] and test_definition["severity"].startswith("Inherited"):
55
+ test_definition["severity"] = None
56
+
57
+ test_definition["export_to_observability"] = prepare_boolean_for_update(
58
+ test_definition["export_to_observability_raw"]
59
+ )
60
+ test_definition["lock_refresh"] = prepare_boolean_for_update(test_definition["lock_refresh"])
61
+ test_definition["test_active"] = prepare_boolean_for_update(test_definition["test_active"])
62
+
63
+ if test_definition["custom_query"] is not None:
64
+ test_definition["custom_query"] = test_definition["custom_query"].strip()
65
+ if test_definition["custom_query"].endswith(";"):
66
+ test_definition["custom_query"] = test_definition["custom_query"][:-1]
67
+
68
+ empty_if_null(test_definition)
69
+
70
+
71
+ def empty_if_null(test_definition):
72
+ for k, v in test_definition.items():
73
+ if v is None:
74
+ test_definition[k] = ""
75
+
76
+
77
+ def prepare_boolean_for_update(value):
78
+ if "Yes" == value or "Y" == value or value is True:
79
+ return "Y"
80
+ elif "No" == value or "N" == value or value is False:
81
+ return "N"
82
+ else:
83
+ return None
84
+
85
+
86
+ def validate_test(test_definition):
87
+ schema = test_definition["schema_name"]
88
+ table_name = test_definition["table_name"]
89
+
90
+ if test_definition["test_type"] == "Condition_Flag":
91
+ condition = test_definition["custom_query"]
92
+ sql_query = f"""SELECT COALESCE(CAST(SUM(CASE WHEN {condition} THEN 1 ELSE 0 END) AS VARCHAR(1000) ) || '|' ,'<NULL>|') FROM {schema}.{table_name}"""
93
+ else:
94
+ sql_query = test_definition["custom_query"]
95
+ sql_query = sql_query.replace("{DATA_SCHEMA}", schema)
96
+
97
+ table_group_id = test_definition["table_groups_id"]
98
+ table_group_df = table_group_service.get_by_id(table_group_id)
99
+
100
+ connection_id = table_group_df.iloc[0]["connection_id"]
101
+
102
+ connection = connection_service.get_by_id(connection_id, hide_passwords=False)
103
+
104
+ database_service.retrieve_target_db_data(
105
+ connection["sql_flavor"],
106
+ connection["project_host"],
107
+ connection["project_port"],
108
+ connection["project_db"],
109
+ connection["project_user"],
110
+ connection["password"],
111
+ connection["url"],
112
+ connection["connect_by_url"],
113
+ connection["connect_by_key"],
114
+ connection["private_key"],
115
+ connection["private_key_passphrase"],
116
+ sql_query,
117
+ )
@@ -0,0 +1,13 @@
1
+ import streamlit as st
2
+
3
+ import testgen.ui.queries.test_run_queries as test_run_queries
4
+
5
+
6
+ def cascade_delete(test_suite_names):
7
+ schema = st.session_state["dbschema"]
8
+ test_run_queries.cascade_delete(schema, test_suite_names)
9
+
10
+
11
+ def update_status(test_run_id, status):
12
+ schema = st.session_state["dbschema"]
13
+ test_run_queries.update_status(schema, test_run_id, status)