tablevalidator-databricks 0.1.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.
- tablevalidator_databricks/__init__.py +15 -0
- tablevalidator_databricks/cli/__init__.py +0 -0
- tablevalidator_databricks/cli/main.py +99 -0
- tablevalidator_databricks/generator/__init__.py +0 -0
- tablevalidator_databricks/generator/generator.py +39 -0
- tablevalidator_databricks/templates/widget_notebook.py.tmpl +178 -0
- tablevalidator_databricks-0.1.0.dist-info/METADATA +97 -0
- tablevalidator_databricks-0.1.0.dist-info/RECORD +11 -0
- tablevalidator_databricks-0.1.0.dist-info/WHEEL +5 -0
- tablevalidator_databricks-0.1.0.dist-info/entry_points.txt +2 -0
- tablevalidator_databricks-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""tablevalidator_databricks: generates a widget-driven Databricks notebook
|
|
2
|
+
UI on top of the table-validator package's validate_tables() API.
|
|
3
|
+
|
|
4
|
+
This package contains no validation logic of its own - it only writes a
|
|
5
|
+
notebook file whose cells call into table_validator's existing engine
|
|
6
|
+
(see tablevalidator_databricks/templates/widget_notebook.py.tmpl)."""
|
|
7
|
+
|
|
8
|
+
from importlib.metadata import PackageNotFoundError, version
|
|
9
|
+
|
|
10
|
+
try:
|
|
11
|
+
__version__ = version("tablevalidator-databricks")
|
|
12
|
+
except PackageNotFoundError:
|
|
13
|
+
__version__ = "0.0.0"
|
|
14
|
+
|
|
15
|
+
__all__ = ["__version__"]
|
|
File without changes
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"""CLI entry point: `tablevalidator-databricks` console script."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
import typer
|
|
6
|
+
|
|
7
|
+
from tablevalidator_databricks.generator.generator import VALID_MODES, generate_notebook
|
|
8
|
+
|
|
9
|
+
app = typer.Typer(
|
|
10
|
+
name="tablevalidator-databricks",
|
|
11
|
+
help="Generate a widget-driven Databricks notebook UI for table-validator.",
|
|
12
|
+
no_args_is_help=True,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
DEFAULT_OUTPUT_PATH = Path("TableValidator.py")
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@app.command()
|
|
19
|
+
def init(
|
|
20
|
+
output: Path = typer.Option(
|
|
21
|
+
DEFAULT_OUTPUT_PATH,
|
|
22
|
+
"--output",
|
|
23
|
+
help="Path to write the generated notebook to (default: ./TableValidator.py).",
|
|
24
|
+
),
|
|
25
|
+
mode: str = typer.Option(
|
|
26
|
+
"full",
|
|
27
|
+
"--mode",
|
|
28
|
+
help=(
|
|
29
|
+
"basic: only the check-group multiselect and table pickers. "
|
|
30
|
+
"full (default): also shows optional filter widgets (only/ "
|
|
31
|
+
"ignore columns, row filter, primary key). "
|
|
32
|
+
"schema: pre-selects only the Catalog & Schema check group."
|
|
33
|
+
),
|
|
34
|
+
),
|
|
35
|
+
) -> None:
|
|
36
|
+
"""Generate a Databricks notebook with dbutils widgets for source/
|
|
37
|
+
target catalog.schema.table selection and validation checks - fill in
|
|
38
|
+
the widgets and run the notebook, no code to write.
|
|
39
|
+
|
|
40
|
+
Import the generated file into a Databricks workspace via
|
|
41
|
+
Workspace -> Import, with format set to "Source" (or "File" -> upload
|
|
42
|
+
directly, depending on your workspace UI version) - Databricks
|
|
43
|
+
recognizes the "# Databricks notebook source" header and cell
|
|
44
|
+
(# COMMAND ----------) markers and opens it as a real notebook, not a
|
|
45
|
+
plain text file.
|
|
46
|
+
"""
|
|
47
|
+
if mode not in VALID_MODES:
|
|
48
|
+
typer.secho(
|
|
49
|
+
f"Invalid --mode {mode!r} - must be one of {', '.join(VALID_MODES)}.",
|
|
50
|
+
fg=typer.colors.RED,
|
|
51
|
+
)
|
|
52
|
+
raise typer.Exit(code=1)
|
|
53
|
+
|
|
54
|
+
generate_notebook(output, mode)
|
|
55
|
+
|
|
56
|
+
typer.echo(f"Generated notebook: {output.resolve()}")
|
|
57
|
+
typer.echo(
|
|
58
|
+
"Import it into Databricks (Workspace -> Import, format 'Source'), "
|
|
59
|
+
"fill in the widgets, and Run All."
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
@app.command()
|
|
64
|
+
def info() -> None:
|
|
65
|
+
"""Show what this tool does and how to use the generated notebook."""
|
|
66
|
+
typer.echo(
|
|
67
|
+
"\n"
|
|
68
|
+
"tablevalidator-databricks - Databricks notebook UI for table-validator\n"
|
|
69
|
+
"------------------------------------------------------------------------\n"
|
|
70
|
+
"Generates a Databricks notebook with dbutils widgets (dynamic "
|
|
71
|
+
"catalog/schema/table dropdowns sourced from Unity Catalog, plus a "
|
|
72
|
+
"validation-checks multiselect) as a wrapper around the "
|
|
73
|
+
"table-validator package's validate_tables() API - no Python code "
|
|
74
|
+
"to write, no separate credentials to configure (the notebook "
|
|
75
|
+
"reuses its own ambient Spark session, same as validate_tables() "
|
|
76
|
+
"itself).\n"
|
|
77
|
+
"\n"
|
|
78
|
+
"Workflow:\n"
|
|
79
|
+
"\n"
|
|
80
|
+
" 1. tablevalidator-databricks init\n"
|
|
81
|
+
" Writes a ready-to-use notebook (./TableValidator.py by "
|
|
82
|
+
"default). Use --output to write elsewhere, --mode to pick basic/"
|
|
83
|
+
"full/schema (see 'tablevalidator-databricks init --help').\n"
|
|
84
|
+
"\n"
|
|
85
|
+
" 2. Import the generated file into Databricks\n"
|
|
86
|
+
" Workspace -> Import, format 'Source'.\n"
|
|
87
|
+
"\n"
|
|
88
|
+
" 3. Fill in the widgets and Run All\n"
|
|
89
|
+
" Pick Source/Target Catalog, Schema, Table, and which checks "
|
|
90
|
+
"to run, then run the notebook top to bottom. Re-run the "
|
|
91
|
+
"'Create widgets' cell after changing a Catalog/Schema selection, "
|
|
92
|
+
"to repopulate the dropdowns below it (a Databricks widget "
|
|
93
|
+
"limitation, not a bug).\n"
|
|
94
|
+
"\n"
|
|
95
|
+
"This package contains no validation logic of its own - every "
|
|
96
|
+
"generated notebook calls directly into table-validator's own "
|
|
97
|
+
"validate_tables() engine, so results are identical to calling it "
|
|
98
|
+
"yourself in a code cell.\n"
|
|
99
|
+
)
|
|
File without changes
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Renders the bundled widget_notebook.py.tmpl into a real Databricks
|
|
3
|
+
notebook file. No Jinja2 - the template has exactly two placeholders,
|
|
4
|
+
substituted via plain str.replace(), so a templating dependency isn't
|
|
5
|
+
worth adding for this.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import importlib.resources
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
|
|
13
|
+
VALID_MODES = ("basic", "full", "schema")
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def generate_notebook(output: Path, mode: str) -> None:
|
|
17
|
+
"""Write a widget-driven Databricks notebook to `output`.
|
|
18
|
+
|
|
19
|
+
mode controls which optional widgets/pre-selected checks the notebook
|
|
20
|
+
starts with - see tablevalidator_databricks/cli/main.py's `init`
|
|
21
|
+
command docstring for what each mode means. The underlying comparison
|
|
22
|
+
logic is identical regardless of mode; only the widgets shown differ,
|
|
23
|
+
so all three modes render from the same template file rather than
|
|
24
|
+
three separate ones.
|
|
25
|
+
"""
|
|
26
|
+
if mode not in VALID_MODES:
|
|
27
|
+
raise ValueError(f"mode must be one of {VALID_MODES}, got {mode!r}")
|
|
28
|
+
|
|
29
|
+
template = (
|
|
30
|
+
importlib.resources.files("tablevalidator_databricks.templates")
|
|
31
|
+
.joinpath("widget_notebook.py.tmpl")
|
|
32
|
+
.read_text(encoding="utf-8")
|
|
33
|
+
)
|
|
34
|
+
rendered = template.replace("{{MODE}}", mode).replace(
|
|
35
|
+
"{{SHOW_OPTIONAL_WIDGETS}}", "True" if mode == "full" else "False",
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
output.parent.mkdir(parents=True, exist_ok=True)
|
|
39
|
+
output.write_text(rendered, encoding="utf-8")
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
# Databricks notebook source
|
|
2
|
+
# MAGIC %md
|
|
3
|
+
# MAGIC # Table Validator
|
|
4
|
+
# MAGIC Generated by `tablevalidator-databricks init --mode {{MODE}}`.
|
|
5
|
+
# MAGIC
|
|
6
|
+
# MAGIC Fill in the widgets above (Source/Target Catalog, Schema, Table, and
|
|
7
|
+
# MAGIC which checks to run), then **Run All**. No code needs to be written or
|
|
8
|
+
# MAGIC edited - every cell below just reads the widgets and calls the
|
|
9
|
+
# MAGIC `table_validator` package's `validate_tables()` API.
|
|
10
|
+
# MAGIC
|
|
11
|
+
# MAGIC **Cascading dropdowns**: Databricks widgets don't auto-refresh when an
|
|
12
|
+
# MAGIC upstream selection changes. After picking a different Source/Target
|
|
13
|
+
# MAGIC Catalog, re-run the "Create widgets" cell below to repopulate the Schema
|
|
14
|
+
# MAGIC dropdown for that catalog; likewise after changing Schema, re-run it
|
|
15
|
+
# MAGIC again to repopulate Table. This is a real Databricks widget limitation,
|
|
16
|
+
# MAGIC not a bug in this notebook.
|
|
17
|
+
|
|
18
|
+
# COMMAND ----------
|
|
19
|
+
|
|
20
|
+
# MAGIC %pip install table-validator
|
|
21
|
+
|
|
22
|
+
# COMMAND ----------
|
|
23
|
+
|
|
24
|
+
dbutils.library.restartPython()
|
|
25
|
+
|
|
26
|
+
# COMMAND ----------
|
|
27
|
+
|
|
28
|
+
from table_validator import validate_tables
|
|
29
|
+
from table_validator.config.schema import ValidationType
|
|
30
|
+
|
|
31
|
+
MODE = "{{MODE}}"
|
|
32
|
+
SHOW_OPTIONAL_WIDGETS = {{SHOW_OPTIONAL_WIDGETS}}
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _list_catalogs():
|
|
36
|
+
return sorted(r["catalog"] for r in spark.sql("SHOW CATALOGS").collect())
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _list_schemas(catalog: str):
|
|
40
|
+
if not catalog:
|
|
41
|
+
return []
|
|
42
|
+
rows = spark.sql(f"SHOW SCHEMAS IN `{catalog}`").collect()
|
|
43
|
+
return sorted(r["databaseName"] for r in rows)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _list_tables(catalog: str, schema: str):
|
|
47
|
+
if not catalog or not schema:
|
|
48
|
+
return []
|
|
49
|
+
rows = spark.sql(f"SHOW TABLES IN `{catalog}`.`{schema}`").collect()
|
|
50
|
+
return sorted(r["tableName"] for r in rows)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _current_or_first(options, current):
|
|
54
|
+
"""Keep a widget's existing selection across a re-run of this cell if
|
|
55
|
+
it's still valid for the (possibly new) upstream selection, otherwise
|
|
56
|
+
fall back to the first available option (or "" if there are none)."""
|
|
57
|
+
if current in options:
|
|
58
|
+
return current
|
|
59
|
+
return options[0] if options else ""
|
|
60
|
+
|
|
61
|
+
# COMMAND ----------
|
|
62
|
+
|
|
63
|
+
# MAGIC %md ### Create widgets
|
|
64
|
+
# MAGIC Re-run this cell after changing Source/Target Catalog or Schema, to
|
|
65
|
+
# MAGIC repopulate the dropdowns below them.
|
|
66
|
+
|
|
67
|
+
# COMMAND ----------
|
|
68
|
+
|
|
69
|
+
def _get_widget(name: str, default: str = "") -> str:
|
|
70
|
+
try:
|
|
71
|
+
return dbutils.widgets.get(name)
|
|
72
|
+
except Exception:
|
|
73
|
+
return default
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
catalogs = _list_catalogs()
|
|
77
|
+
|
|
78
|
+
source_catalog_current = _current_or_first(catalogs, _get_widget("source_catalog"))
|
|
79
|
+
target_catalog_current = _current_or_first(catalogs, _get_widget("target_catalog"))
|
|
80
|
+
|
|
81
|
+
source_schemas = _list_schemas(source_catalog_current)
|
|
82
|
+
target_schemas = _list_schemas(target_catalog_current)
|
|
83
|
+
|
|
84
|
+
source_schema_current = _current_or_first(source_schemas, _get_widget("source_schema"))
|
|
85
|
+
target_schema_current = _current_or_first(target_schemas, _get_widget("target_schema"))
|
|
86
|
+
|
|
87
|
+
source_tables = _list_tables(source_catalog_current, source_schema_current)
|
|
88
|
+
target_tables = _list_tables(target_catalog_current, target_schema_current)
|
|
89
|
+
|
|
90
|
+
source_table_current = _current_or_first(source_tables, _get_widget("source_table"))
|
|
91
|
+
target_table_current = _current_or_first(target_tables, _get_widget("target_table"))
|
|
92
|
+
|
|
93
|
+
dbutils.widgets.dropdown("source_catalog", source_catalog_current, catalogs or [""], "Source Catalog")
|
|
94
|
+
dbutils.widgets.dropdown("source_schema", source_schema_current, source_schemas or [""], "Source Schema")
|
|
95
|
+
dbutils.widgets.dropdown("source_table", source_table_current, source_tables or [""], "Source Table")
|
|
96
|
+
|
|
97
|
+
dbutils.widgets.dropdown("target_catalog", target_catalog_current, catalogs or [""], "Target Catalog")
|
|
98
|
+
dbutils.widgets.dropdown("target_schema", target_schema_current, target_schemas or [""], "Target Schema")
|
|
99
|
+
dbutils.widgets.dropdown("target_table", target_table_current, target_tables or [""], "Target Table")
|
|
100
|
+
|
|
101
|
+
print(
|
|
102
|
+
"Widgets created. If you just changed Source/Target Catalog or Schema, "
|
|
103
|
+
"re-run this cell once more to repopulate the dropdowns below it."
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
# COMMAND ----------
|
|
107
|
+
|
|
108
|
+
# MAGIC %md ### Validation checks
|
|
109
|
+
|
|
110
|
+
# COMMAND ----------
|
|
111
|
+
|
|
112
|
+
_default_checks = ["Catalog & Schema", "Column", "Row"] if MODE != "schema" else ["Catalog & Schema"]
|
|
113
|
+
dbutils.widgets.multiselect(
|
|
114
|
+
"checks", ",".join(_default_checks),
|
|
115
|
+
["Catalog & Schema", "Column", "Row"], "Validation Checks",
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
if SHOW_OPTIONAL_WIDGETS:
|
|
119
|
+
dbutils.widgets.text("only_columns", "", "Only compare these columns (comma-separated, optional)")
|
|
120
|
+
dbutils.widgets.text("ignore_columns", "", "Skip these columns entirely (comma-separated, optional)")
|
|
121
|
+
dbutils.widgets.text("row_filter", "", "Row filter - SQL WHERE-fragment (optional)")
|
|
122
|
+
dbutils.widgets.text("primary_key", "", "Primary key column(s) (comma-separated, optional)")
|
|
123
|
+
|
|
124
|
+
# COMMAND ----------
|
|
125
|
+
|
|
126
|
+
# MAGIC %md ### Run validation
|
|
127
|
+
|
|
128
|
+
# COMMAND ----------
|
|
129
|
+
|
|
130
|
+
source = f"{dbutils.widgets.get('source_catalog')}.{dbutils.widgets.get('source_schema')}.{dbutils.widgets.get('source_table')}"
|
|
131
|
+
target = f"{dbutils.widgets.get('target_catalog')}.{dbutils.widgets.get('target_schema')}.{dbutils.widgets.get('target_table')}"
|
|
132
|
+
|
|
133
|
+
_CHECK_MAP = {
|
|
134
|
+
"Catalog & Schema": {ValidationType.CATALOG, ValidationType.SCHEMA},
|
|
135
|
+
"Column": {ValidationType.COLUMN},
|
|
136
|
+
"Row": {ValidationType.ROW},
|
|
137
|
+
}
|
|
138
|
+
selected_checks = [c for c in dbutils.widgets.get("checks").split(",") if c]
|
|
139
|
+
enabled_validations = set()
|
|
140
|
+
for check in selected_checks:
|
|
141
|
+
enabled_validations |= _CHECK_MAP.get(check, set())
|
|
142
|
+
if not enabled_validations:
|
|
143
|
+
enabled_validations = None # fall back to validate_tables()'s own "run everything" default
|
|
144
|
+
|
|
145
|
+
kwargs = {"enabled_validations": enabled_validations}
|
|
146
|
+
|
|
147
|
+
if SHOW_OPTIONAL_WIDGETS:
|
|
148
|
+
only_columns = [c.strip() for c in dbutils.widgets.get("only_columns").split(",") if c.strip()]
|
|
149
|
+
ignore_columns = [c.strip() for c in dbutils.widgets.get("ignore_columns").split(",") if c.strip()]
|
|
150
|
+
row_filter = dbutils.widgets.get("row_filter").strip()
|
|
151
|
+
primary_key = [c.strip() for c in dbutils.widgets.get("primary_key").split(",") if c.strip()]
|
|
152
|
+
|
|
153
|
+
if only_columns:
|
|
154
|
+
kwargs["only_columns"] = only_columns
|
|
155
|
+
if ignore_columns:
|
|
156
|
+
kwargs["ignore_columns"] = ignore_columns
|
|
157
|
+
if row_filter:
|
|
158
|
+
kwargs["row_filter"] = row_filter
|
|
159
|
+
if primary_key:
|
|
160
|
+
kwargs["primary_key"] = primary_key
|
|
161
|
+
|
|
162
|
+
result = validate_tables(source, target, **kwargs)
|
|
163
|
+
|
|
164
|
+
# COMMAND ----------
|
|
165
|
+
|
|
166
|
+
# MAGIC %md ### Results
|
|
167
|
+
|
|
168
|
+
# COMMAND ----------
|
|
169
|
+
|
|
170
|
+
print(result)
|
|
171
|
+
display(result.table_validation.to_dataframe())
|
|
172
|
+
|
|
173
|
+
# Other sheets, mirroring the Excel report's own tabs - uncomment as needed:
|
|
174
|
+
# display(result.column_validation.to_dataframe())
|
|
175
|
+
# display(result.data_mismatches.to_dataframe())
|
|
176
|
+
# display(result.row_hash_mismatches.to_dataframe())
|
|
177
|
+
# display(result.mismatch_categories.to_dataframe())
|
|
178
|
+
# display(result.suggestions.to_dataframe())
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: tablevalidator-databricks
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Generates a widget-driven Databricks notebook UI on top of the table-validator package - no code required to run a comparison.
|
|
5
|
+
License: MIT
|
|
6
|
+
Requires-Python: >=3.9
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
Requires-Dist: typer>=0.12
|
|
9
|
+
Requires-Dist: table-validator>=0.1.19
|
|
10
|
+
Provides-Extra: dev
|
|
11
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
12
|
+
Requires-Dist: ruff>=0.4; extra == "dev"
|
|
13
|
+
Requires-Dist: black>=24.0; extra == "dev"
|
|
14
|
+
|
|
15
|
+
# tablevalidator-databricks
|
|
16
|
+
|
|
17
|
+
A widget-driven Databricks notebook UI for [`table-validator`](https://pypi.org/project/table-validator/)
|
|
18
|
+
- generates a notebook with `dbutils.widgets` for picking source/target
|
|
19
|
+
tables and validation checks, so you can run a comparison without writing
|
|
20
|
+
any Python.
|
|
21
|
+
|
|
22
|
+
This package contains **no validation logic of its own**. It only writes a
|
|
23
|
+
notebook file whose cells call directly into `table-validator`'s own
|
|
24
|
+
`validate_tables()` API - the exact same engine the CLI (`tablevalidator`)
|
|
25
|
+
and the notebook-native Python API already use.
|
|
26
|
+
|
|
27
|
+
## Install
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
pip install tablevalidator-databricks
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Installing this package also installs `table-validator` (a dependency), so
|
|
34
|
+
there's nothing else to install separately.
|
|
35
|
+
|
|
36
|
+
## Usage
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
tablevalidator-databricks init
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Writes `./TableValidator.py` - a ready-to-use Databricks notebook. Options:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
tablevalidator-databricks init --output MyValidation.py --mode full
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
- `--mode basic` - only the check-group multiselect and table pickers.
|
|
49
|
+
- `--mode full` (default) - also shows optional filter widgets (only/
|
|
50
|
+
ignore columns, row filter, primary key).
|
|
51
|
+
- `--mode schema` - pre-selects only the "Catalog & Schema" check group,
|
|
52
|
+
for a lightweight schema-shape-only comparison.
|
|
53
|
+
|
|
54
|
+
Then, in your Databricks workspace: **Workspace -> Import**, format
|
|
55
|
+
"Source", and select the generated file. Databricks recognizes the
|
|
56
|
+
`# Databricks notebook source` header and `# COMMAND ----------` cell
|
|
57
|
+
markers and opens it as a real notebook.
|
|
58
|
+
|
|
59
|
+
## What the generated notebook does
|
|
60
|
+
|
|
61
|
+
1. Installs `table-validator` (`%pip install table-validator`).
|
|
62
|
+
2. Queries Unity Catalog (`SHOW CATALOGS` / `SHOW SCHEMAS IN ...` /
|
|
63
|
+
`SHOW TABLES IN ...` via the notebook's own ambient Spark session) to
|
|
64
|
+
populate Source/Target Catalog, Schema, and Table dropdowns.
|
|
65
|
+
3. Shows a "Validation Checks" multiselect (Catalog & Schema / Column /
|
|
66
|
+
Row - the three independently-selectable check groups the engine
|
|
67
|
+
actually supports) plus, in `full` mode, optional text widgets for
|
|
68
|
+
column filtering, a row filter, and a primary key.
|
|
69
|
+
4. On run, reads every widget and calls
|
|
70
|
+
`validate_tables(source, target, enabled_validations=..., ...)`,
|
|
71
|
+
then prints a summary and displays the Table Validation sheet.
|
|
72
|
+
|
|
73
|
+
**Cascading dropdowns**: Databricks widgets don't auto-refresh when an
|
|
74
|
+
upstream selection changes. After changing Source/Target Catalog or
|
|
75
|
+
Schema, re-run the notebook's "Create widgets" cell once to repopulate the
|
|
76
|
+
dropdowns below it before running the rest of the notebook. This is a
|
|
77
|
+
Databricks widget limitation, not a bug in the generated notebook.
|
|
78
|
+
|
|
79
|
+
## Why a separate package
|
|
80
|
+
|
|
81
|
+
`table-validator` stays focused on the actual comparison engine (CLI +
|
|
82
|
+
notebook API + report generation). This package is purely a Databricks-
|
|
83
|
+
specific UI layer on top of it - keeping the two independently versioned
|
|
84
|
+
and installable means CLI-only or code-only users never need Databricks-
|
|
85
|
+
specific tooling pulled in, and this package can add more Databricks UX
|
|
86
|
+
(e.g. richer widgets) without touching the core engine at all.
|
|
87
|
+
|
|
88
|
+
## Development
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
pip install -e ".[dev]"
|
|
92
|
+
pytest
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## License
|
|
96
|
+
|
|
97
|
+
MIT - see the `table-validator` repository's [LICENSE](../table_validator/LICENSE).
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
tablevalidator_databricks/__init__.py,sha256=qhtaaSY_pHgTu5JxvXVoSAMfVC1R2I5OcPcyzLQISQo,560
|
|
2
|
+
tablevalidator_databricks/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
|
+
tablevalidator_databricks/cli/main.py,sha256=p4ex05jnieabbg1ANuXOCKKJd7-zlbs-PUz5fXD7KAU,3897
|
|
4
|
+
tablevalidator_databricks/generator/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
5
|
+
tablevalidator_databricks/generator/generator.py,sha256=Gn9ziozDmYvK7TnctnVlqu2_Oe8R4DsdN9NQYJcAzMg,1418
|
|
6
|
+
tablevalidator_databricks/templates/widget_notebook.py.tmpl,sha256=k_jGdIZN6tqHPsk1tOLK-E7Qv-_kY-YGA4c90L29m-U,6759
|
|
7
|
+
tablevalidator_databricks-0.1.0.dist-info/METADATA,sha256=2eVYYB4qHAUpFquB2sQyHMFLB7Z-MhTWjuRlsLZwmzU,3713
|
|
8
|
+
tablevalidator_databricks-0.1.0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
|
|
9
|
+
tablevalidator_databricks-0.1.0.dist-info/entry_points.txt,sha256=PzigbELsTjFQ9wJIge3nI3sBQWLsptchG-jyovFFreI,85
|
|
10
|
+
tablevalidator_databricks-0.1.0.dist-info/top_level.txt,sha256=LgxXkFPKeoTSwNXHPQLzWd4x4qDNL86KDxqbCCIb-b4,26
|
|
11
|
+
tablevalidator_databricks-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
tablevalidator_databricks
|