"""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 def within(child, parent): return isinstance(child, str) and bool(PATH.fullmatch(child)) and (child == parent or child.startswith(parent + ".") or child.startswith(parent + "[")) def grid(table, path, parent=None): source = table.get("sourcePath") if not isinstance(source, str) or not PATH.fullmatch(source): add(path, "invalid source path") return if parent is not None and not within(source, parent): add(path, "fact_table_source_mismatch") if not text(table.get("title"), 120, 1) or not text(table.get("note"), 500): add(path, "invalid text") columns = table.get("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") return rows = table.get("rows") if not isinstance(rows, list) or len(rows) > 2000: add(path, "invalid rows") return for row in rows: if not isinstance(row, dict) or set(row) != {"sourcePath", "cells"}: add(path, "invalid row keys") continue if not within(row["sourcePath"], 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") if not isinstance(value, list) or len(value) > 8: add("factTables", "must be array with at most eight groups") return seen = set() required = {"id", "title", "claimStatus", "sourcePath", "note", "columns", "rows"} subkeys = required - {"claimStatus"} for index, table in enumerate(value): path = f"factTables[{index}]" if not isinstance(table, dict) or not required <= set(table) or set(table) - required - {"subtables"}: 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) if not isinstance(table["claimStatus"], str) or table["claimStatus"] not in STATUSES: add(path, "invalid claim status") grid(table, path) if "subtables" not in table: continue children = table["subtables"] if not isinstance(children, list) or len(children) > 8: add(path, "invalid subtables") continue child_ids = set() for child_index, child in enumerate(children): child_path = f"{path}.subtables[{child_index}]" if not isinstance(child, dict) or set(child) != subkeys: add(child_path, "invalid subtable keys") continue child_id = child["id"] if not text(child_id, 80, 1) or not re.fullmatch(r"[a-z][a-z0-9_-]*", child_id) or child_id in child_ids: add(child_path, "invalid or duplicate subtable") else: child_ids.add(child_id) parent = table["sourcePath"] if isinstance(parent, str): grid(child, child_path, parent)