recce-nightly 0.62.0.20250417__py3-none-any.whl → 1.30.0.20251221__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.
Potentially problematic release.
This version of recce-nightly might be problematic. Click here for more details.
- recce/VERSION +1 -1
- recce/__init__.py +27 -22
- recce/adapter/base.py +11 -14
- recce/adapter/dbt_adapter/__init__.py +845 -461
- recce/adapter/dbt_adapter/dbt_version.py +3 -0
- recce/adapter/sqlmesh_adapter.py +24 -35
- recce/apis/check_api.py +59 -42
- recce/apis/check_events_api.py +353 -0
- recce/apis/check_func.py +41 -35
- recce/apis/run_api.py +25 -19
- recce/apis/run_func.py +64 -25
- recce/artifact.py +119 -51
- recce/cli.py +1301 -324
- recce/config.py +43 -34
- recce/connect_to_cloud.py +138 -0
- recce/core.py +55 -47
- recce/data/404/index.html +2 -0
- recce/data/404.html +2 -1
- recce/data/__next.@lineage.!KHNsb3Qp.__PAGE__.txt +7 -0
- recce/data/__next.@lineage.!KHNsb3Qp.txt +4 -0
- recce/data/__next.__PAGE__.txt +6 -0
- recce/data/__next._full.txt +32 -0
- recce/data/__next._head.txt +8 -0
- recce/data/__next._index.txt +14 -0
- recce/data/__next._tree.txt +8 -0
- recce/data/_next/static/chunks/025a7e3e3f9f40ae.js +1 -0
- recce/data/_next/static/chunks/0ce56d67ef5779ca.js +4 -0
- recce/data/_next/static/chunks/1a6a78780155dac7.js +48 -0
- recce/data/_next/static/chunks/1de8485918b9182a.css +2 -0
- recce/data/_next/static/chunks/1e4b1b50d1e34993.js +1 -0
- recce/data/_next/static/chunks/206d5d181e4c738e.js +1 -0
- recce/data/_next/static/chunks/2c357efc34c5b859.js +25 -0
- recce/data/_next/static/chunks/2e9d95d2d48c479c.js +1 -0
- recce/data/_next/static/chunks/2f016dc4a3edad2e.js +2 -0
- recce/data/_next/static/chunks/313251962d698f7c.js +1 -0
- recce/data/_next/static/chunks/3a9f021f38eb5574.css +1 -0
- recce/data/_next/static/chunks/40079da8d2b8f651.js +1 -0
- recce/data/_next/static/chunks/4599182bffb64661.js +38 -0
- recce/data/_next/static/chunks/4e62f6e184173580.js +1 -0
- recce/data/_next/static/chunks/5c4dfb0d09eaa401.js +1 -0
- recce/data/_next/static/chunks/69e4f06ccfdfc3ac.js +1 -0
- recce/data/_next/static/chunks/6b206cb4707d6bee.js +1 -0
- recce/data/_next/static/chunks/6d8557f062aa4386.css +1 -0
- recce/data/_next/static/chunks/7fbe3650bd83b6b5.js +1 -0
- recce/data/_next/static/chunks/83fa823a825674f6.js +1 -0
- recce/data/_next/static/chunks/848a6c9b5f55f7ed.js +1 -0
- recce/data/_next/static/chunks/859462b0858aef88.css +2 -0
- recce/data/_next/static/chunks/923964f18c87d0f1.css +1 -0
- recce/data/_next/static/chunks/939390f911895d7c.js +48 -0
- recce/data/_next/static/chunks/99a9817237a07f43.js +1 -0
- recce/data/_next/static/chunks/9fed8b4b2b924054.js +5 -0
- recce/data/_next/static/chunks/b6949f6c5892110c.js +1 -0
- recce/data/_next/static/chunks/b851a1d3f8149828.js +1 -0
- recce/data/_next/static/chunks/c734f9ad957de0b4.js +1 -0
- recce/data/_next/static/chunks/cdde321b0ec75717.js +2 -0
- recce/data/_next/static/chunks/d0f91117d77ff844.css +1 -0
- recce/data/_next/static/chunks/d6c8667911c2500f.js +1 -0
- recce/data/_next/static/chunks/da8dab68c02752cf.js +74 -0
- recce/data/_next/static/chunks/dc074049c9d12d97.js +109 -0
- recce/data/_next/static/chunks/ee7f1a8227342421.js +1 -0
- recce/data/_next/static/chunks/fa2f4e56c2fccc73.js +1 -0
- recce/data/_next/static/chunks/turbopack-1fad664f62979b93.js +3 -0
- recce/data/_next/static/media/favicon.a8d38d84.ico +0 -0
- recce/data/_next/static/media/montserrat-cyrillic-800-normal.d80d830d.woff2 +0 -0
- recce/data/_next/static/media/montserrat-cyrillic-800-normal.f9d58125.woff +0 -0
- recce/data/_next/static/media/montserrat-cyrillic-ext-800-normal.076c2a93.woff2 +0 -0
- recce/data/_next/static/media/montserrat-cyrillic-ext-800-normal.a4fa76b5.woff +0 -0
- recce/data/_next/static/media/montserrat-latin-800-normal.cde454cc.woff2 +0 -0
- recce/data/_next/static/media/montserrat-latin-800-normal.d5761935.woff +0 -0
- recce/data/_next/static/media/montserrat-latin-ext-800-normal.40ec0659.woff2 +0 -0
- recce/data/_next/static/media/montserrat-latin-ext-800-normal.b671449b.woff +0 -0
- recce/data/_next/static/media/montserrat-vietnamese-800-normal.9f7b8541.woff +0 -0
- recce/data/_next/static/media/montserrat-vietnamese-800-normal.f9eb854e.woff2 +0 -0
- recce/data/_next/static/nX-Uz0AH6Tc6hIQUFGqaB/_buildManifest.js +11 -0
- recce/data/_next/static/nX-Uz0AH6Tc6hIQUFGqaB/_clientMiddlewareManifest.json +1 -0
- recce/data/_not-found/__next._full.txt +24 -0
- recce/data/_not-found/__next._head.txt +8 -0
- recce/data/_not-found/__next._index.txt +13 -0
- recce/data/_not-found/__next._not-found.__PAGE__.txt +5 -0
- recce/data/_not-found/__next._not-found.txt +4 -0
- recce/data/_not-found/__next._tree.txt +6 -0
- recce/data/_not-found/index.html +2 -0
- recce/data/_not-found/index.txt +24 -0
- recce/data/auth_callback.html +68 -0
- recce/data/checks/__next.@lineage.__DEFAULT__.txt +7 -0
- recce/data/checks/__next._full.txt +39 -0
- recce/data/checks/__next._head.txt +8 -0
- recce/data/checks/__next._index.txt +14 -0
- recce/data/checks/__next._tree.txt +8 -0
- recce/data/checks/__next.checks.__PAGE__.txt +10 -0
- recce/data/checks/__next.checks.txt +4 -0
- recce/data/checks/index.html +2 -0
- recce/data/checks/index.txt +39 -0
- recce/data/imgs/reload-image.svg +4 -0
- recce/data/index.html +2 -27
- recce/data/index.txt +32 -7
- recce/data/lineage/__next.@lineage.__DEFAULT__.txt +7 -0
- recce/data/lineage/__next._full.txt +39 -0
- recce/data/lineage/__next._head.txt +8 -0
- recce/data/lineage/__next._index.txt +14 -0
- recce/data/lineage/__next._tree.txt +8 -0
- recce/data/lineage/__next.lineage.__PAGE__.txt +10 -0
- recce/data/lineage/__next.lineage.txt +4 -0
- recce/data/lineage/index.html +2 -0
- recce/data/lineage/index.txt +39 -0
- recce/data/query/__next.@lineage.__DEFAULT__.txt +7 -0
- recce/data/query/__next._full.txt +37 -0
- recce/data/query/__next._head.txt +8 -0
- recce/data/query/__next._index.txt +14 -0
- recce/data/query/__next._tree.txt +8 -0
- recce/data/query/__next.query.__PAGE__.txt +9 -0
- recce/data/query/__next.query.txt +4 -0
- recce/data/query/index.html +2 -0
- recce/data/query/index.txt +37 -0
- recce/diff.py +6 -12
- recce/event/CONFIG.bak +1 -0
- recce/event/__init__.py +86 -74
- recce/event/collector.py +33 -22
- recce/event/track.py +49 -27
- recce/exceptions.py +1 -1
- recce/git.py +7 -7
- recce/github.py +57 -53
- recce/mcp_server.py +725 -0
- recce/models/__init__.py +4 -1
- recce/models/check.py +438 -21
- recce/models/run.py +1 -0
- recce/models/types.py +134 -28
- recce/pull_request.py +27 -25
- recce/run.py +179 -122
- recce/server.py +394 -104
- recce/state/__init__.py +31 -0
- recce/state/cloud.py +644 -0
- recce/state/const.py +26 -0
- recce/state/local.py +56 -0
- recce/state/state.py +119 -0
- recce/state/state_loader.py +174 -0
- recce/summary.py +196 -149
- recce/tasks/__init__.py +19 -3
- recce/tasks/core.py +11 -13
- recce/tasks/dataframe.py +82 -18
- recce/tasks/histogram.py +69 -34
- recce/tasks/lineage.py +2 -2
- recce/tasks/profile.py +152 -86
- recce/tasks/query.py +180 -89
- recce/tasks/rowcount.py +37 -31
- recce/tasks/schema.py +18 -15
- recce/tasks/top_k.py +35 -35
- recce/tasks/utils.py +147 -0
- recce/tasks/valuediff.py +247 -155
- recce/util/__init__.py +3 -0
- recce/util/api_token.py +80 -0
- recce/util/breaking.py +105 -100
- recce/util/cll.py +274 -219
- recce/util/cloud/__init__.py +15 -0
- recce/util/cloud/base.py +115 -0
- recce/util/cloud/check_events.py +190 -0
- recce/util/cloud/checks.py +242 -0
- recce/util/io.py +22 -17
- recce/util/lineage.py +65 -16
- recce/util/logger.py +1 -1
- recce/util/onboarding_state.py +45 -0
- recce/util/perf_tracking.py +85 -0
- recce/util/recce_cloud.py +347 -72
- recce/util/singleton.py +4 -4
- recce/util/startup_perf.py +121 -0
- recce/yaml/__init__.py +7 -10
- recce_nightly-1.30.0.20251221.dist-info/METADATA +195 -0
- recce_nightly-1.30.0.20251221.dist-info/RECORD +183 -0
- {recce_nightly-0.62.0.20250417.dist-info → recce_nightly-1.30.0.20251221.dist-info}/WHEEL +1 -2
- recce/data/_next/static/chunks/1f229bf6-d9fe92e56db8d93b.js +0 -1
- recce/data/_next/static/chunks/29e3cc0d-8c150e37dff9631b.js +0 -1
- recce/data/_next/static/chunks/36e1c10d-bb0210cbd6573a8d.js +0 -1
- recce/data/_next/static/chunks/3998a672-eaad84bdd88cc73e.js +0 -1
- recce/data/_next/static/chunks/450c323b-1bb5db526e54435a.js +0 -1
- recce/data/_next/static/chunks/47d8844f-79a1b53c66a7d7ec.js +0 -1
- recce/data/_next/static/chunks/500-e51c92a025a51234.js +0 -65
- recce/data/_next/static/chunks/6dc81886-c94b9b91bc2c3caf.js +0 -1
- recce/data/_next/static/chunks/700-3b65fc3666820d00.js +0 -2
- recce/data/_next/static/chunks/7a8a3e83-d7fa409d97b38b2b.js +0 -1
- recce/data/_next/static/chunks/7f27ae6c-413f6b869a04183a.js +0 -1
- recce/data/_next/static/chunks/9746af58-d74bef4d03eea6ab.js +0 -1
- recce/data/_next/static/chunks/a30376cd-7d806e1602f2dc3a.js +0 -1
- recce/data/_next/static/chunks/app/_not-found/page-8a886fa0855c3105.js +0 -1
- recce/data/_next/static/chunks/app/layout-9102e22cb73f74d6.js +0 -1
- recce/data/_next/static/chunks/app/page-9adc25782272ed2e.js +0 -1
- recce/data/_next/static/chunks/b63b1b3f-7395c74e11a14e95.js +0 -1
- recce/data/_next/static/chunks/c132bf7d-8102037f9ccf372a.js +0 -1
- recce/data/_next/static/chunks/c1ceaa8b-a1e442154d23515e.js +0 -1
- recce/data/_next/static/chunks/cd9f8d63-cf0d5a7b0f7a92e8.js +0 -54
- recce/data/_next/static/chunks/ce84277d-f42c2c58049cea2d.js +0 -1
- recce/data/_next/static/chunks/e24bf851-0f8cbc99656833e7.js +0 -1
- recce/data/_next/static/chunks/fee69bc6-f17d36c080742e74.js +0 -1
- recce/data/_next/static/chunks/framework-ded83d71b51ce901.js +0 -1
- recce/data/_next/static/chunks/main-a0859f1f36d0aa6c.js +0 -1
- recce/data/_next/static/chunks/main-app-0225a2255968e566.js +0 -1
- recce/data/_next/static/chunks/pages/_app-d5672bf3d8b6371b.js +0 -1
- recce/data/_next/static/chunks/pages/_error-ed75be3f25588548.js +0 -1
- recce/data/_next/static/chunks/webpack-567d72f0bc0820d5.js +0 -1
- recce/data/_next/static/css/c9ecb46a4b21c126.css +0 -14
- recce/data/_next/static/media/montserrat-cyrillic-800-normal.22628180.woff2 +0 -0
- recce/data/_next/static/media/montserrat-cyrillic-800-normal.31d693bb.woff +0 -0
- recce/data/_next/static/media/montserrat-cyrillic-ext-800-normal.7e2c1e62.woff +0 -0
- recce/data/_next/static/media/montserrat-cyrillic-ext-800-normal.94a63aea.woff2 +0 -0
- recce/data/_next/static/media/montserrat-latin-800-normal.6f8fa298.woff2 +0 -0
- recce/data/_next/static/media/montserrat-latin-800-normal.97e20d5e.woff +0 -0
- recce/data/_next/static/media/montserrat-latin-ext-800-normal.013b84f9.woff2 +0 -0
- recce/data/_next/static/media/montserrat-latin-ext-800-normal.aff52ab0.woff +0 -0
- recce/data/_next/static/media/montserrat-vietnamese-800-normal.5f21869b.woff +0 -0
- recce/data/_next/static/media/montserrat-vietnamese-800-normal.c0035377.woff2 +0 -0
- recce/data/_next/static/qiyFlux77VkhxiceAJe_F/_buildManifest.js +0 -1
- recce/state.py +0 -753
- recce_nightly-0.62.0.20250417.dist-info/METADATA +0 -311
- recce_nightly-0.62.0.20250417.dist-info/RECORD +0 -139
- recce_nightly-0.62.0.20250417.dist-info/top_level.txt +0 -2
- tests/__init__.py +0 -0
- tests/adapter/__init__.py +0 -0
- tests/adapter/dbt_adapter/__init__.py +0 -0
- tests/adapter/dbt_adapter/conftest.py +0 -13
- tests/adapter/dbt_adapter/dbt_test_helper.py +0 -283
- tests/adapter/dbt_adapter/test_dbt_adapter.py +0 -40
- tests/adapter/dbt_adapter/test_dbt_cll.py +0 -102
- tests/adapter/dbt_adapter/test_selector.py +0 -177
- tests/tasks/__init__.py +0 -0
- tests/tasks/conftest.py +0 -4
- tests/tasks/test_histogram.py +0 -137
- tests/tasks/test_lineage.py +0 -42
- tests/tasks/test_preset_checks.py +0 -50
- tests/tasks/test_profile.py +0 -73
- tests/tasks/test_query.py +0 -151
- tests/tasks/test_row_count.py +0 -116
- tests/tasks/test_schema.py +0 -99
- tests/tasks/test_top_k.py +0 -73
- tests/tasks/test_valuediff.py +0 -74
- tests/test_cli.py +0 -122
- tests/test_config.py +0 -45
- tests/test_core.py +0 -27
- tests/test_dbt.py +0 -36
- tests/test_pull_request.py +0 -130
- tests/test_server.py +0 -98
- tests/test_state.py +0 -123
- tests/test_summary.py +0 -57
- /recce/data/_next/static/chunks/{polyfills-42372ed130431b0a.js → a6dad97d9634a72d.js} +0 -0
- /recce/data/_next/static/{qiyFlux77VkhxiceAJe_F → nX-Uz0AH6Tc6hIQUFGqaB}/_ssgManifest.js +0 -0
- {recce_nightly-0.62.0.20250417.dist-info → recce_nightly-1.30.0.20251221.dist-info}/entry_points.txt +0 -0
- {recce_nightly-0.62.0.20250417.dist-info → recce_nightly-1.30.0.20251221.dist-info}/licenses/LICENSE +0 -0
recce/summary.py
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import os
|
|
2
2
|
import sys
|
|
3
|
-
from typing import List,
|
|
3
|
+
from typing import Dict, List, Optional, Set, Type, Union
|
|
4
4
|
from uuid import UUID
|
|
5
5
|
|
|
6
6
|
from pydantic import BaseModel
|
|
7
7
|
|
|
8
8
|
from recce.apis.check_func import get_node_name_by_id
|
|
9
9
|
from recce.core import RecceContext
|
|
10
|
-
from recce.models import CheckDAO, RunDAO, RunType
|
|
10
|
+
from recce.models import CheckDAO, Run, RunDAO, RunType
|
|
11
11
|
from recce.tasks.core import TaskResultDiffer
|
|
12
12
|
from recce.tasks.histogram import HistogramDiffTaskResultDiffer
|
|
13
13
|
from recce.tasks.profile import ProfileDiffResultDiffer
|
|
@@ -15,13 +15,16 @@ from recce.tasks.query import QueryDiffResultDiffer
|
|
|
15
15
|
from recce.tasks.rowcount import RowCountDiffResultDiffer
|
|
16
16
|
from recce.tasks.schema import SchemaDiffResultDiffer
|
|
17
17
|
from recce.tasks.top_k import TopKDiffTaskResultDiffer
|
|
18
|
-
from recce.tasks.valuediff import
|
|
18
|
+
from recce.tasks.valuediff import (
|
|
19
|
+
ValueDiffDetailTaskResultDiffer,
|
|
20
|
+
ValueDiffTaskResultDiffer,
|
|
21
|
+
)
|
|
19
22
|
|
|
20
|
-
RECCE_CLOUD_HOST = os.environ.get(
|
|
23
|
+
RECCE_CLOUD_HOST = os.environ.get("RECCE_CLOUD_HOST", "https://cloud.datarecce.io")
|
|
21
24
|
|
|
22
|
-
ADD_COLOR =
|
|
23
|
-
MODIFIED_COLOR =
|
|
24
|
-
REMOVE_COLOR =
|
|
25
|
+
ADD_COLOR = "#1dce00"
|
|
26
|
+
MODIFIED_COLOR = "#ffa502"
|
|
27
|
+
REMOVE_COLOR = "#ff067e"
|
|
25
28
|
|
|
26
29
|
MAX_MERMAID_TEXT_SIZE = 50000 # source: https://mermaid.js.org/config/schema-docs/config.html#maxtextsize
|
|
27
30
|
|
|
@@ -42,44 +45,44 @@ class Node:
|
|
|
42
45
|
base_data: dict
|
|
43
46
|
current_data: dict
|
|
44
47
|
|
|
45
|
-
def __init__(self, node_id: str, node_data: dict, data_from: str =
|
|
48
|
+
def __init__(self, node_id: str, node_data: dict, data_from: str = "base"):
|
|
46
49
|
self.id = node_id
|
|
47
|
-
self.name = node_data[
|
|
50
|
+
self.name = node_data["name"]
|
|
48
51
|
self.data_from = data_from
|
|
49
|
-
self.resource_type = node_data[
|
|
50
|
-
self.package_name = node_data[
|
|
52
|
+
self.resource_type = node_data["resource_type"]
|
|
53
|
+
self.package_name = node_data["package_name"]
|
|
51
54
|
self.children = []
|
|
52
55
|
self.parents = []
|
|
53
56
|
|
|
54
57
|
self.base_data = {}
|
|
55
58
|
self.current_data = {}
|
|
56
59
|
|
|
57
|
-
if data_from ==
|
|
60
|
+
if data_from == "base":
|
|
58
61
|
self.base_data = node_data
|
|
59
|
-
elif data_from ==
|
|
62
|
+
elif data_from == "current":
|
|
60
63
|
self.current_data = node_data
|
|
61
64
|
|
|
62
65
|
@property
|
|
63
66
|
def change_status(self):
|
|
64
|
-
base_checksum = self.base_data.get(
|
|
65
|
-
curr_checksum = self.current_data.get(
|
|
66
|
-
if self.data_from ==
|
|
67
|
-
return
|
|
68
|
-
elif self.data_from ==
|
|
69
|
-
return
|
|
67
|
+
base_checksum = self.base_data.get("checksum", {}).get("checksum")
|
|
68
|
+
curr_checksum = self.current_data.get("checksum", {}).get("checksum")
|
|
69
|
+
if self.data_from == "base":
|
|
70
|
+
return "removed"
|
|
71
|
+
elif self.data_from == "current":
|
|
72
|
+
return "added"
|
|
70
73
|
elif base_checksum and curr_checksum and base_checksum != curr_checksum:
|
|
71
|
-
return
|
|
74
|
+
return "modified"
|
|
72
75
|
return None
|
|
73
76
|
|
|
74
77
|
def update_data(self, node_data: dict, data_from: str):
|
|
75
|
-
if data_from not in [
|
|
76
|
-
raise ValueError(f
|
|
78
|
+
if data_from not in ["base", "current"]:
|
|
79
|
+
raise ValueError(f"Invalid data_from value: {data_from}")
|
|
77
80
|
if self.data_from != data_from:
|
|
78
|
-
self.data_from =
|
|
81
|
+
self.data_from = "both"
|
|
79
82
|
|
|
80
|
-
if data_from ==
|
|
83
|
+
if data_from == "base":
|
|
81
84
|
self.base_data = node_data
|
|
82
|
-
elif data_from ==
|
|
85
|
+
elif data_from == "current":
|
|
83
86
|
self.current_data = node_data
|
|
84
87
|
|
|
85
88
|
def append_parent(self, parent_id: str):
|
|
@@ -93,8 +96,8 @@ class Node:
|
|
|
93
96
|
def _cal_row_count_delta_percentage(self):
|
|
94
97
|
row_count_diff, run_result = _get_node_row_count_diff(self.id, self.name)
|
|
95
98
|
if row_count_diff:
|
|
96
|
-
base = run_result.get(
|
|
97
|
-
current = run_result.get(
|
|
99
|
+
base = run_result.get("base", 0)
|
|
100
|
+
current = run_result.get("curr", 0)
|
|
98
101
|
if int(current) > int(base):
|
|
99
102
|
p = (int(current) - int(base)) / int(current) * 100
|
|
100
103
|
return f'🔼 +{round(p, 2) if p > 0.1 else "<0.1"}%'
|
|
@@ -104,25 +107,25 @@ class Node:
|
|
|
104
107
|
return None
|
|
105
108
|
|
|
106
109
|
def _get_schema_diff(self):
|
|
107
|
-
base_schema = self.base_data.get(
|
|
108
|
-
current_schema = self.current_data.get(
|
|
110
|
+
base_schema = self.base_data.get("columns", {})
|
|
111
|
+
current_schema = self.current_data.get("columns", {})
|
|
109
112
|
schema_diff = TaskResultDiffer.diff(base_schema, current_schema)
|
|
110
113
|
return schema_diff
|
|
111
114
|
|
|
112
115
|
def _what_changed(self, checks=None):
|
|
113
116
|
changes = []
|
|
114
|
-
if self.change_status ==
|
|
115
|
-
return [
|
|
116
|
-
elif self.change_status ==
|
|
117
|
-
return [
|
|
118
|
-
elif self.change_status ==
|
|
119
|
-
changes.append(
|
|
117
|
+
if self.change_status == "added":
|
|
118
|
+
return ["Added Node"]
|
|
119
|
+
elif self.change_status == "removed":
|
|
120
|
+
return ["Removed Node"]
|
|
121
|
+
elif self.change_status == "modified":
|
|
122
|
+
changes.append("Code")
|
|
120
123
|
row_count_delta_percentage = self._cal_row_count_delta_percentage()
|
|
121
124
|
if row_count_delta_percentage:
|
|
122
|
-
changes.append(f
|
|
125
|
+
changes.append(f"Row Count {row_count_delta_percentage}")
|
|
123
126
|
schema_diff = self._get_schema_diff()
|
|
124
127
|
if schema_diff:
|
|
125
|
-
changes.append(
|
|
128
|
+
changes.append("Schema")
|
|
126
129
|
|
|
127
130
|
if checks:
|
|
128
131
|
for check in checks:
|
|
@@ -131,7 +134,7 @@ class Node:
|
|
|
131
134
|
# Skip the row count and schema diff check, since we already have it.
|
|
132
135
|
continue
|
|
133
136
|
if check.node_ids and self.id in check.node_ids:
|
|
134
|
-
changes.append(str(check.type).replace(
|
|
137
|
+
changes.append(str(check.type).replace("_", " ").title())
|
|
135
138
|
return changes
|
|
136
139
|
|
|
137
140
|
def get_node_str(self, checks=None):
|
|
@@ -140,12 +143,12 @@ class Node:
|
|
|
140
143
|
|
|
141
144
|
if self.change_status is not None:
|
|
142
145
|
is_changed = True
|
|
143
|
-
if self.change_status ==
|
|
144
|
-
style = f
|
|
145
|
-
elif self.change_status ==
|
|
146
|
-
style = f
|
|
147
|
-
elif self.change_status ==
|
|
148
|
-
style = f
|
|
146
|
+
if self.change_status == "added":
|
|
147
|
+
style = f"style {self.id} stroke:{ADD_COLOR}"
|
|
148
|
+
elif self.change_status == "modified":
|
|
149
|
+
style = f"style {self.id} stroke:{MODIFIED_COLOR}"
|
|
150
|
+
elif self.change_status == "removed":
|
|
151
|
+
style = f"style {self.id} stroke:{REMOVE_COLOR}"
|
|
149
152
|
|
|
150
153
|
if checks:
|
|
151
154
|
for check in checks:
|
|
@@ -154,13 +157,13 @@ class Node:
|
|
|
154
157
|
|
|
155
158
|
content_output = f'{self.id}["{self.name}'
|
|
156
159
|
if is_changed:
|
|
157
|
-
content_output +=
|
|
160
|
+
content_output += "\n\n[What's Changed]\n"
|
|
158
161
|
changes = self._what_changed(checks)
|
|
159
|
-
content_output +=
|
|
162
|
+
content_output += ", ".join(changes)
|
|
160
163
|
|
|
161
164
|
content_output += '"]\n'
|
|
162
165
|
if style:
|
|
163
|
-
content_output += f
|
|
166
|
+
content_output += f"{style}\n"
|
|
164
167
|
return content_output
|
|
165
168
|
|
|
166
169
|
|
|
@@ -171,7 +174,7 @@ class Edge:
|
|
|
171
174
|
parent_id: str
|
|
172
175
|
change_status: Union[str, None]
|
|
173
176
|
|
|
174
|
-
def __init__(self, edge_id: str, parent_id: str, child_id: str, edge_from: str =
|
|
177
|
+
def __init__(self, edge_id: str, parent_id: str, child_id: str, edge_from: str = "base"):
|
|
175
178
|
self.id = edge_id
|
|
176
179
|
self.edge_from = edge_from
|
|
177
180
|
self.child_id = child_id
|
|
@@ -179,7 +182,7 @@ class Edge:
|
|
|
179
182
|
|
|
180
183
|
def update_edge_from(self, edge_from: str):
|
|
181
184
|
if self.edge_from != edge_from:
|
|
182
|
-
self.edge_from =
|
|
185
|
+
self.edge_from = "both"
|
|
183
186
|
|
|
184
187
|
|
|
185
188
|
class CheckSummary(BaseModel):
|
|
@@ -225,21 +228,21 @@ class LineageGraph:
|
|
|
225
228
|
edges: Dict[str, Edge] = {}
|
|
226
229
|
checks: List[CheckSummary] = None
|
|
227
230
|
|
|
228
|
-
def create_node(self, node_id: str, node_data: dict, data_from: str =
|
|
231
|
+
def create_node(self, node_id: str, node_data: dict, data_from: str = "base"):
|
|
229
232
|
if node_id not in self.nodes:
|
|
230
233
|
self.nodes[node_id] = Node(node_id, node_data, data_from)
|
|
231
234
|
else:
|
|
232
235
|
self.nodes[node_id].update_data(node_data, data_from)
|
|
233
236
|
|
|
234
|
-
def create_edge(self, parent_id: str, child_id: str, edge_from: str =
|
|
237
|
+
def create_edge(self, parent_id: str, child_id: str, edge_from: str = "base"):
|
|
235
238
|
if parent_id not in self.nodes:
|
|
236
|
-
_warn(f
|
|
239
|
+
_warn(f"Parent node {parent_id} not found in graph")
|
|
237
240
|
return
|
|
238
241
|
if child_id not in self.nodes:
|
|
239
|
-
_warn(f
|
|
242
|
+
_warn(f"Child node {child_id} not found in graph")
|
|
240
243
|
return
|
|
241
244
|
|
|
242
|
-
edge_id = f
|
|
245
|
+
edge_id = f"{parent_id}-->{child_id}"
|
|
243
246
|
if edge_id in self.edges:
|
|
244
247
|
self.edges[edge_id].update_edge_from(edge_from)
|
|
245
248
|
else:
|
|
@@ -249,67 +252,89 @@ class LineageGraph:
|
|
|
249
252
|
|
|
250
253
|
@property
|
|
251
254
|
def modified_set(self) -> Set[str]:
|
|
252
|
-
return set(
|
|
255
|
+
return set(
|
|
256
|
+
[node_id for node_id, node in self.nodes.items() if node.change_status in ["added", "removed", "modified"]]
|
|
257
|
+
)
|
|
253
258
|
|
|
254
259
|
def get_edge_str(self, edge_id):
|
|
255
260
|
edge = self.edges[edge_id]
|
|
256
261
|
child = self.nodes[edge.child_id]
|
|
257
262
|
|
|
258
|
-
if child.change_status ==
|
|
259
|
-
return f
|
|
260
|
-
if child.change_status is None or child.change_status ==
|
|
261
|
-
return f
|
|
262
|
-
if child.change_status ==
|
|
263
|
-
return f
|
|
263
|
+
if child.change_status == "removed":
|
|
264
|
+
return f"{edge.parent_id}-.->{edge.child_id}\n"
|
|
265
|
+
if child.change_status is None or child.change_status == "modified":
|
|
266
|
+
return f"{edge.parent_id}---->{edge.child_id}\n"
|
|
267
|
+
if child.change_status == "added":
|
|
268
|
+
return f"{edge.parent_id}-...->{edge.child_id}\n"
|
|
264
269
|
|
|
265
270
|
|
|
266
271
|
def _build_lineage_graph(base, current) -> LineageGraph:
|
|
267
272
|
graph = LineageGraph()
|
|
268
273
|
|
|
274
|
+
# Get the current package name to filter nodes (from the current manifest metadata)
|
|
275
|
+
package_name = None
|
|
276
|
+
manifest_metadata = current.get("manifest_metadata")
|
|
277
|
+
if manifest_metadata and hasattr(manifest_metadata, "project_name"):
|
|
278
|
+
# The default package name is the project name
|
|
279
|
+
package_name = manifest_metadata.project_name
|
|
280
|
+
|
|
269
281
|
# Init Graph nodes with base & current nodes
|
|
270
|
-
for node_id, node_data in base.get(
|
|
271
|
-
|
|
282
|
+
for node_id, node_data in base.get("nodes", {}).items():
|
|
283
|
+
# Skip nodes that are not from the current package
|
|
284
|
+
if package_name and node_data.get("package_name") != package_name:
|
|
285
|
+
continue
|
|
286
|
+
graph.create_node(node_id, node_data, "base")
|
|
272
287
|
|
|
273
|
-
for node_id, node_data in current.get(
|
|
288
|
+
for node_id, node_data in current.get("nodes", {}).items():
|
|
289
|
+
# Skip nodes that are not from the current package
|
|
290
|
+
if package_name and node_data.get("package_name") != package_name:
|
|
291
|
+
continue
|
|
274
292
|
if node_id not in graph.nodes:
|
|
275
|
-
node = Node(node_id, node_data,
|
|
293
|
+
node = Node(node_id, node_data, "current")
|
|
276
294
|
graph.nodes[node_id] = node
|
|
277
295
|
else:
|
|
278
296
|
node = graph.nodes[node_id]
|
|
279
|
-
node.update_data(node_data,
|
|
297
|
+
node.update_data(node_data, "current")
|
|
280
298
|
|
|
281
299
|
# Build edges
|
|
282
|
-
for child_id, parents in base.get(
|
|
300
|
+
for child_id, parents in base.get("parent_map", {}).items():
|
|
283
301
|
for parent_id in parents:
|
|
284
|
-
graph.
|
|
285
|
-
|
|
302
|
+
if child_id not in graph.nodes or parent_id not in graph.nodes:
|
|
303
|
+
continue
|
|
304
|
+
|
|
305
|
+
graph.create_edge(parent_id, child_id, "base")
|
|
306
|
+
for child_id, parents in current.get("parent_map", {}).items():
|
|
286
307
|
for parent_id in parents:
|
|
287
|
-
graph.
|
|
308
|
+
if child_id not in graph.nodes or parent_id not in graph.nodes:
|
|
309
|
+
continue
|
|
310
|
+
|
|
311
|
+
graph.create_edge(parent_id, child_id, "current")
|
|
288
312
|
|
|
289
313
|
return graph
|
|
290
314
|
|
|
291
315
|
|
|
292
316
|
def _build_node_schema(lineage, node_id):
|
|
293
|
-
return lineage.get(
|
|
317
|
+
return lineage.get("nodes", {}).get(node_id, {}).get("columns", {})
|
|
294
318
|
|
|
295
319
|
|
|
296
320
|
def _get_node_row_count_diff(node_id, node_name):
|
|
297
321
|
row_count_runs = RunDAO().list(type_filter=RunType.ROW_COUNT_DIFF)
|
|
298
322
|
for run in row_count_runs:
|
|
299
|
-
|
|
323
|
+
node_ids = (run.params or {}).get("node_ids") or []
|
|
324
|
+
if node_id in node_ids:
|
|
300
325
|
result = run.result.get(node_name, {})
|
|
301
|
-
diff = TaskResultDiffer.diff(result.get(
|
|
326
|
+
diff = TaskResultDiffer.diff(result.get("base"), result.get("curr"))
|
|
302
327
|
return diff, result
|
|
303
|
-
elif run.params.get(
|
|
328
|
+
elif run.params.get("node_id") == node_id:
|
|
304
329
|
result = run.result.get(node_name, {})
|
|
305
|
-
diff = TaskResultDiffer.diff(result.get(
|
|
330
|
+
diff = TaskResultDiffer.diff(result.get("base"), result.get("curr"))
|
|
306
331
|
return diff, result
|
|
307
332
|
return None, None
|
|
308
333
|
|
|
309
334
|
|
|
310
335
|
def _generate_mismatched_nodes_summary(check: CheckSummary, limit: int = 3) -> str:
|
|
311
336
|
if not check.related_nodes:
|
|
312
|
-
return
|
|
337
|
+
return "N/A"
|
|
313
338
|
|
|
314
339
|
nodes = check.related_nodes
|
|
315
340
|
if check.changed_nodes:
|
|
@@ -318,39 +343,43 @@ def _generate_mismatched_nodes_summary(check: CheckSummary, limit: int = 3) -> s
|
|
|
318
343
|
nodes = check.changed_nodes
|
|
319
344
|
|
|
320
345
|
if len(nodes) <= limit:
|
|
321
|
-
return
|
|
346
|
+
return ", ".join(nodes)
|
|
322
347
|
|
|
323
|
-
display_nodes = nodes[:limit - 1]
|
|
324
|
-
return
|
|
348
|
+
display_nodes = nodes[: limit - 1]
|
|
349
|
+
return ", ".join(display_nodes) + f", and {len(nodes) - len(display_nodes)} more nodes"
|
|
325
350
|
|
|
326
351
|
|
|
327
352
|
def generate_summary_metadata(base_lineage, curr_lineage):
|
|
328
353
|
from py_markdown_table.markdown_table import markdown_table
|
|
329
354
|
|
|
330
|
-
base_manifest = base_lineage.get(
|
|
331
|
-
base_catalog = base_lineage.get(
|
|
332
|
-
curr_manifest = curr_lineage.get(
|
|
333
|
-
curr_catalog = curr_lineage.get(
|
|
355
|
+
base_manifest = base_lineage.get("manifest_metadata")
|
|
356
|
+
base_catalog = base_lineage.get("catalog_metadata")
|
|
357
|
+
curr_manifest = curr_lineage.get("manifest_metadata")
|
|
358
|
+
curr_catalog = curr_lineage.get("catalog_metadata")
|
|
334
359
|
|
|
335
360
|
metadata = [
|
|
336
361
|
{
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
362
|
+
"": "Base",
|
|
363
|
+
"Manifest": base_manifest.generated_at.strftime("%Y-%m-%d %H:%M:%S"),
|
|
364
|
+
"Catalog": base_catalog.generated_at.strftime("%Y-%m-%d %H:%M:%S") if base_catalog else "N/A",
|
|
340
365
|
},
|
|
341
366
|
{
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
}
|
|
367
|
+
"": "Current",
|
|
368
|
+
"Manifest": curr_manifest.generated_at.strftime("%Y-%m-%d %H:%M:%S"),
|
|
369
|
+
"Catalog": curr_catalog.generated_at.strftime("%Y-%m-%d %H:%M:%S") if curr_catalog else "N/A",
|
|
370
|
+
},
|
|
346
371
|
]
|
|
347
372
|
|
|
348
|
-
return
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
373
|
+
return (
|
|
374
|
+
markdown_table(metadata)
|
|
375
|
+
.set_params(
|
|
376
|
+
quote=False,
|
|
377
|
+
row_sep="markdown",
|
|
378
|
+
padding_width=1,
|
|
379
|
+
padding_weight="right", # Aligns the cell's contents to the beginning of the cell
|
|
380
|
+
)
|
|
381
|
+
.get_markdown()
|
|
382
|
+
)
|
|
354
383
|
|
|
355
384
|
|
|
356
385
|
def generate_check_summary(base_lineage, curr_lineage) -> (List[CheckSummary], Dict[str, int]):
|
|
@@ -375,9 +404,19 @@ def generate_check_summary(base_lineage, curr_lineage) -> (List[CheckSummary], D
|
|
|
375
404
|
continue
|
|
376
405
|
elif check.type == RunType.SCHEMA_DIFF:
|
|
377
406
|
differ = SchemaDiffResultDiffer(check, base_lineage, curr_lineage)
|
|
378
|
-
elif (
|
|
379
|
-
|
|
380
|
-
|
|
407
|
+
elif (
|
|
408
|
+
check.type
|
|
409
|
+
in [
|
|
410
|
+
RunType.ROW_COUNT_DIFF,
|
|
411
|
+
RunType.QUERY_DIFF,
|
|
412
|
+
RunType.VALUE_DIFF,
|
|
413
|
+
RunType.VALUE_DIFF_DETAIL,
|
|
414
|
+
RunType.PROFILE_DIFF,
|
|
415
|
+
RunType.TOP_K_DIFF,
|
|
416
|
+
RunType.HISTOGRAM_DIFF,
|
|
417
|
+
]
|
|
418
|
+
and run is not None
|
|
419
|
+
):
|
|
381
420
|
# Check the result is changed or not
|
|
382
421
|
differ = differ_factory(run)
|
|
383
422
|
|
|
@@ -390,19 +429,19 @@ def generate_check_summary(base_lineage, curr_lineage) -> (List[CheckSummary], D
|
|
|
390
429
|
description=check.description,
|
|
391
430
|
changes=differ.changes,
|
|
392
431
|
node_ids=differ.related_node_ids,
|
|
393
|
-
changed_nodes=differ.changed_nodes
|
|
432
|
+
changed_nodes=differ.changed_nodes,
|
|
394
433
|
)
|
|
395
434
|
)
|
|
396
435
|
|
|
397
436
|
return checks_summary, {
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
437
|
+
"total": len(checks),
|
|
438
|
+
"mismatch": len(checks_summary),
|
|
439
|
+
"failed": failed_checks_count,
|
|
401
440
|
}
|
|
402
441
|
|
|
403
442
|
|
|
404
443
|
def generate_mermaid_lineage_graph(graph: LineageGraph):
|
|
405
|
-
content = up_to_level_content =
|
|
444
|
+
content = up_to_level_content = "graph LR\n"
|
|
406
445
|
is_not_modified = False
|
|
407
446
|
# Only show the modified nodes and there children
|
|
408
447
|
queue = list(graph.modified_set)
|
|
@@ -426,7 +465,7 @@ def generate_mermaid_lineage_graph(graph: LineageGraph):
|
|
|
426
465
|
content += node.get_node_str(graph.checks)
|
|
427
466
|
for child_id in node.children:
|
|
428
467
|
queue.append(child_id)
|
|
429
|
-
edge_id = f
|
|
468
|
+
edge_id = f"{node_id}-->{child_id}"
|
|
430
469
|
if edge_id not in display_edge:
|
|
431
470
|
display_edge.add(edge_id)
|
|
432
471
|
content += graph.get_edge_str(edge_id)
|
|
@@ -439,90 +478,98 @@ def generate_mermaid_lineage_graph(graph: LineageGraph):
|
|
|
439
478
|
return up_to_level_content, is_not_modified, len(content) > MAX_MERMAID_TEXT_SIZE
|
|
440
479
|
|
|
441
480
|
|
|
442
|
-
def generate_markdown_summary(ctx: RecceContext, summary_format: str =
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
graph =
|
|
447
|
-
graph.checks, check_statistics = generate_check_summary(base_lineage, curr_lineage)
|
|
481
|
+
def generate_markdown_summary(ctx: RecceContext, summary_format: str = "markdown"):
|
|
482
|
+
lineage_diff = ctx.get_lineage_diff()
|
|
483
|
+
summary_metadata = generate_summary_metadata(lineage_diff.base, lineage_diff.current)
|
|
484
|
+
graph = _build_lineage_graph(lineage_diff.base, lineage_diff.current)
|
|
485
|
+
graph.checks, check_statistics = generate_check_summary(lineage_diff.base, lineage_diff.current)
|
|
448
486
|
mermaid_content, is_empty_graph, is_partial_graph = generate_mermaid_lineage_graph(graph)
|
|
449
487
|
check_content = generate_check_content(graph, check_statistics)
|
|
450
488
|
|
|
451
|
-
if summary_format ==
|
|
489
|
+
if summary_format == "mermaid":
|
|
452
490
|
return mermaid_content
|
|
453
|
-
elif summary_format ==
|
|
491
|
+
elif summary_format == "check":
|
|
454
492
|
return check_content
|
|
455
|
-
elif summary_format ==
|
|
493
|
+
elif summary_format == "markdown":
|
|
456
494
|
|
|
457
|
-
content =
|
|
458
|
-
content += f
|
|
495
|
+
content = "# Recce Summary\n"
|
|
496
|
+
content += f"## Manifest Information\n{summary_metadata}\n"
|
|
459
497
|
|
|
460
498
|
if is_empty_graph is False:
|
|
461
|
-
content += f
|
|
499
|
+
content += f"""
|
|
462
500
|
## Lineage Graph
|
|
463
501
|
{"_Too many nodes to generate! Please see the full lineage graph on Recce instance._" if is_partial_graph else ''}
|
|
464
502
|
```mermaid
|
|
465
503
|
{mermaid_content}
|
|
466
504
|
```
|
|
467
|
-
|
|
505
|
+
"""
|
|
468
506
|
else:
|
|
469
|
-
content +=
|
|
507
|
+
content += """
|
|
470
508
|
## Lineage Graph
|
|
471
509
|
No changed module was detected.
|
|
472
|
-
|
|
510
|
+
"""
|
|
473
511
|
if check_content:
|
|
474
512
|
content += check_content
|
|
475
513
|
|
|
476
|
-
if ctx.state_loader.cloud_mode:
|
|
514
|
+
if ctx.state_loader.cloud_mode and ctx.state_loader.pr_info is not None:
|
|
477
515
|
pr_info = ctx.state_loader.pr_info
|
|
478
|
-
|
|
516
|
+
if pr_info.repository is not None and pr_info.id is not None:
|
|
517
|
+
# the classic route will be deprecated soon
|
|
518
|
+
content += f"\nSee PR page: {RECCE_CLOUD_HOST}/classic/{pr_info.repository}/pulls/{pr_info.id}\n"
|
|
479
519
|
|
|
480
520
|
return content
|
|
481
521
|
|
|
482
522
|
|
|
483
523
|
def generate_check_content(graph, check_statistics):
|
|
484
524
|
from py_markdown_table.markdown_table import markdown_table
|
|
485
|
-
|
|
525
|
+
|
|
526
|
+
content = ""
|
|
486
527
|
check_content = None
|
|
487
528
|
# Generate the check summary if we found any changes
|
|
488
529
|
if len(graph.checks) > 0:
|
|
489
530
|
data = []
|
|
490
531
|
for check in graph.checks:
|
|
491
|
-
data.append(
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
532
|
+
data.append(
|
|
533
|
+
{
|
|
534
|
+
"Name": check.name,
|
|
535
|
+
"Type": str(check.type).replace("_", " ").title(),
|
|
536
|
+
"Mismatched Nodes": _generate_mismatched_nodes_summary(check),
|
|
537
|
+
# Temporarily remove the type of changes, until we implement a better way to display it.
|
|
538
|
+
# 'Type of Changes': _formate_changes(check.changes)
|
|
539
|
+
}
|
|
540
|
+
)
|
|
541
|
+
check_content = (
|
|
542
|
+
markdown_table(data)
|
|
543
|
+
.set_params(
|
|
544
|
+
quote=False,
|
|
545
|
+
row_sep="markdown",
|
|
546
|
+
padding_width=1,
|
|
547
|
+
padding_weight="right", # Aligns the cell's contents to the beginning of the cell
|
|
548
|
+
)
|
|
549
|
+
.get_markdown()
|
|
550
|
+
)
|
|
504
551
|
|
|
505
|
-
if check_statistics.get(
|
|
506
|
-
warning_message =
|
|
552
|
+
if check_statistics.get("total", 0) > 0:
|
|
553
|
+
warning_message = ""
|
|
507
554
|
statistics = {
|
|
508
|
-
|
|
509
|
-
|
|
555
|
+
"Checks Run": check_statistics.get("total", 0),
|
|
556
|
+
"Data Mismatch Detected": check_statistics.get("mismatch", 0),
|
|
510
557
|
}
|
|
511
|
-
if check_statistics.get(
|
|
512
|
-
statistics[
|
|
513
|
-
warning_message =
|
|
558
|
+
if check_statistics.get("failed", 0) > 0:
|
|
559
|
+
statistics["Incomplete Checks"] = check_statistics.get("failed", 0)
|
|
560
|
+
warning_message = """
|
|
514
561
|
:warning: **Incomplete Checks** refers to checks that did not successfully run due to configuration or SQL errors.
|
|
515
562
|
Please check the output of `recce run` for more information
|
|
516
|
-
|
|
517
|
-
check_summary = markdown_table([statistics]).set_params(quote=False, row_sep=
|
|
518
|
-
content += f
|
|
563
|
+
"""
|
|
564
|
+
check_summary = markdown_table([statistics]).set_params(quote=False, row_sep="markdown").get_markdown()
|
|
565
|
+
content += f"""
|
|
519
566
|
## Checks Summary
|
|
520
567
|
{check_summary}
|
|
521
568
|
{warning_message}
|
|
522
|
-
|
|
569
|
+
"""
|
|
523
570
|
if check_content:
|
|
524
|
-
content += f
|
|
571
|
+
content += f"""
|
|
525
572
|
### Checks of Data Mismatch Detected
|
|
526
573
|
{check_content}
|
|
527
|
-
|
|
574
|
+
"""
|
|
528
575
|
return content
|
recce/tasks/__init__.py
CHANGED
|
@@ -1,7 +1,23 @@
|
|
|
1
1
|
from .core import Task
|
|
2
2
|
from .histogram import HistogramDiffTask
|
|
3
3
|
from .profile import ProfileDiffTask, ProfileTask
|
|
4
|
-
from .query import
|
|
5
|
-
from .rowcount import
|
|
4
|
+
from .query import QueryBaseTask, QueryDiffTask, QueryTask
|
|
5
|
+
from .rowcount import RowCountDiffTask, RowCountTask
|
|
6
6
|
from .top_k import TopKDiffTask
|
|
7
|
-
from .valuediff import
|
|
7
|
+
from .valuediff import ValueDiffDetailTask, ValueDiffTask
|
|
8
|
+
|
|
9
|
+
# Explicitly declare exports
|
|
10
|
+
__all__ = [
|
|
11
|
+
"Task",
|
|
12
|
+
"HistogramDiffTask",
|
|
13
|
+
"ProfileDiffTask",
|
|
14
|
+
"ProfileTask",
|
|
15
|
+
"QueryBaseTask",
|
|
16
|
+
"QueryDiffTask",
|
|
17
|
+
"QueryTask",
|
|
18
|
+
"RowCountDiffTask",
|
|
19
|
+
"RowCountTask",
|
|
20
|
+
"TopKDiffTask",
|
|
21
|
+
"ValueDiffDetailTask",
|
|
22
|
+
"ValueDiffTask",
|
|
23
|
+
]
|