| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677 |
- #!/usr/bin/env python3
- """Re-fetch the full hourly table from the DB and compare it to the cached copy.
- This honours the "DB full rerun" requirement without overwriting any cached
- artifact. The result is written to
- ``models/results/verification/db_parity.json``.
- """
- from __future__ import annotations
- import argparse
- import json
- import sys
- from pathlib import Path
- sys.path.insert(0, str(Path(__file__).resolve().parents[2]))
- from models.common import db, paths, queries
- from models.common.constants import EXPECTED_PER_HOUR
- def key(row: dict):
- return (row["批次"], row["小时"])
- def normalize(row: dict):
- values = {field: row.get(field) for field in queries.NUMERIC_HOURLY_FIELDS}
- values["运行样本数"] = row.get("运行样本数")
- return values
- def main() -> None:
- parser = argparse.ArgumentParser(description="DB 全量小时中间表一致性校验")
- parser.add_argument("--start", default="2025-01-01 00:00:00")
- parser.add_argument("--end", default="2026-09-01 00:00:00")
- parser.add_argument("--chunk-days", type=int, default=14)
- parser.add_argument("--cached", default=str(paths.HOURLY_FULL))
- args = parser.parse_args()
- batch_to_unit = db.load_pks_batches()
- selected = sorted(batch_to_unit)
- configs = db.load_alarm_config({batch: batch_to_unit[batch] for batch in selected})
- start, end = queries.parse_dt(args.start), queries.parse_dt(args.end)
- print(f"从数据库重新聚合 {start} .. {end},批次 {selected}", flush=True)
- fetched = queries.fetch_all_hourly(start, end, args.chunk_days, batch_to_unit, configs, selected)
- print(f"数据库小时行:{len(fetched):,}", flush=True)
- cached = queries.load_hourly_csv(Path(args.cached))
- print(f"缓存小时行:{len(cached):,}", flush=True)
- fetched_by_key = {key(row): row for row in fetched}
- cached_by_key = {key(row): row for row in cached}
- only_db = sorted(set(fetched_by_key) - set(cached_by_key))
- only_cache = sorted(set(cached_by_key) - set(fetched_by_key))
- mismatched = []
- for item_key in sorted(set(fetched_by_key) & set(cached_by_key)):
- if normalize(fetched_by_key[item_key]) != normalize(cached_by_key[item_key]):
- mismatched.append({"key": item_key, "db": normalize(fetched_by_key[item_key]), "cache": normalize(cached_by_key[item_key])})
- report = {
- "db_rows": len(fetched),
- "cached_rows": len(cached),
- "only_in_db": len(only_db),
- "only_in_cache": len(only_cache),
- "mismatched_rows": len(mismatched),
- "identical": not only_db and not only_cache and not mismatched,
- "only_in_db_examples": only_db[:10],
- "only_in_cache_examples": only_cache[:10],
- "mismatched_examples": mismatched[:5],
- }
- output = paths.RESULTS / "verification"
- output.mkdir(parents=True, exist_ok=True)
- (output / "db_parity.json").write_text(json.dumps(report, ensure_ascii=False, indent=2), encoding="utf-8")
- print(json.dumps({k: v for k, v in report.items() if not k.endswith("examples")}, ensure_ascii=False, indent=2), flush=True)
- print(f"报告:{output / 'db_parity.json'}", flush=True)
- if __name__ == "__main__":
- main()
|