checkup 0.5.2__tar.gz → 0.6.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.
Files changed (49) hide show
  1. {checkup-0.5.2 → checkup-0.6.0}/PKG-INFO +10 -5
  2. {checkup-0.5.2 → checkup-0.6.0}/README.md +9 -4
  3. {checkup-0.5.2 → checkup-0.6.0}/pyproject.toml +2 -1
  4. {checkup-0.5.2 → checkup-0.6.0}/src/checkup/cli/commands/run.py +7 -2
  5. {checkup-0.5.2 → checkup-0.6.0}/src/checkup/cli/executor.py +20 -15
  6. {checkup-0.5.2 → checkup-0.6.0}/src/checkup/materializers/__init__.py +2 -0
  7. checkup-0.6.0/src/checkup/materializers/markdown.py +52 -0
  8. {checkup-0.5.2 → checkup-0.6.0}/src/checkup/__init__.py +0 -0
  9. {checkup-0.5.2 → checkup-0.6.0}/src/checkup/cli/__init__.py +0 -0
  10. {checkup-0.5.2 → checkup-0.6.0}/src/checkup/cli/commands/__init__.py +0 -0
  11. {checkup-0.5.2 → checkup-0.6.0}/src/checkup/cli/commands/config.py +0 -0
  12. {checkup-0.5.2 → checkup-0.6.0}/src/checkup/cli/commands/init.py +0 -0
  13. {checkup-0.5.2 → checkup-0.6.0}/src/checkup/cli/commands/plugins.py +0 -0
  14. {checkup-0.5.2 → checkup-0.6.0}/src/checkup/cli/commands/schema.py +0 -0
  15. {checkup-0.5.2 → checkup-0.6.0}/src/checkup/cli/config_wizard/__init__.py +0 -0
  16. {checkup-0.5.2 → checkup-0.6.0}/src/checkup/cli/config_wizard/_common.py +0 -0
  17. {checkup-0.5.2 → checkup-0.6.0}/src/checkup/cli/config_wizard/create.py +0 -0
  18. {checkup-0.5.2 → checkup-0.6.0}/src/checkup/cli/config_wizard/edit.py +0 -0
  19. {checkup-0.5.2 → checkup-0.6.0}/src/checkup/cli/utils.py +0 -0
  20. {checkup-0.5.2 → checkup-0.6.0}/src/checkup/config.py +0 -0
  21. {checkup-0.5.2 → checkup-0.6.0}/src/checkup/configuration/__init__.py +0 -0
  22. {checkup-0.5.2 → checkup-0.6.0}/src/checkup/configuration/env.py +0 -0
  23. {checkup-0.5.2 → checkup-0.6.0}/src/checkup/configuration/io.py +0 -0
  24. {checkup-0.5.2 → checkup-0.6.0}/src/checkup/configuration/models.py +0 -0
  25. {checkup-0.5.2 → checkup-0.6.0}/src/checkup/configuration/schema.py +0 -0
  26. {checkup-0.5.2 → checkup-0.6.0}/src/checkup/errors.py +0 -0
  27. {checkup-0.5.2 → checkup-0.6.0}/src/checkup/executor/__init__.py +0 -0
  28. {checkup-0.5.2 → checkup-0.6.0}/src/checkup/executor/batch_executors.py +0 -0
  29. {checkup-0.5.2 → checkup-0.6.0}/src/checkup/executor/metric_calculator.py +0 -0
  30. {checkup-0.5.2 → checkup-0.6.0}/src/checkup/executor/provider_executor.py +0 -0
  31. {checkup-0.5.2 → checkup-0.6.0}/src/checkup/executor/state.py +0 -0
  32. {checkup-0.5.2 → checkup-0.6.0}/src/checkup/graph.py +0 -0
  33. {checkup-0.5.2 → checkup-0.6.0}/src/checkup/hub.py +0 -0
  34. {checkup-0.5.2 → checkup-0.6.0}/src/checkup/materializers/base.py +0 -0
  35. {checkup-0.5.2 → checkup-0.6.0}/src/checkup/materializers/console.py +0 -0
  36. {checkup-0.5.2 → checkup-0.6.0}/src/checkup/materializers/csv_file.py +0 -0
  37. {checkup-0.5.2 → checkup-0.6.0}/src/checkup/materializers/database.py +0 -0
  38. {checkup-0.5.2 → checkup-0.6.0}/src/checkup/materializers/html_report.py +0 -0
  39. {checkup-0.5.2 → checkup-0.6.0}/src/checkup/measurement.py +0 -0
  40. {checkup-0.5.2 → checkup-0.6.0}/src/checkup/metric.py +0 -0
  41. {checkup-0.5.2 → checkup-0.6.0}/src/checkup/provider.py +0 -0
  42. {checkup-0.5.2 → checkup-0.6.0}/src/checkup/providers/__init__.py +0 -0
  43. {checkup-0.5.2 → checkup-0.6.0}/src/checkup/providers/tags.py +0 -0
  44. {checkup-0.5.2 → checkup-0.6.0}/src/checkup/registry/__init__.py +0 -0
  45. {checkup-0.5.2 → checkup-0.6.0}/src/checkup/registry/discovery.py +0 -0
  46. {checkup-0.5.2 → checkup-0.6.0}/src/checkup/templates/metrics_report.html +0 -0
  47. {checkup-0.5.2 → checkup-0.6.0}/src/checkup/types.py +0 -0
  48. {checkup-0.5.2 → checkup-0.6.0}/src/checkup/utils.py +0 -0
  49. {checkup-0.5.2 → checkup-0.6.0}/src/checkup/validators.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: checkup
