"""Optional v2 server-owned fact tables; no writer output accepts these fields.""" import math import re GROUPS = {"vimshottari", "ashtakavarga", "shadbala", "functional_roles", "avasthas", "special_points", "sade_sati", "annual"} PATH = re.compile(r"[A-Za-z0-9_.\[\]-]{1,400}") STATUSES = {"multi_system_consensus", "single_system_inference", "parameter_sensitive", "unclosed_divisional_chart", "user_history_verification_required", "blocked"} def validate_fact_tables(value, add): def text(item, maximum, minimum=0): return isinstance(item, str) and minimum <= len(item) <= maximum if not isinstance(value, list) or len(value) > 8: add("factTables", "must be array with at most eight groups") return seen = set() for index, table in enumerate(value): path = f"factTables[{index}]" if not isinstance(table, dict) or set(table) != {"id", "title", "claimStatus", "sourcePath", "note", "columns", "rows"}: add(path, "invalid table keys") continue group = table["id"] if not isinstance(group, str) or group not in GROUPS or group in seen: add(path, "invalid or duplicate group") else: seen.add(group) source = table["sourcePath"] if not isinstance(source, str) or not PATH.fullmatch(source): add(path, "invalid source path") continue if not text(table["title"], 120, 1) or not text(table["note"], 500): add(path, "invalid text") if table["claimStatus"] not in STATUSES: add(path, "invalid claim status") columns = table["columns"] if not isinstance(columns, list) or not 1 <= len(columns) <= 16 or not all(text(c, 80, 1) for c in columns): add(path, "invalid columns") continue rows = table["rows"] if not isinstance(rows, list) or len(rows) > 2000: add(path, "invalid rows") continue for row in rows: if not isinstance(row, dict) or set(row) != {"sourcePath", "cells"}: add(path, "invalid row keys") continue origin = row["sourcePath"] if not isinstance(origin, str) or not PATH.fullmatch(origin) or not (origin == source or origin.startswith(source + ".") or origin.startswith(source + "[")): add(path, "fact_table_source_mismatch") cells = row["cells"] if not isinstance(cells, list) or len(cells) != len(columns): add(path, "fact_table_cell_count") continue for cell in cells: if cell is None or isinstance(cell, bool) or text(cell, 300): continue if isinstance(cell, (int, float)) and math.isfinite(cell): continue add(path, "invalid cell")