Remove Trino: Superset reads fuel-db directly as fuel_ro (PD-14)
trino/ deleted; the role trino_ro becomes fuel_ro, its password read from a secret file at connect time; dashboards' SQL and column list for PostgreSQL; the Compose project is renamed petrolyst, with volumes pinned to their old names. AGENTS.md points to the architecture context. Not deployed: the next deploy needs README's one-time steps first. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
686318dfac
commit
8405706ed8
25 files changed
+453
-410
No files matched your search
@@ -5,7 +5,7 @@ Implements SPEC.md in this folder: the two dashboards the Petrolyst app embeds,
|
||||
(atoma-overview) and "ATOMA · Site" (atoma-site, the four detail tabs), one dataset per
|
||||
public.v_* view and every chart. Nothing in the bundle names a site.
|
||||
|
||||
build_bundle.py --database-uuid <uuid of "Fuel (Trino)"> [--out atoma-dashboards.zip]
|
||||
build_bundle.py --database-uuid <uuid of "Fuel"> [--out atoma-dashboards.zip]
|
||||
|
||||
The database uuid differs per Superset instance; `import_bundle.py --show-database`
|
||||
prints it. Output is deterministic: the same inputs give a byte-identical zip,
|
||||
@@ -37,8 +37,10 @@ import yaml
|
||||
HERE = Path(__file__).resolve().parent
|
||||
VERSION = "1.0.0"
|
||||
NAMESPACE = uuid.uuid5(uuid.NAMESPACE_URL, "https://petrolyst.com/superset/atoma-dashboards")
|
||||
DATABASE_NAME = "Fuel (Trino)"
|
||||
DATABASE_URI = "trino://superset@trino:8080/fuel" # as superset-init registers it
|
||||
DATABASE_NAME = "Fuel"
|
||||
# As superset-init registers it: fuel-db directly, as the read-only role fuel_ro. The password
|
||||
# comes from a secret file at connect time (superset_config.py), never from the bundle.
|
||||
DATABASE_URI = "postgresql+psycopg2://fuel_ro@fuel-db:5432/fuel"
|
||||
CATALOG, SCHEMA = "fuel", "public"
|
||||
|
||||
SITE_FILTER = "NATIVE_FILTER-site"
|
||||
@@ -69,7 +71,7 @@ MAIN_DTTM = {
|
||||
"v_electricity_cost": "bill_month",
|
||||
}
|
||||
|
||||
# Calculated columns: (name, Trino SQL, type, label). The Petrolyst server's site_status
|
||||
# Calculated columns: (name, PostgreSQL SQL, type, label). The Petrolyst server's site_status
|
||||
# (petrolyst-platform server/) shows the same two texts.
|
||||
CALCULATED = {
|
||||
"v_site": [
|
||||
@@ -93,7 +95,7 @@ CALCULATED = {
|
||||
],
|
||||
}
|
||||
|
||||
# Virtual datasets: name -> (view it extends, extra output columns, Trino SQL).
|
||||
# Virtual datasets: name -> (view it extends, extra output columns, PostgreSQL SQL).
|
||||
# coverage_detail feeds S3. The view's lane names start with a sort number ("1 Stock
|
||||
# intervals"), which reads as a count on the chart, so it adds lane_name without the number
|
||||
# (the chart still sorts by lane). Its amount column is the tooltip: the litres delivered in a
|
||||
@@ -106,7 +108,7 @@ CALCULATED = {
|
||||
# day cannot be told apart. Everywhere else the date window is exact: on a single-visit day the
|
||||
# opening visit's delivery is included and the closing visit's excluded.
|
||||
VIRTUAL = {
|
||||
"coverage_detail": ("v_coverage", [("lane_name", "varchar(60)"), ("amount", "varchar(120)")], """\
|
||||
"coverage_detail": ("v_coverage", [("lane_name", "character varying(60)"), ("amount", "character varying(120)")], """\
|
||||
SELECT c.site_uid, c.site_code, c.site_name, c.site_label, c.lane, c.status,
|
||||
c.start_on, c.end_on, c.start_ts, c.end_ts,
|
||||
regexp_replace(c.lane, '^[0-9]+ ', '') AS lane_name,
|
||||
@@ -114,19 +116,19 @@ SELECT c.site_uid, c.site_code, c.site_name, c.site_label, c.lane, c.status,
|
||||
CASE WHEN f.ambiguous THEN 'not attributed: a delivery on a day with several visits (see Deliveries)'
|
||||
WHEN f.filled_l IS NULL AND coalesce(f.n_unknown, 0) = 0 THEN 'no delivery'
|
||||
WHEN f.filled_l IS NULL THEN 'filled, quantity not recorded'
|
||||
ELSE format('%,.0f L filled', CAST(f.filled_l AS double)) END
|
||||
ELSE to_char(round(f.filled_l), 'FM999,999,999,990') || ' L filled' END
|
||||
|| CASE WHEN f.n_unknown > 0 AND f.filled_l IS NOT NULL AND NOT f.ambiguous
|
||||
THEN ', plus ' || CAST(f.n_unknown AS varchar) || ' of unknown quantity' ELSE '' END
|
||||
WHEN c.lane = '4 Electricity bills' THEN
|
||||
CASE WHEN b.n_bills > 1 THEN 'several bills start this day (see Electricity bills)'
|
||||
WHEN b.disputed THEN 'bill disputed, amount withheld'
|
||||
WHEN b.amount IS NULL THEN 'bill amount not recorded'
|
||||
ELSE format('%,.0f AFN billed (inferred)', CAST(b.amount AS double)) END
|
||||
ELSE to_char(round(b.amount), 'FM999,999,999,990') || ' AFN billed (inferred)' END
|
||||
END AS amount
|
||||
FROM public.v_coverage c
|
||||
LEFT JOIN (SELECT s.site_uid, s.start_on, s.end_on,
|
||||
sum(CASE WHEN d.delivery_date < s.end_on THEN d.delivered_l END) AS filled_l,
|
||||
count_if(d.delivery_date < s.end_on AND d.delivered_l IS NULL) AS n_unknown,
|
||||
count(*) FILTER (WHERE d.delivery_date < s.end_on AND d.delivered_l IS NULL) AS n_unknown,
|
||||
bool_or(d.delivery_date IN (s.start_on, s.end_on) AND m.site_uid IS NOT NULL) AS ambiguous
|
||||
FROM (SELECT DISTINCT site_uid, start_on, end_on FROM public.v_coverage
|
||||
WHERE lane = '1 Stock intervals') s
|
||||
@@ -216,8 +218,8 @@ def read_columns(path: Path) -> dict[str, list[tuple[str, str]]]:
|
||||
return views
|
||||
|
||||
|
||||
def superset_type(trino_type: str) -> str:
|
||||
return trino_type.upper().replace(",", ", ")
|
||||
def superset_type(data_type: str) -> str:
|
||||
return data_type.upper().replace(",", ", ")
|
||||
|
||||
|
||||
def dataset_yaml(view: str, columns: list[tuple[str, str]], database_uuid: str) -> dict[str, Any]:
|
||||
@@ -968,7 +970,7 @@ def build(database_uuid: str, columns_path: Path) -> dict[str, Any]:
|
||||
views = read_columns(columns_path)
|
||||
files: dict[str, Any] = {
|
||||
"metadata.yaml": {"version": VERSION, "type": "Dashboard", "timestamp": "2026-10-24T00:00:00+00:00"},
|
||||
"databases/Fuel_Trino.yaml": {
|
||||
"databases/Fuel.yaml": {
|
||||
"database_name": DATABASE_NAME,
|
||||
"sqlalchemy_uri": DATABASE_URI,
|
||||
"cache_timeout": None,
|
||||
@@ -988,7 +990,7 @@ def build(database_uuid: str, columns_path: Path) -> dict[str, Any]:
|
||||
for name, (base, extra, _) in VIRTUAL.items():
|
||||
views[name] = views[base] + extra
|
||||
for view in sorted(views):
|
||||
files[f"datasets/Fuel_Trino/{view}.yaml"] = dataset_yaml(view, views[view], database_uuid)
|
||||
files[f"datasets/Fuel/{view}.yaml"] = dataset_yaml(view, views[view], database_uuid)
|
||||
|
||||
# PD-12: the Petrolyst app embeds the Overview and one site dashboard, whose four detail
|
||||
# tabs are Superset's own (owner, 29 Oct). The app shows the site header and chooses site
|
||||
@@ -1083,7 +1085,7 @@ def write_zip(files: dict[str, Any], out: Path) -> None:
|
||||
def main(argv: list[str] | None = None) -> int:
|
||||
parser = argparse.ArgumentParser(description=__doc__.split("\n\n")[0])
|
||||
parser.add_argument("--database-uuid", required=True, type=uuid.UUID,
|
||||
help='uuid of the "Fuel (Trino)" database in the target Superset')
|
||||
help='uuid of the "Fuel" database in the target Superset')
|
||||
parser.add_argument("--columns", type=Path, default=HERE / "columns.tsv")
|
||||
parser.add_argument("--out", type=Path, default=Path("atoma-dashboards.zip"))
|
||||
args = parser.parse_args(argv)
|
||||
|
||||
Reference in new issue
Block a user