3
- Version: 0.5.2
3
+ Version: 0.6.0
4
4
  Summary: Computational governance framework for measuring data product health
5
5
  Author: Jan Vanbuel
6
6
  Author-email: Jan Vanbuel <jan.vanbuel@ond.vlaanderen.be>
@@ -20,12 +20,17 @@ Project-URL: Issues, https://github.com/datamindedbe/checkup/issues
20
20
  Project-URL: Source, https://github.com/datamindedbe/checkup
21
21
  Description-Content-Type: text/markdown
22
22
 
23
- # CheckUp
23
+ <p align="center">
24
+ <img src="https://raw.githubusercontent.com/datamindedbe/checkup/main/assets/banner.png" alt="CheckUp" width="100%" />
25
+ </p>
24
26
 
25
- [![PyPI version](https://img.shields.io/pypi/v/checkup)](https://pypi.org/project/checkup/)
26
- [![Python versions](https://img.shields.io/pypi/pyversions/checkup)](https://pypi.org/project/checkup/)
27
+ <h3 align="center">Computational Governance Framework</h3>
28
+ <p align="center">for measuring <b>data product health</b></p>
27
29
 
28
- Computational governance framework for measuring data product health.
30
+ <p align="center">
31
+ <a href="https://pypi.org/project/checkup/"><img src="https://img.shields.io/pypi/v/checkup" alt="PyPI version" /></a>
32
+ <a href="https://pypi.org/project/checkup/"><img src="https://img.shields.io/pypi/pyversions/checkup" alt="Python versions" /></a>
33
+ </p>
29
34
 
30
35
  ## Plugins
31
36
 
@@ -1,9 +1,14 @@
1
- # CheckUp
1
+ <p align="center">
2
+ <img src="https://raw.githubusercontent.com/datamindedbe/checkup/main/assets/banner.png" alt="CheckUp" width="100%" />
3
+ </p>
2
4
 
3
- [![PyPI version](https://img.shields.io/pypi/v/checkup)](https://pypi.org/project/checkup/)
4
- [![Python versions](https://img.shields.io/pypi/pyversions/checkup)](https://pypi.org/project/checkup/)
5
+ <h3 align="center">Computational Governance Framework</h3>
6
+ <p align="center">for measuring <b>data product health</b></p>
5
7
 
6
- Computational governance framework for measuring data product health.
8
+ <p align="center">
9
+ <a href="https://pypi.org/project/checkup/"><img src="https://img.shields.io/pypi/v/checkup" alt="PyPI version" /></a>
10
+ <a href="https://pypi.org/project/checkup/"><img src="https://img.shields.io/pypi/pyversions/checkup" alt="Python versions" /></a>
11
+ </p>
7
12
 
8
13
  ## Plugins
9
14
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "checkup"
3
- version = "0.5.2"
3
+ version = "0.6.0"
4
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" }]
@@ -32,6 +32,7 @@ checkup = "checkup:main"
32
32
  console = "checkup.materializers:ConsoleMaterializer"
33
33
  csv = "checkup.materializers:CSVMaterializer"
34
34
  html = "checkup.materializers:HTMLMaterializer"
35
+ markdown = "checkup.materializers:MarkdownMaterializer"
35
36
  sqlalchemy = "checkup.materializers:SQLAlchemyMaterializer"
36
37
 
37
38
  [build-system]
@@ -43,14 +43,18 @@ def run(
43
43
  multiprocessing: Annotated[
44
44
  bool,
45
45
  typer.Option(
46
- "--multiprocessing",
47
- help="When disabled, run sequentially without subprocesses",
46
+ "--multiprocessing/--no-multiprocessing",
47
+ help="Use multiprocessing. Disable with --no-multiprocessing to run sequentially without subprocesses",
48
48
  ),
49
49
  ] = True,
50
50
  verbose: Annotated[
51
51
  bool,
52
52
  typer.Option("--verbose", "-v", help="Verbose output"),
53
53
  ] = False,
54
+ quiet: Annotated[
55
+ bool,
56
+ typer.Option("--quiet", "-q", help="Only print materializer output to stdout"),
57
+ ] = False,
54
58
  ) -> None:
55
59
  """
56
60
  Run metrics and materialize results.
@@ -66,4 +70,5 @@ def run(
66
70
  cfg,
67
71
  materializer="console" if dry_run else materializer,
68
72
  multiprocessing=multiprocessing,
73
+ quiet=quiet,
69
74
  )
@@ -27,6 +27,7 @@ def execute_checkup(
27
27
  config: CheckupConfig,
28
28
  materializer: str | None = None,
29
29
  multiprocessing: bool = True,
30
+ quiet: bool = False,
30
31
  ) -> None:
31
32
  """
32
33
  Execute checkup with the given configuration.
@@ -35,23 +36,26 @@ def execute_checkup(
35
36
  config: Loaded checkup configuration
36
37
  materializer: Override materializer type (e.g., "console")
37
38
  multiprocessing: If False, run sequentially without subprocesses
39
+ quiet: If True, send status/errors to stderr so stdout holds only the materializer output.
38
40
  """
39
41
 
42
+ out = Console(stderr=True) if quiet else console
43
+
40
44
  registry = get_registry()
41
45
 
42
- providers = _resolve_providers(config, registry)
46
+ providers = _resolve_providers(config, registry, out)
43
47
  if not providers:
44
- console.print("[yellow]No providers configured[/yellow]")
48
+ out.print("[yellow]No providers configured[/yellow]")
45
49
  return
46
50
 
47
- metrics = _resolve_metrics(config, registry)
51
+ metrics = _resolve_metrics(config, registry, out)
48
52
  if not metrics:
49
- console.print("[yellow]No metrics configured[/yellow]")
53
+ out.print("[yellow]No metrics configured[/yellow]")
50
54
  return
51
55
 
52
- materializer = _resolve_materializer(config, registry, materializer)
56
+ materializer = _resolve_materializer(config, registry, materializer, out)
53
57
 
54
- console.print(f"[blue]Running {len(metrics)} metrics...[/blue]")
58
+ out.print(f"[blue]Running {len(metrics)} metrics...[/blue]")
55
59
 
56
60
  result = (
57
61
  CheckHub()
@@ -62,7 +66,7 @@ def execute_checkup(
62
66
 
63
67
  if result.errors:
64
68
  for _, error in result.errors:
65
- console.print(f"[red]Error: {error}[/red]")
69
+ out.print(f"[red]Error: {error}[/red]")
66
70
 
67
71
  result.materialize(materializer)
68
72
 
@@ -70,6 +74,7 @@ def execute_checkup(
70
74
  def _resolve_providers(
71
75
  config: CheckupConfig,
72
76
  registry: "PluginRegistry",
77
+ out: Console = console,
73
78
  ) -> list["Provider"]:
74
79
  """
75
80
  Resolve provider configs to provider instances.
@@ -83,14 +88,14 @@ def _resolve_providers(
83
88
  for provider_config in config.providers:
84
89
  provider_cls = registry.get_provider(provider_config.name)
85
90
  if provider_cls is None:
86
- console.print(f"[yellow]Unknown provider: {provider_config.name}[/yellow]")
91
+ out.print(f"[yellow]Unknown provider: {provider_config.name}[/yellow]")
87
92
  continue
88
93
 
89
94
  try:
90
95
  provider = provider_cls(**provider_config.config)
91
96
  providers.append(provider)
92
97
  except Exception as e:
93
- console.print(
98
+ out.print(
94
99
  f"[red]Failed to instantiate provider {provider_config.name}: {e}[/red]"
95
100
  )
96
101
 
@@ -100,6 +105,7 @@ def _resolve_providers(
100
105
  def _resolve_metrics(
101
106
  config: CheckupConfig,
102
107
  registry: "PluginRegistry",
108
+ out: Console = console,
103
109
  ) -> list["Metric"]:
104
110
  """
105
111
  Resolve metric configs to metric instances.
@@ -110,7 +116,7 @@ def _resolve_metrics(
110
116
  for metric_config in config.metrics:
111
117
  metric_cls = registry.get_metric(metric_config.type)
112
118
  if metric_cls is None:
113
- console.print(f"[yellow]Unknown metric: {metric_config.type}[/yellow]")
119
+ out.print(f"[yellow]Unknown metric: {metric_config.type}[/yellow]")
114
120
  continue
115
121
 
116
122
  try:
@@ -121,7 +127,7 @@ def _resolve_metrics(
121
127
  )
122
128
  )
123
129
  except Exception as e:
124
- console.print(
130
+ out.print(
125
131
  f"[red]Failed to instantiate metric {metric_config.type}: {e}[/red]"
126
132
  )
127
133
 
@@ -132,6 +138,7 @@ def _resolve_materializer(
132
138
  config: CheckupConfig,
133
139
  registry: "PluginRegistry",
134
140
  override: str | None = None,
141
+ out: Console = console,
135
142
  ) -> "Materializer":
136
143
  """
137
144
  Resolve materializer config to materializer instance.
@@ -150,13 +157,11 @@ def _resolve_materializer(
150
157
  materializer_cls = registry.get_materializer(mat_type)
151
158
 
152
159
  if materializer_cls is None:
153
- console.print(
154
- f"[yellow]Unknown materializer: {mat_type}, using console[/yellow]"
155
- )
160
+ out.print(f"[yellow]Unknown materializer: {mat_type}, using console[/yellow]")
156
161
  return ConsoleMaterializer()
157
162
 
158
163
  try:
159
164
  return materializer_cls(**mat_config)
160
165
  except Exception as e:
161
- console.print(f"[red]Failed to instantiate materializer {mat_type}: {e}[/red]")
166
+ out.print(f"[red]Failed to instantiate materializer {mat_type}: {e}[/red]")
162
167
  return ConsoleMaterializer()
@@ -9,11 +9,13 @@ from checkup.materializers.console import ConsoleMaterializer
9
9
  from checkup.materializers.csv_file import CSVMaterializer
10
10
  from checkup.materializers.database import SQLAlchemyMaterializer
11
11
  from checkup.materializers.html_report import HTMLMaterializer
12
+ from checkup.materializers.markdown import MarkdownMaterializer
12
13
 
13
14
  __all__ = [
14
15
  "ConsoleMaterializer",
15
16
  "CSVMaterializer",
16
17
  "HTMLMaterializer",
18
+ "MarkdownMaterializer",
17
19
  "Materializer",
18
20
  "SQLAlchemyMaterializer",
19
21
  "group_measurements_by_tags",
@@ -0,0 +1,52 @@
1
+ """Markdown materializer for Markdown table output."""
2
+
3
+ from checkup.materializers.base import Materializer
4
+ from checkup.measurement import Measurement
5
+
6
+ COLUMNS = ("Name", "Description", "Value", "Unit", "Diagnostics")
7
+ ALIGNMENTS = ("---", "---", "---:", "---", "---") # Right-align the Value column.
8
+
9
+
10
+ class MarkdownMaterializer(Materializer):
11
+ """
12
+ Output measurements as a GitHub-flavoured Markdown table.
13
+ """
14
+
15
+ def materialize(
16
+ self, measurements: list[Measurement], direct_metric_names: set[str]
17
+ ) -> None:
18
+ """
19
+ Print measurements as a Markdown table.
20
+ """
21
+
22
+ filtered = self._filter_measurements(measurements, direct_metric_names)
23
+
24
+ rows = [self._row(COLUMNS), self._row(ALIGNMENTS)]
25
+ for measurement in filtered:
26
+ rows.append(
27
+ self._row(
28
+ (
29
+ measurement.metric.name,
30
+ measurement.metric.description,
31
+ str(measurement.value) if measurement.value is not None else "",
32
+ measurement.metric.unit,
33
+ measurement.diagnostic,
34
+ )
35
+ )
36
+ )
37
+ print("\n".join(rows))
38
+
39
+ @classmethod
40
+ def _row(cls, cells: tuple[str, ...]) -> str:
41
+ return "| " + " | ".join(cls._cell(cell) for cell in cells) + " |"
42
+
43
+ @staticmethod
44
+ def _cell(value: str) -> str:
45
+ """
46
+ Escape a value for a Markdown table cell.
47
+
48
+ Cells cannot contain a raw pipe (column separator) or newline (row separator),
49
+ so escape pipes and turn newlines into `<br>`.
50
+ """
51
+
52
+ return str(value or "").replace("|", "\\|").replace("\n", "<br>").strip()
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes