94 lines
3.0 KiB
Python
94 lines
3.0 KiB
Python
"""Program schema — per-program DB table definitions + subprogram list."""
|
|
|
|
from __future__ import annotations
|
|
from dataclasses import dataclass, field
|
|
from pathlib import Path
|
|
from typing import Optional
|
|
|
|
|
|
@dataclass
|
|
class ColumnDef:
|
|
name: str
|
|
type: str # SQL type: "CHAR(6)", "NUMERIC(4)", "VARCHAR(30)"
|
|
primary_key: bool = False
|
|
nullable: bool = False
|
|
default: Optional[str] = None
|
|
cobol_field: Optional[str] = None # COBOL field name if different
|
|
|
|
|
|
@dataclass
|
|
class TableDef:
|
|
name: str
|
|
columns: list[ColumnDef] = field(default_factory=list)
|
|
create_if_missing: bool = True
|
|
sql_name: Optional[str] = None # COBOL SQL table name if different from YAML name
|
|
|
|
|
|
@dataclass
|
|
class SysinDef:
|
|
"""SYSIN card configuration for a single run scenario.
|
|
Each program defines its own sysin fields; period/modes are KIN08DBU specific.
|
|
"""
|
|
period: str | None = "202607"
|
|
include_invalid_period: bool = False
|
|
modes: list[str] = field(default_factory=lambda: ["NORMAL"])
|
|
|
|
|
|
@dataclass
|
|
class ScenarioDef:
|
|
"""A single run scenario: one set of inputs for one COBOL execution."""
|
|
id: str
|
|
sysin: SysinDef = field(default_factory=SysinDef)
|
|
inject_duplicate_pk: bool = False
|
|
|
|
|
|
@dataclass
|
|
class ProgramSchema:
|
|
program_id: str
|
|
db_tables: list[TableDef] = field(default_factory=list)
|
|
subprograms: list[str] = field(default_factory=list)
|
|
db_type: str = "SQLite"
|
|
db_name: str = "OVERTIME.DB"
|
|
runs: list[ScenarioDef] = field(default_factory=list)
|
|
|
|
@classmethod
|
|
def from_yaml(cls, path: str | Path) -> ProgramSchema:
|
|
import yaml
|
|
with open(path, encoding="utf-8") as f:
|
|
raw = yaml.safe_load(f)
|
|
tables = []
|
|
for t in raw.get("db_tables", []):
|
|
cols = [ColumnDef(**c) for c in t.get("columns", [])]
|
|
tables.append(TableDef(
|
|
name=t["name"], columns=cols,
|
|
sql_name=t.get("sql_name"),
|
|
))
|
|
runs_raw = raw.get("runs", [])
|
|
runs = []
|
|
for r in runs_raw:
|
|
sysin_raw = r.get("sysin", {})
|
|
runs.append(ScenarioDef(
|
|
id=r["id"],
|
|
sysin=SysinDef(**sysin_raw),
|
|
inject_duplicate_pk=r.get("inject_duplicate_pk", False),
|
|
))
|
|
return cls(
|
|
program_id=raw["program_id"],
|
|
db_tables=tables,
|
|
subprograms=raw.get("subprograms", []),
|
|
db_type=raw.get("db_type", "SQLite"),
|
|
db_name=raw.get("db_name", "OVERTIME.DB"),
|
|
runs=runs,
|
|
)
|
|
|
|
|
|
def load_schema(program_id: str, search_dirs: list[str | Path] | None = None) -> ProgramSchema:
|
|
"""Load per-program YAML schema by program ID."""
|
|
if search_dirs is None:
|
|
search_dirs = [Path(__file__).parent / "programs"]
|
|
for d in search_dirs:
|
|
p = Path(d) / f"{program_id}.yaml"
|
|
if p.exists():
|
|
return ProgramSchema.from_yaml(p)
|
|
raise FileNotFoundError(f"Schema not found for {program_id} in {search_dirs}")
|