58 lines
1.8 KiB
Python
58 lines
1.8 KiB
Python
"""Warehouse schema retrieval.
|
|
|
|
Physical layer via SQLAlchemy's Inspector against the active dataset's
|
|
Postgres schema; semantic layer (table/column descriptions, metric catalog)
|
|
from YAML files under `api/datasets/<name>/`.
|
|
|
|
Cached on first call — the schema is stable for a process's lifetime.
|
|
Restart the api to pick up a dataset switch or YAML edits.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from functools import lru_cache
|
|
from typing import Any
|
|
|
|
from sqlalchemy import inspect
|
|
|
|
from api.config import get_settings
|
|
from api.datasets import read_yaml
|
|
from api.tools.db import get_engine
|
|
from api.tools.types import Column, Metric, SchemaContext, Table
|
|
|
|
|
|
@lru_cache(maxsize=1)
|
|
def load_schema() -> SchemaContext:
|
|
dataset = get_settings().dataset
|
|
docs = read_yaml(dataset, "schema_docs.yaml")
|
|
metrics_yaml = read_yaml(dataset, "metrics.yaml")
|
|
|
|
# Postgres schema name comes from the dataset id; the schema_docs file
|
|
# can override it, but normally they match.
|
|
schema_name: str = docs.get("schema", dataset)
|
|
table_descs: dict[str, dict[str, Any]] = docs.get("tables", {}) or {}
|
|
|
|
insp = inspect(get_engine())
|
|
tables: dict[str, Table] = {
|
|
name: Table(
|
|
name=name,
|
|
description=(desc := table_descs.get(name, {})).get("description"),
|
|
columns=[
|
|
Column.from_inspector(c, (desc.get("columns") or {}).get(c["name"]))
|
|
for c in insp.get_columns(name, schema=schema_name)
|
|
],
|
|
)
|
|
for name in insp.get_table_names(schema=schema_name)
|
|
}
|
|
|
|
metrics: dict[str, Metric] = {
|
|
name: Metric.from_spec(name, spec)
|
|
for name, spec in (metrics_yaml.get("metrics", {}) or {}).items()
|
|
}
|
|
|
|
return SchemaContext(schema=schema_name, tables=tables, metrics=metrics)
|
|
|
|
|
|
def get_metric(name: str) -> Metric | None:
|
|
return load_schema().metrics.get(name)
|