checkup 0.2.9__tar.gz → 0.3.0__tar.gz
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.
- checkup-0.3.0/PKG-INFO +46 -0
- checkup-0.3.0/README.md +27 -0
- {checkup-0.2.9 → checkup-0.3.0}/pyproject.toml +15 -2
- {checkup-0.2.9 → checkup-0.3.0}/src/checkup/__init__.py +6 -5
- checkup-0.3.0/src/checkup/cli/__init__.py +18 -0
- checkup-0.3.0/src/checkup/cli/commands/__init__.py +15 -0
- checkup-0.3.0/src/checkup/cli/commands/config.py +23 -0
- checkup-0.3.0/src/checkup/cli/commands/init.py +23 -0
- checkup-0.3.0/src/checkup/cli/commands/run.py +58 -0
- checkup-0.3.0/src/checkup/cli/commands/schema.py +29 -0
- checkup-0.3.0/src/checkup/cli/config_wizard/__init__.py +8 -0
- checkup-0.3.0/src/checkup/cli/config_wizard/_common.py +133 -0
- checkup-0.3.0/src/checkup/cli/config_wizard/create.py +134 -0
- checkup-0.3.0/src/checkup/cli/config_wizard/edit.py +200 -0
- checkup-0.3.0/src/checkup/cli/executor.py +148 -0
- checkup-0.3.0/src/checkup/cli/utils.py +75 -0
- checkup-0.3.0/src/checkup/configuration/__init__.py +18 -0
- checkup-0.3.0/src/checkup/configuration/env.py +149 -0
- checkup-0.3.0/src/checkup/configuration/io.py +213 -0
- checkup-0.3.0/src/checkup/configuration/models.py +41 -0
- checkup-0.3.0/src/checkup/configuration/schema.py +211 -0
- {checkup-0.2.9 → checkup-0.3.0}/src/checkup/executor.py +151 -150
- {checkup-0.2.9 → checkup-0.3.0}/src/checkup/hub.py +35 -49
- {checkup-0.2.9 → checkup-0.3.0}/src/checkup/materializers/__init__.py +5 -5
- checkup-0.3.0/src/checkup/materializers/base.py +108 -0
- checkup-0.3.0/src/checkup/materializers/console.py +92 -0
- checkup-0.3.0/src/checkup/materializers/csv_file.py +37 -0
- {checkup-0.2.9 → checkup-0.3.0}/src/checkup/materializers/database.py +17 -19
- {checkup-0.2.9 → checkup-0.3.0}/src/checkup/materializers/html_report.py +10 -8
- checkup-0.3.0/src/checkup/metric.py +118 -0
- checkup-0.3.0/src/checkup/registry/__init__.py +13 -0
- checkup-0.3.0/src/checkup/registry/discovery.py +190 -0
- {checkup-0.2.9 → checkup-0.3.0}/src/checkup/templates/metrics_report.html +22 -22
- checkup-0.2.9/PKG-INFO +0 -23
- checkup-0.2.9/README.md +0 -9
- checkup-0.2.9/src/checkup/materializers/base.py +0 -104
- checkup-0.2.9/src/checkup/materializers/console.py +0 -49
- checkup-0.2.9/src/checkup/materializers/csv_file.py +0 -35
- checkup-0.2.9/src/checkup/metric.py +0 -74
- {checkup-0.2.9 → checkup-0.3.0}/src/checkup/config.py +0 -0
- {checkup-0.2.9 → checkup-0.3.0}/src/checkup/errors.py +0 -0
- {checkup-0.2.9 → checkup-0.3.0}/src/checkup/graph.py +0 -0
- {checkup-0.2.9 → checkup-0.3.0}/src/checkup/provider.py +0 -0
- {checkup-0.2.9 → checkup-0.3.0}/src/checkup/providers/__init__.py +0 -0
- {checkup-0.2.9 → checkup-0.3.0}/src/checkup/providers/tags.py +0 -0
- {checkup-0.2.9 → checkup-0.3.0}/src/checkup/types.py +0 -0
- {checkup-0.2.9 → checkup-0.3.0}/src/checkup/utils.py +0 -0
- {checkup-0.2.9 → checkup-0.3.0}/src/checkup/validators.py +0 -0
checkup-0.3.0/PKG-INFO
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: checkup
|
|
3
|
+
Version: 0.3.0
|
|
4
|
+
Summary: Computational governance framework for measuring data product health
|
|
5
|
+
Author: Jan Vanbuel
|
|
6
|
+
Author-email: Jan Vanbuel <jan.vanbuel@ond.vlaanderen.be>
|
|
7
|
+
Classifier: Programming Language :: Python :: 3
|
|
8
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
10
|
+
Requires-Dist: jinja2>=3.1.6
|
|
11
|
+
Requires-Dist: pydantic>=2.11.7
|
|
12
|
+
Requires-Dist: pyyaml>=6.0
|
|
13
|
+
Requires-Dist: questionary>=2.0
|
|
14
|
+
Requires-Dist: rich>=13.0
|
|
15
|
+
Requires-Dist: sqlalchemy>=2.0
|
|
16
|
+
Requires-Dist: typer>=0.24
|
|
17
|
+
Requires-Python: >=3.12
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
|
|
20
|
+
# CheckUp
|
|
21
|
+
|
|
22
|
+
[](https://pypi.org/project/checkup/)
|
|
23
|
+
[](https://pypi.org/project/checkup/)
|
|
24
|
+
|
|
25
|
+
Computational governance framework for measuring data product health.
|
|
26
|
+
|
|
27
|
+
## Plugins
|
|
28
|
+
|
|
29
|
+
Checkup uses plugins to provide metrics for different tools and platforms.
|
|
30
|
+
|
|
31
|
+
| Plugin | Version | Status | Description |
|
|
32
|
+
|--------|---------|--------|-------------|
|
|
33
|
+
| [checkup-dbt](https://pypi.org/project/checkup-dbt/) | [](https://pypi.org/project/checkup-dbt/) | Usable | dbt metrics |
|
|
34
|
+
| [checkup-git](https://pypi.org/project/checkup-git/) | [](https://pypi.org/project/checkup-git/) | Usable | Git repository metrics |
|
|
35
|
+
| [checkup-python](https://pypi.org/project/checkup-python/) | [](https://pypi.org/project/checkup-python/) | Early | Python project metrics |
|
|
36
|
+
| [checkup-conveyor](https://pypi.org/project/checkup-conveyor/) | [](https://pypi.org/project/checkup-conveyor/) | Early | [Conveyor](https://conveyor.dataminded.com/) API metrics |
|
|
37
|
+
| [checkup-airflow](https://pypi.org/project/checkup-airflow/) | [](https://pypi.org/project/checkup-airflow/) | Planned | Airflow metrics |
|
|
38
|
+
| [checkup-bitbucket](https://pypi.org/project/checkup-bitbucket/) | [](https://pypi.org/project/checkup-bitbucket/) | Planned | Bitbucket metrics |
|
|
39
|
+
| [checkup-github](https://pypi.org/project/checkup-github/) | [](https://pypi.org/project/checkup-github/) | Planned | GitHub metrics |
|
|
40
|
+
| [checkup-gitlab](https://pypi.org/project/checkup-gitlab/) | [](https://pypi.org/project/checkup-gitlab/) | Planned | GitLab metrics |
|
|
41
|
+
|
|
42
|
+
## Key Concepts
|
|
43
|
+
|
|
44
|
+
- **Metrics** - Calculate values from context
|
|
45
|
+
- **Providers** - Functions that enrich context (shared across metrics)
|
|
46
|
+
- **Materializers** - Output formats (Console, HTML, CSV, etc.)
|
checkup-0.3.0/README.md
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# CheckUp
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/checkup/)
|
|
4
|
+
[](https://pypi.org/project/checkup/)
|
|
5
|
+
|
|
6
|
+
Computational governance framework for measuring data product health.
|
|
7
|
+
|
|
8
|
+
## Plugins
|
|
9
|
+
|
|
10
|
+
Checkup uses plugins to provide metrics for different tools and platforms.
|
|
11
|
+
|
|
12
|
+
| Plugin | Version | Status | Description |
|
|
13
|
+
|--------|---------|--------|-------------|
|
|
14
|
+
| [checkup-dbt](https://pypi.org/project/checkup-dbt/) | [](https://pypi.org/project/checkup-dbt/) | Usable | dbt metrics |
|
|
15
|
+
| [checkup-git](https://pypi.org/project/checkup-git/) | [](https://pypi.org/project/checkup-git/) | Usable | Git repository metrics |
|
|
16
|
+
| [checkup-python](https://pypi.org/project/checkup-python/) | [](https://pypi.org/project/checkup-python/) | Early | Python project metrics |
|
|
17
|
+
| [checkup-conveyor](https://pypi.org/project/checkup-conveyor/) | [](https://pypi.org/project/checkup-conveyor/) | Early | [Conveyor](https://conveyor.dataminded.com/) API metrics |
|
|
18
|
+
| [checkup-airflow](https://pypi.org/project/checkup-airflow/) | [](https://pypi.org/project/checkup-airflow/) | Planned | Airflow metrics |
|
|
19
|
+
| [checkup-bitbucket](https://pypi.org/project/checkup-bitbucket/) | [](https://pypi.org/project/checkup-bitbucket/) | Planned | Bitbucket metrics |
|
|
20
|
+
| [checkup-github](https://pypi.org/project/checkup-github/) | [](https://pypi.org/project/checkup-github/) | Planned | GitHub metrics |
|
|
21
|
+
| [checkup-gitlab](https://pypi.org/project/checkup-gitlab/) | [](https://pypi.org/project/checkup-gitlab/) | Planned | GitLab metrics |
|
|
22
|
+
|
|
23
|
+
## Key Concepts
|
|
24
|
+
|
|
25
|
+
- **Metrics** - Calculate values from context
|
|
26
|
+
- **Providers** - Functions that enrich context (shared across metrics)
|
|
27
|
+
- **Materializers** - Output formats (Console, HTML, CSV, etc.)
|
|
@@ -1,21 +1,34 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "checkup"
|
|
3
|
-
version = "0.
|
|
4
|
-
description = "
|
|
3
|
+
version = "0.3.0"
|
|
4
|
+
description = "Computational governance framework for measuring data product health"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
authors = [{ name = "Jan Vanbuel", email = "jan.vanbuel@ond.vlaanderen.be" }]
|
|
7
7
|
requires-python = ">=3.12"
|
|
8
|
+
classifiers = [
|
|
9
|
+
"Programming Language :: Python :: 3",
|
|
10
|
+
"Programming Language :: Python :: 3.12",
|
|
11
|
+
"Programming Language :: Python :: 3.13",
|
|
12
|
+
]
|
|
8
13
|
dependencies = [
|
|
9
14
|
"jinja2>=3.1.6",
|
|
10
15
|
"pydantic>=2.11.7",
|
|
11
16
|
"pyyaml>=6.0",
|
|
17
|
+
"questionary>=2.0",
|
|
12
18
|
"rich>=13.0",
|
|
13
19
|
"sqlalchemy>=2.0",
|
|
20
|
+
"typer>=0.24",
|
|
14
21
|
]
|
|
15
22
|
|
|
16
23
|
[project.scripts]
|
|
17
24
|
checkup = "checkup:main"
|
|
18
25
|
|
|
26
|
+
[project.entry-points."checkup.materializers"]
|
|
27
|
+
console = "checkup.materializers:ConsoleMaterializer"
|
|
28
|
+
csv = "checkup.materializers:CSVMaterializer"
|
|
29
|
+
html = "checkup.materializers:HTMLMaterializer"
|
|
30
|
+
sqlalchemy = "checkup.materializers:SQLAlchemyMaterializer"
|
|
31
|
+
|
|
19
32
|
[build-system]
|
|
20
33
|
requires = ["uv_build>=0.8.13,<0.9.0"]
|
|
21
34
|
build-backend = "uv_build"
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""Checkup -
|
|
1
|
+
"""Checkup - Computational governance framework for measuring data product health."""
|
|
2
2
|
|
|
3
3
|
from checkup.errors import (
|
|
4
4
|
DuplicateMetricNameError,
|
|
@@ -14,7 +14,7 @@ from checkup.materializers import (
|
|
|
14
14
|
Materializer,
|
|
15
15
|
SQLAlchemyMaterializer,
|
|
16
16
|
)
|
|
17
|
-
from checkup.metric import ExecutorType, Metric
|
|
17
|
+
from checkup.metric import ExecutorType, Measurement, Metric
|
|
18
18
|
from checkup.provider import Provider
|
|
19
19
|
from checkup.providers.tags import TagProvider
|
|
20
20
|
from checkup.types import Context
|
|
@@ -25,6 +25,7 @@ __all__ = [
|
|
|
25
25
|
"CheckHub",
|
|
26
26
|
"MeasurementResult",
|
|
27
27
|
"Metric",
|
|
28
|
+
"Measurement",
|
|
28
29
|
"ExecutorType",
|
|
29
30
|
"Provider",
|
|
30
31
|
"TagProvider",
|
|
@@ -48,6 +49,6 @@ __all__ = [
|
|
|
48
49
|
|
|
49
50
|
|
|
50
51
|
def main() -> None:
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
52
|
+
from checkup.cli import app
|
|
53
|
+
|
|
54
|
+
app()
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Checkup CLI application.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import typer
|
|
6
|
+
|
|
7
|
+
from checkup.cli.commands import config, init, run, schema
|
|
8
|
+
|
|
9
|
+
app = typer.Typer(
|
|
10
|
+
name="checkup",
|
|
11
|
+
help="CheckUp - Computational governance framework for measuring data product health",
|
|
12
|
+
no_args_is_help=True,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
app.command()(run)
|
|
16
|
+
app.command()(init)
|
|
17
|
+
app.command()(config)
|
|
18
|
+
app.command()(schema)
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""
|
|
2
|
+
CLI commands.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from checkup.cli.commands.config import config
|
|
6
|
+
from checkup.cli.commands.init import init
|
|
7
|
+
from checkup.cli.commands.run import run
|
|
8
|
+
from checkup.cli.commands.schema import schema
|
|
9
|
+
|
|
10
|
+
__all__ = [
|
|
11
|
+
"config",
|
|
12
|
+
"init",
|
|
13
|
+
"run",
|
|
14
|
+
"schema",
|
|
15
|
+
]
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Config command. Modify an existing config file.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Annotated
|
|
7
|
+
|
|
8
|
+
import typer
|
|
9
|
+
|
|
10
|
+
from checkup.cli.config_wizard import edit_config
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def config(
|
|
14
|
+
config_path: Annotated[
|
|
15
|
+
Path | None,
|
|
16
|
+
typer.Option("--config", "-c", help="Path to config file"),
|
|
17
|
+
] = None,
|
|
18
|
+
) -> None:
|
|
19
|
+
"""
|
|
20
|
+
Modify the checkup.yaml config file.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
edit_config(config_path=config_path)
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Init command. Create a new config file.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Annotated
|
|
7
|
+
|
|
8
|
+
import typer
|
|
9
|
+
|
|
10
|
+
from checkup.cli.config_wizard import create_config
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def init(
|
|
14
|
+
output: Annotated[
|
|
15
|
+
Path | None,
|
|
16
|
+
typer.Option("--output", "-o", help="Output path for config file"),
|
|
17
|
+
] = None,
|
|
18
|
+
) -> None:
|
|
19
|
+
"""
|
|
20
|
+
Create a checkup.yaml config file.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
create_config(output_path=output)
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Run command. Run metrics and materialize results.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Annotated
|
|
8
|
+
|
|
9
|
+
import typer
|
|
10
|
+
|
|
11
|
+
from checkup.cli.executor import execute_checkup
|
|
12
|
+
from checkup.cli.utils import apply_cli_overrides
|
|
13
|
+
from checkup.configuration import load_config
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def run(
|
|
17
|
+
config: Annotated[
|
|
18
|
+
Path | None,
|
|
19
|
+
typer.Option("--config", "-c", help="Path to config file"),
|
|
20
|
+
] = None,
|
|
21
|
+
tag: Annotated[
|
|
22
|
+
list[str] | None,
|
|
23
|
+
typer.Option("--tag", "-t", help="Set tags (key=value)"),
|
|
24
|
+
] = None,
|
|
25
|
+
provider: Annotated[
|
|
26
|
+
list[str] | None,
|
|
27
|
+
typer.Option("--provider", "-p", help="Set providers (name or name:key=value)"),
|
|
28
|
+
] = None,
|
|
29
|
+
metric: Annotated[
|
|
30
|
+
list[str] | None,
|
|
31
|
+
typer.Option("--metric", "-m", help="Set metrics (name or name:key=value)"),
|
|
32
|
+
] = None,
|
|
33
|
+
materializer: Annotated[
|
|
34
|
+
str | None,
|
|
35
|
+
typer.Option(
|
|
36
|
+
"--materializer", help="Set materializer (type or type:key=value)"
|
|
37
|
+
),
|
|
38
|
+
] = None,
|
|
39
|
+
dry_run: Annotated[
|
|
40
|
+
bool,
|
|
41
|
+
typer.Option("--dry-run", help="Don't materialize, just print"),
|
|
42
|
+
] = False,
|
|
43
|
+
verbose: Annotated[
|
|
44
|
+
bool,
|
|
45
|
+
typer.Option("--verbose", "-v", help="Verbose output"),
|
|
46
|
+
] = False,
|
|
47
|
+
) -> None:
|
|
48
|
+
"""
|
|
49
|
+
Run metrics and materialize results.
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
if verbose:
|
|
53
|
+
logging.basicConfig(level=logging.DEBUG)
|
|
54
|
+
|
|
55
|
+
cfg = load_config(config_path=config)
|
|
56
|
+
cfg = apply_cli_overrides(cfg, tag, provider, metric)
|
|
57
|
+
|
|
58
|
+
execute_checkup(cfg, materializer="console" if dry_run else materializer)
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Schema command. Generate JSON schema for checkup.yaml.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Annotated
|
|
7
|
+
|
|
8
|
+
import typer
|
|
9
|
+
from rich.console import Console
|
|
10
|
+
|
|
11
|
+
from checkup.configuration.io import SCHEMA_FILENAME
|
|
12
|
+
from checkup.configuration.schema import write_schema
|
|
13
|
+
|
|
14
|
+
console = Console()
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def schema(
|
|
18
|
+
output: Annotated[
|
|
19
|
+
Path | None,
|
|
20
|
+
typer.Option("--output", "-o", help="Output path for schema file"),
|
|
21
|
+
] = None,
|
|
22
|
+
) -> None:
|
|
23
|
+
"""
|
|
24
|
+
Generate JSON schema for checkup.yaml.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
path = output or Path.cwd() / SCHEMA_FILENAME
|
|
28
|
+
write_schema(path)
|
|
29
|
+
console.print(f"[green]Schema written to {path}[/green]")
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Shared utilities for the config wizard.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import TYPE_CHECKING
|
|
7
|
+
|
|
8
|
+
import yaml
|
|
9
|
+
from rich.console import Console
|
|
10
|
+
|
|
11
|
+
from checkup.configuration.io import SCHEMA_FILENAME
|
|
12
|
+
from checkup.configuration.models import CheckupConfig
|
|
13
|
+
from checkup.configuration.schema import write_schema
|
|
14
|
+
|
|
15
|
+
if TYPE_CHECKING:
|
|
16
|
+
import questionary
|
|
17
|
+
|
|
18
|
+
from checkup.registry import PluginRegistry
|
|
19
|
+
|
|
20
|
+
console = Console(markup=False, highlight=False)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def get_questionary() -> "questionary":
|
|
24
|
+
"""
|
|
25
|
+
Lazy import questionary to avoid slow startup.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
import questionary
|
|
29
|
+
|
|
30
|
+
return questionary
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def select_multiple(
|
|
34
|
+
available: list[str],
|
|
35
|
+
selected: list[str],
|
|
36
|
+
item_name: str,
|
|
37
|
+
) -> list[str]:
|
|
38
|
+
"""
|
|
39
|
+
Select multiple items with fuzzy search.
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
if not available:
|
|
43
|
+
console.print(
|
|
44
|
+
f"[yellow]No {item_name} found. Install checkup plugins.[/yellow]",
|
|
45
|
+
markup=True,
|
|
46
|
+
)
|
|
47
|
+
return []
|
|
48
|
+
|
|
49
|
+
selected_count = len([s for s in selected if s in available])
|
|
50
|
+
console.print(f"Currently selected: {selected_count}/{len(available)} {item_name}")
|
|
51
|
+
|
|
52
|
+
choices = [
|
|
53
|
+
get_questionary().Choice(name, checked=name in selected)
|
|
54
|
+
for name in sorted(available)
|
|
55
|
+
]
|
|
56
|
+
|
|
57
|
+
result = (
|
|
58
|
+
get_questionary()
|
|
59
|
+
.checkbox(
|
|
60
|
+
f"Select {item_name}:",
|
|
61
|
+
choices=choices,
|
|
62
|
+
use_search_filter=True,
|
|
63
|
+
use_jk_keys=False,
|
|
64
|
+
instruction="(↑↓ navigate, space toggle, type to filter, enter confirm)",
|
|
65
|
+
)
|
|
66
|
+
.ask()
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
return result or []
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def select_materializer(current: str | None, registry: "PluginRegistry") -> str | None:
|
|
73
|
+
"""
|
|
74
|
+
Select materializer interactively.
|
|
75
|
+
"""
|
|
76
|
+
|
|
77
|
+
available = registry.list_materializer_names()
|
|
78
|
+
|
|
79
|
+
if not available:
|
|
80
|
+
return "console"
|
|
81
|
+
|
|
82
|
+
default = (
|
|
83
|
+
current if current in available else (available[0] if available else "console")
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
return (
|
|
87
|
+
get_questionary()
|
|
88
|
+
.select(
|
|
89
|
+
"Materializer type:",
|
|
90
|
+
choices=available,
|
|
91
|
+
default=default,
|
|
92
|
+
use_search_filter=True,
|
|
93
|
+
use_jk_keys=False,
|
|
94
|
+
)
|
|
95
|
+
.ask()
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def write_config(path: Path, config: CheckupConfig) -> None:
|
|
100
|
+
"""
|
|
101
|
+
Write config to file with empty lines between sections.
|
|
102
|
+
Also generates the JSON schema file.
|
|
103
|
+
"""
|
|
104
|
+
|
|
105
|
+
console.print(f"\n[bold]Writing config to {path}[/bold]", markup=True)
|
|
106
|
+
|
|
107
|
+
data = config.model_dump(exclude_defaults=True)
|
|
108
|
+
|
|
109
|
+
lines = [f"# yaml-language-server: $schema={SCHEMA_FILENAME}"]
|
|
110
|
+
|
|
111
|
+
# Write sections separately with blank lines between them
|
|
112
|
+
for key in CheckupConfig.model_fields:
|
|
113
|
+
if key not in data or not data[key]:
|
|
114
|
+
continue
|
|
115
|
+
lines.append("")
|
|
116
|
+
lines.append(
|
|
117
|
+
yaml.dump(
|
|
118
|
+
{key: data[key]}, default_flow_style=False, sort_keys=False
|
|
119
|
+
).rstrip()
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
with open(path, "w") as f:
|
|
123
|
+
f.write("\n".join(lines))
|
|
124
|
+
f.write("\n")
|
|
125
|
+
|
|
126
|
+
schema_path = path.parent / SCHEMA_FILENAME
|
|
127
|
+
write_schema(schema_path)
|
|
128
|
+
console.print(f"[green]Schema written to {schema_path}[/green]", markup=True)
|
|
129
|
+
|
|
130
|
+
console.print("[green]Done![/green]", markup=True)
|
|
131
|
+
console.print(
|
|
132
|
+
"Run [bold]checkup run[/bold] to test your configuration.", markup=True
|
|
133
|
+
)
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Interactive config creation.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import TYPE_CHECKING
|
|
7
|
+
|
|
8
|
+
from checkup.configuration.io import CONFIG_FILENAME
|
|
9
|
+
from checkup.configuration.models import (
|
|
10
|
+
CheckupConfig,
|
|
11
|
+
MaterializerConfig,
|
|
12
|
+
MetricConfig,
|
|
13
|
+
ProviderConfig,
|
|
14
|
+
)
|
|
15
|
+
from checkup.registry import get_registry
|
|
16
|
+
|
|
17
|
+
from ._common import (
|
|
18
|
+
console,
|
|
19
|
+
get_questionary,
|
|
20
|
+
select_materializer,
|
|
21
|
+
select_multiple,
|
|
22
|
+
write_config,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
if TYPE_CHECKING:
|
|
26
|
+
from checkup.registry import PluginRegistry
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def create_config(output_path: Path | None = None) -> None:
|
|
30
|
+
"""
|
|
31
|
+
Interactively create a new config file.
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
path = output_path or Path.cwd() / CONFIG_FILENAME
|
|
35
|
+
|
|
36
|
+
if not _confirm_overwrite(path):
|
|
37
|
+
return
|
|
38
|
+
|
|
39
|
+
registry = get_registry()
|
|
40
|
+
config = _build_config(registry)
|
|
41
|
+
|
|
42
|
+
if config is None:
|
|
43
|
+
console.print("\n[yellow]Cancelled.[/yellow]", markup=True)
|
|
44
|
+
return
|
|
45
|
+
|
|
46
|
+
write_config(path, config)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _confirm_overwrite(path: Path) -> bool:
|
|
50
|
+
if not path.exists():
|
|
51
|
+
return True
|
|
52
|
+
|
|
53
|
+
overwrite = (
|
|
54
|
+
get_questionary().confirm(f"{path} exists. Overwrite?", default=False).ask()
|
|
55
|
+
)
|
|
56
|
+
if overwrite is None:
|
|
57
|
+
console.print("\n[yellow]Cancelled.[/yellow]", markup=True)
|
|
58
|
+
return False
|
|
59
|
+
return overwrite
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _build_config(registry: "PluginRegistry") -> CheckupConfig | None:
|
|
63
|
+
tags = _prompt_tags()
|
|
64
|
+
if tags is None:
|
|
65
|
+
return None
|
|
66
|
+
|
|
67
|
+
provider_names = _prompt_providers(registry)
|
|
68
|
+
if provider_names is None:
|
|
69
|
+
return None
|
|
70
|
+
|
|
71
|
+
metric_names = _prompt_metrics(registry, provider_names)
|
|
72
|
+
if metric_names is None:
|
|
73
|
+
return None
|
|
74
|
+
|
|
75
|
+
mat = _prompt_materializer(registry)
|
|
76
|
+
if mat is None:
|
|
77
|
+
return None
|
|
78
|
+
|
|
79
|
+
return CheckupConfig(
|
|
80
|
+
tags=tags,
|
|
81
|
+
providers=[ProviderConfig(name=p) for p in provider_names],
|
|
82
|
+
metrics=[MetricConfig(name=m) for m in metric_names],
|
|
83
|
+
materializer=MaterializerConfig(type=mat) if mat != "console" else None,
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def _prompt_tags() -> dict[str, str] | None:
|
|
88
|
+
console.print("\n[bold]Tags[/bold]", markup=True)
|
|
89
|
+
console.print(
|
|
90
|
+
"Tags identify your data product (e.g., product=my-product, team=analytics)"
|
|
91
|
+
)
|
|
92
|
+
return _collect_tags()
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def _prompt_providers(registry: "PluginRegistry") -> list[str] | None:
|
|
96
|
+
console.print("\n[bold]Providers[/bold]", markup=True)
|
|
97
|
+
return select_multiple(registry.list_provider_names(), [], "providers")
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def _prompt_metrics(
|
|
101
|
+
registry: "PluginRegistry", provider_names: list[str]
|
|
102
|
+
) -> list[str] | None:
|
|
103
|
+
console.print("\n[bold]Metrics[/bold]", markup=True)
|
|
104
|
+
with console.status("Loading metrics..."):
|
|
105
|
+
available_metrics = registry.list_compatible_metric_names(provider_names)
|
|
106
|
+
return select_multiple(available_metrics, [], "metrics")
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def _prompt_materializer(registry: "PluginRegistry") -> str | None:
|
|
110
|
+
console.print("\n[bold]Materializer[/bold]", markup=True)
|
|
111
|
+
return select_materializer(None, registry)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def _collect_tags() -> dict[str, str] | None:
|
|
115
|
+
tags: dict[str, str] = {}
|
|
116
|
+
|
|
117
|
+
while True:
|
|
118
|
+
tag: str = (
|
|
119
|
+
get_questionary()
|
|
120
|
+
.text(
|
|
121
|
+
"Add tag (key=value, or empty to finish):",
|
|
122
|
+
)
|
|
123
|
+
.ask()
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
if tag is None:
|
|
127
|
+
return None
|
|
128
|
+
if not tag:
|
|
129
|
+
break
|
|
130
|
+
if "=" in tag:
|
|
131
|
+
key, value = tag.split("=", 1)
|
|
132
|
+
tags[key.strip()] = value.strip()
|
|
133
|
+
|
|
134
|
+
return tags
|