quickdbd-cli 0.1.13__tar.gz → 0.1.14__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.
- {quickdbd_cli-0.1.13 → quickdbd_cli-0.1.14}/NOTICE +2 -1
- {quickdbd_cli-0.1.13 → quickdbd_cli-0.1.14}/PKG-INFO +2 -2
- {quickdbd_cli-0.1.13 → quickdbd_cli-0.1.14}/README.md +1 -1
- {quickdbd_cli-0.1.13 → quickdbd_cli-0.1.14}/pyproject.toml +1 -1
- quickdbd_cli-0.1.14/src/quickdbd_cli/__init__.py +1 -0
- {quickdbd_cli-0.1.13 → quickdbd_cli-0.1.14}/src/quickdbd_cli/cli.py +31 -1
- {quickdbd_cli-0.1.13 → quickdbd_cli-0.1.14}/tests/test_gallery.py +5 -0
- {quickdbd_cli-0.1.13 → quickdbd_cli-0.1.14}/tests/test_pipeline.py +41 -0
- quickdbd_cli-0.1.13/src/quickdbd_cli/__init__.py +0 -1
- {quickdbd_cli-0.1.13 → quickdbd_cli-0.1.14}/.gitignore +0 -0
- {quickdbd_cli-0.1.13 → quickdbd_cli-0.1.14}/LICENSE +0 -0
- {quickdbd_cli-0.1.13 → quickdbd_cli-0.1.14}/examples/quickdbd-sample.txt +0 -0
- {quickdbd_cli-0.1.13 → quickdbd_cli-0.1.14}/examples/synthetic/finance.txt +0 -0
- {quickdbd_cli-0.1.13 → quickdbd_cli-0.1.14}/examples/synthetic/hr.txt +0 -0
- {quickdbd_cli-0.1.13 → quickdbd_cli-0.1.14}/examples/synthetic/inventory.txt +0 -0
- {quickdbd_cli-0.1.13 → quickdbd_cli-0.1.14}/examples/synthetic/marketing.txt +0 -0
- {quickdbd_cli-0.1.13 → quickdbd_cli-0.1.14}/examples/synthetic/qdbd.layout.json +0 -0
- {quickdbd_cli-0.1.13 → quickdbd_cli-0.1.14}/examples/synthetic/sales.txt +0 -0
- {quickdbd_cli-0.1.13 → quickdbd_cli-0.1.14}/scripts/build_gallery.py +0 -0
- {quickdbd_cli-0.1.13 → quickdbd_cli-0.1.14}/scripts/gen_font_metrics.py +0 -0
- {quickdbd_cli-0.1.13 → quickdbd_cli-0.1.14}/scripts/gen_schema.py +0 -0
- {quickdbd_cli-0.1.13 → quickdbd_cli-0.1.14}/scripts/gen_thumbnails.py +0 -0
- {quickdbd_cli-0.1.13 → quickdbd_cli-0.1.14}/scripts/release.sh +0 -0
- {quickdbd_cli-0.1.13 → quickdbd_cli-0.1.14}/src/quickdbd_cli/__main__.py +0 -0
- {quickdbd_cli-0.1.13 → quickdbd_cli-0.1.14}/src/quickdbd_cli/assets/Inter-Bold.ttf +0 -0
- {quickdbd_cli-0.1.13 → quickdbd_cli-0.1.14}/src/quickdbd_cli/assets/Inter-Bold.woff2 +0 -0
- {quickdbd_cli-0.1.13 → quickdbd_cli-0.1.14}/src/quickdbd_cli/assets/Inter-LICENSE.txt +0 -0
- {quickdbd_cli-0.1.13 → quickdbd_cli-0.1.14}/src/quickdbd_cli/assets/Inter-Regular.ttf +0 -0
- {quickdbd_cli-0.1.13 → quickdbd_cli-0.1.14}/src/quickdbd_cli/assets/Inter-Regular.woff2 +0 -0
- {quickdbd_cli-0.1.13 → quickdbd_cli-0.1.14}/src/quickdbd_cli/assets/editor.js +0 -0
- {quickdbd_cli-0.1.13 → quickdbd_cli-0.1.14}/src/quickdbd_cli/assets/elk-LICENSE.md +0 -0
- {quickdbd_cli-0.1.13 → quickdbd_cli-0.1.14}/src/quickdbd_cli/assets/elk.bundled.js +0 -0
- {quickdbd_cli-0.1.13 → quickdbd_cli-0.1.14}/src/quickdbd_cli/assets/font-metrics.json +0 -0
- {quickdbd_cli-0.1.13 → quickdbd_cli-0.1.14}/src/quickdbd_cli/assets/render.js +0 -0
- {quickdbd_cli-0.1.13 → quickdbd_cli-0.1.14}/src/quickdbd_cli/assets/style.css +0 -0
- {quickdbd_cli-0.1.13 → quickdbd_cli-0.1.14}/src/quickdbd_cli/assets/viewer.js +0 -0
- {quickdbd_cli-0.1.13 → quickdbd_cli-0.1.14}/src/quickdbd_cli/dbt.py +0 -0
- {quickdbd_cli-0.1.13 → quickdbd_cli-0.1.14}/src/quickdbd_cli/diff.py +0 -0
- {quickdbd_cli-0.1.13 → quickdbd_cli-0.1.14}/src/quickdbd_cli/export.py +0 -0
- {quickdbd_cli-0.1.13 → quickdbd_cli-0.1.14}/src/quickdbd_cli/html.py +0 -0
- {quickdbd_cli-0.1.13 → quickdbd_cli-0.1.14}/src/quickdbd_cli/jsrt.py +0 -0
- {quickdbd_cli-0.1.13 → quickdbd_cli-0.1.14}/src/quickdbd_cli/layout.py +0 -0
- {quickdbd_cli-0.1.13 → quickdbd_cli-0.1.14}/src/quickdbd_cli/measure.py +0 -0
- {quickdbd_cli-0.1.13 → quickdbd_cli-0.1.14}/src/quickdbd_cli/model.py +0 -0
- {quickdbd_cli-0.1.13 → quickdbd_cli-0.1.14}/src/quickdbd_cli/parser.py +0 -0
- {quickdbd_cli-0.1.13 → quickdbd_cli-0.1.14}/src/quickdbd_cli/project.py +0 -0
- {quickdbd_cli-0.1.13 → quickdbd_cli-0.1.14}/src/quickdbd_cli/serve.py +0 -0
- {quickdbd_cli-0.1.13 → quickdbd_cli-0.1.14}/src/quickdbd_cli/share.py +0 -0
- {quickdbd_cli-0.1.13 → quickdbd_cli-0.1.14}/src/quickdbd_cli/tags.py +0 -0
- {quickdbd_cli-0.1.13 → quickdbd_cli-0.1.14}/tests/fixtures/jaffle/LICENSE +0 -0
- {quickdbd_cli-0.1.13 → quickdbd_cli-0.1.14}/tests/fixtures/jaffle/README.md +0 -0
- {quickdbd_cli-0.1.13 → quickdbd_cli-0.1.14}/tests/fixtures/jaffle/catalog.json +0 -0
- {quickdbd_cli-0.1.13 → quickdbd_cli-0.1.14}/tests/fixtures/jaffle/manifest.json +0 -0
- {quickdbd_cli-0.1.13 → quickdbd_cli-0.1.14}/tests/test_dbt.py +0 -0
- {quickdbd_cli-0.1.13 → quickdbd_cli-0.1.14}/tests/test_diff.py +0 -0
- {quickdbd_cli-0.1.13 → quickdbd_cli-0.1.14}/tests/test_parser.py +0 -0
- {quickdbd_cli-0.1.13 → quickdbd_cli-0.1.14}/tests/test_path.py +0 -0
- {quickdbd_cli-0.1.13 → quickdbd_cli-0.1.14}/tests/test_select.py +0 -0
- {quickdbd_cli-0.1.13 → quickdbd_cli-0.1.14}/tests/test_serve.py +0 -0
- {quickdbd_cli-0.1.13 → quickdbd_cli-0.1.14}/tests/test_share.py +0 -0
- {quickdbd_cli-0.1.13 → quickdbd_cli-0.1.14}/tests/test_tags.py +0 -0
|
@@ -16,7 +16,8 @@ This product bundles third-party components, distributed unmodified:
|
|
|
16
16
|
|
|
17
17
|
The demo gallery (gallery/, not part of the Python package) includes schemas derived from:
|
|
18
18
|
|
|
19
|
-
* Northwind and AdventureWorks (gallery/northwind, gallery/
|
|
19
|
+
* Northwind, pubs, AdventureWorks and AdventureWorks DW (gallery/northwind, gallery/pubs,
|
|
20
|
+
gallery/adventureworks, gallery/adventureworks-dw), converted from
|
|
20
21
|
microsoft/sql-server-samples. Copyright (c) Microsoft Corporation. MIT License.
|
|
21
22
|
* Sakila (gallery/sakila), converted from sakila-schema.sql. Copyright (c) 2006, 2026, Oracle and/or
|
|
22
23
|
its affiliates. BSD-3-Clause.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: quickdbd-cli
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.14
|
|
4
4
|
Summary: Compile QuickDBD-syntax schema files into interactive HTML ER diagrams, with no table limit.
|
|
5
5
|
Project-URL: Homepage, https://github.com/MarkPhamm/quickdbd-cli
|
|
6
6
|
Author: Minh (Mark) Pham
|
|
@@ -77,7 +77,7 @@ Every demo is a single HTML page written by `qdbd build`, from a QuickDBD schema
|
|
|
77
77
|
| [](https://qdbd.dev/demo/synthetic/)<br>**[200 tables](https://qdbd.dev/demo/synthetic/)**<br>Five business domains, 200 tables and every relationship type. Search, filter, click a table to focus it. | [](https://qdbd.dev/demo/diff/)<br>**[Schema diff](https://qdbd.dev/demo/diff/)**<br>What a branch changed, coloured in: new tables in green, changed columns in amber, removals listed. |
|
|
78
78
|
| [](https://qdbd.dev/demo/adventureworks/)<br>**[AdventureWorks](https://qdbd.dev/demo/adventureworks/)**<br>Microsoft's bicycle-company OLTP database: 68 tables in five schemas, one region each, with column descriptions. | [](https://qdbd.dev/demo/synthea/#t=concept)<br>**[OMOP CDM on Synthea](https://qdbd.dev/demo/synthea/#t=concept)**<br>OHDSI's dbt-synthea: synthetic patient records mapped to the OMOP Common Data Model. 145 tables, 235 relationships from tests. |
|
|
79
79
|
|
|
80
|
-
[View all
|
|
80
|
+
[View all 18 demos →](https://qdbd.dev/demo/)
|
|
81
81
|
<!-- gallery:end -->
|
|
82
82
|
|
|
83
83
|
## Quickstart
|
|
@@ -49,7 +49,7 @@ Every demo is a single HTML page written by `qdbd build`, from a QuickDBD schema
|
|
|
49
49
|
| [](https://qdbd.dev/demo/synthetic/)<br>**[200 tables](https://qdbd.dev/demo/synthetic/)**<br>Five business domains, 200 tables and every relationship type. Search, filter, click a table to focus it. | [](https://qdbd.dev/demo/diff/)<br>**[Schema diff](https://qdbd.dev/demo/diff/)**<br>What a branch changed, coloured in: new tables in green, changed columns in amber, removals listed. |
|
|
50
50
|
| [](https://qdbd.dev/demo/adventureworks/)<br>**[AdventureWorks](https://qdbd.dev/demo/adventureworks/)**<br>Microsoft's bicycle-company OLTP database: 68 tables in five schemas, one region each, with column descriptions. | [](https://qdbd.dev/demo/synthea/#t=concept)<br>**[OMOP CDM on Synthea](https://qdbd.dev/demo/synthea/#t=concept)**<br>OHDSI's dbt-synthea: synthetic patient records mapped to the OMOP Common Data Model. 145 tables, 235 relationships from tests. |
|
|
51
51
|
|
|
52
|
-
[View all
|
|
52
|
+
[View all 18 demos →](https://qdbd.dev/demo/)
|
|
53
53
|
<!-- gallery:end -->
|
|
54
54
|
|
|
55
55
|
## Quickstart
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "quickdbd-cli"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.14"
|
|
8
8
|
description = "Compile QuickDBD-syntax schema files into interactive HTML ER diagrams, with no table limit."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "Apache-2.0"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.1.14"
|
|
@@ -3,11 +3,13 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import argparse
|
|
6
|
+
import dataclasses
|
|
7
|
+
import json
|
|
6
8
|
import sys
|
|
7
9
|
from pathlib import Path
|
|
8
10
|
|
|
9
11
|
from . import __version__
|
|
10
|
-
from .model import Schema
|
|
12
|
+
from .model import Diagnostic, Schema
|
|
11
13
|
from .project import Project, SelectionError, selector_expr
|
|
12
14
|
|
|
13
15
|
OK = "No errors found. Good job!"
|
|
@@ -36,9 +38,31 @@ def _title(project: Project) -> str:
|
|
|
36
38
|
return "diagram"
|
|
37
39
|
|
|
38
40
|
|
|
41
|
+
def _github_annotation(d: Diagnostic, level: str) -> str:
|
|
42
|
+
"""A GitHub Actions workflow command, which shows `d` inline on the PR's changed lines."""
|
|
43
|
+
|
|
44
|
+
def esc(s: str) -> str:
|
|
45
|
+
return s.replace("%", "%25").replace("\r", "%0D").replace("\n", "%0A")
|
|
46
|
+
|
|
47
|
+
def esc_prop(s: str) -> str:
|
|
48
|
+
return esc(s).replace(":", "%3A").replace(",", "%2C")
|
|
49
|
+
|
|
50
|
+
return f"::{level} file={esc_prop(d.path)},line={d.line},title=qdbd::{esc(d.message)}"
|
|
51
|
+
|
|
52
|
+
|
|
39
53
|
def cmd_check(args) -> int:
|
|
40
54
|
project = _open(args)
|
|
41
55
|
schema = project.compile()
|
|
56
|
+
if args.format == "json":
|
|
57
|
+
ok = not schema.errors and not (args.strict and schema.warnings)
|
|
58
|
+
diags = [dataclasses.asdict(d) for d in schema.diagnostics]
|
|
59
|
+
report = {"ok": ok, "errors": len(schema.errors), "warnings": len(schema.warnings), "diagnostics": diags}
|
|
60
|
+
print(json.dumps(report, indent=2))
|
|
61
|
+
return 0 if ok else 1
|
|
62
|
+
if args.format == "github":
|
|
63
|
+
# Under --strict a warning fails the job, so it's annotated as an error.
|
|
64
|
+
for d in schema.diagnostics:
|
|
65
|
+
print(_github_annotation(d, "error" if args.strict else d.severity))
|
|
42
66
|
ok = _print_diagnostics(schema, args.strict)
|
|
43
67
|
if ok and not schema.diagnostics:
|
|
44
68
|
print(OK)
|
|
@@ -345,6 +369,12 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
345
369
|
sp = sub.add_parser("check", help="validate schema files")
|
|
346
370
|
paths(sp)
|
|
347
371
|
sp.add_argument("--strict", action="store_true", help="treat warnings (QuickDBD incompatibilities) as errors")
|
|
372
|
+
sp.add_argument(
|
|
373
|
+
"--format",
|
|
374
|
+
choices=["text", "github", "json"],
|
|
375
|
+
default="text",
|
|
376
|
+
help="github: also emit GitHub Actions annotations (inline on PR lines); json: a machine-readable report",
|
|
377
|
+
)
|
|
348
378
|
sp.set_defaults(func=cmd_check)
|
|
349
379
|
|
|
350
380
|
sp = sub.add_parser("build", help="write an interactive HTML diagram")
|
|
@@ -91,6 +91,11 @@ def test_sample_databases_and_dbt_projects(built):
|
|
|
91
91
|
assert sizes["adventureworks"][0] == 68 and sizes["adventureworks"][1] >= 90
|
|
92
92
|
assert sizes["synthea"] == (145, 235)
|
|
93
93
|
assert sizes["shopify"][0] == 155 and sizes["shopify"][1] > 150 # every link inferred
|
|
94
|
+
for d, n in (("pubs", 11), ("adventureworks-dw", 28), ("saas-billing", 13), ("courses", 14)):
|
|
95
|
+
m = _model(built / d / "index.html")
|
|
96
|
+
assert len(m["tables"]) == n and m["rels"], d
|
|
97
|
+
dw = _model(built / "adventureworks-dw" / "index.html")
|
|
98
|
+
assert {"dim", "fact"} <= {t["name"] for t in dw["tags"]} # coloured by the Dim/Fact name prefixes
|
|
94
99
|
aw = _model(built / "adventureworks" / "index.html")
|
|
95
100
|
assert aw["regions"] and any(t.get("doc") for t in aw["tables"]) # MS_Description -> tooltips
|
|
96
101
|
|
|
@@ -136,6 +136,47 @@ def test_check_strict_warnings(tmp_path):
|
|
|
136
136
|
assert main(["check", "--strict", str(f)]) == 1
|
|
137
137
|
|
|
138
138
|
|
|
139
|
+
def test_check_format_github(tmp_path, capsys):
|
|
140
|
+
bad = tmp_path / "bad.txt"
|
|
141
|
+
bad.write_text("T\n-\nA int foo\nB decimal(10, 2)\n")
|
|
142
|
+
assert main(["check", "--format", "github", str(bad)]) == 1
|
|
143
|
+
out = capsys.readouterr().out.splitlines()
|
|
144
|
+
assert f"::error file={bad},line=3,title=qdbd::Unrecognized keyword 'foo'" in out
|
|
145
|
+
assert any(line.startswith(f"::warning file={bad},line=4,title=qdbd::QuickDBD would reject this") for line in out)
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def test_check_format_github_strict_promotes_warnings(tmp_path, capsys):
|
|
149
|
+
f = tmp_path / "w.txt"
|
|
150
|
+
f.write_text("T\n-\nA decimal(10, 2)\n")
|
|
151
|
+
assert main(["check", "--format", "github", "--strict", str(f)]) == 1
|
|
152
|
+
assert capsys.readouterr().out.startswith(f"::error file={f},line=3,")
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def test_github_annotation_escaping():
|
|
156
|
+
from quickdbd_cli.cli import _github_annotation
|
|
157
|
+
from quickdbd_cli.model import Diagnostic
|
|
158
|
+
|
|
159
|
+
d = Diagnostic("a,b:c.txt", 2, "50% bad\nnext", "warning")
|
|
160
|
+
assert _github_annotation(d, "warning") == "::warning file=a%2Cb%3Ac.txt,line=2,title=qdbd::50%25 bad%0Anext"
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def test_check_format_json(tmp_path, capsys):
|
|
164
|
+
bad = tmp_path / "bad.txt"
|
|
165
|
+
bad.write_text("T\n-\nA int foo\n")
|
|
166
|
+
assert main(["check", "--format", "json", str(bad)]) == 1
|
|
167
|
+
data = json.loads(capsys.readouterr().out)
|
|
168
|
+
assert data["ok"] is False and data["errors"] == 1 and data["warnings"] == 0
|
|
169
|
+
assert data["diagnostics"] == [
|
|
170
|
+
{"path": str(bad), "line": 3, "severity": "error", "message": "Unrecognized keyword 'foo'"}
|
|
171
|
+
]
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def test_check_format_json_ok(capsys):
|
|
175
|
+
assert main(["check", "--format", "json", str(SAMPLE)]) == 0
|
|
176
|
+
data = json.loads(capsys.readouterr().out)
|
|
177
|
+
assert data == {"ok": True, "errors": 0, "warnings": 0, "diagnostics": []}
|
|
178
|
+
|
|
179
|
+
|
|
139
180
|
def test_build_is_deterministic_and_does_not_write_sidecar(tmp_path):
|
|
140
181
|
work = tmp_path / "s"
|
|
141
182
|
shutil.copytree(SYNTH, work)
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.1.13"
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|