| """ |
| Parity check: backend/constants.py vs frontend/src/constants.ts. |
| Run from project root: python3 scripts/check_constants_sync.py |
| Exits 0 if in sync, 1 if drift detected. |
| """ |
| import re |
| import sys |
| from pathlib import Path |
|
|
| ROOT = Path(__file__).parent.parent |
| BACKEND = ROOT / "backend" / "app" / "constants.py" |
| FRONTEND = ROOT / "frontend" / "src" / "constants.ts" |
|
|
| EMOTION_KEYS = {"calm", "happy", "narrative", "angry", "excited", "sad", "fearful", "command"} |
| errors = [] |
|
|
| |
| py_text = BACKEND.read_text() |
|
|
| |
| py_reliability: dict[str, str] = {} |
| for m in re.finditer(r'"(calm|happy|narrative|angry|excited|sad|fearful|command)":\s*\{[^}]*"reliability":\s*"(high|medium)"', py_text): |
| py_reliability[m.group(1)] = m.group(2) |
|
|
| |
| |
| def extract_template_keys_py(text: str, speaker: str) -> set[str]: |
| |
| pattern = rf'"{speaker}":\s*\{{' |
| m = re.search(pattern, text) |
| if not m: |
| return set() |
| start = m.end() |
| # Scan forward collecting emotion keys until balanced brace closes |
| depth = 1 |
| pos = start |
| keys: set[str] = set() |
| while pos < len(text) and depth > 0: |
| if text[pos] == '{': |
| depth += 1 |
| elif text[pos] == '}': |
| depth -= 1 |
| if depth == 0: |
| break |
| |
| m2 = re.match(r'\s*"(calm|happy|narrative|angry|excited|sad|fearful|command)":\s*', text[pos:pos+30]) |
| if m2: |
| keys.add(m2.group(1)) |
| pos += 1 |
| return keys |
|
|
| py_yash_emo = extract_template_keys_py(py_text, "Yash") |
| py_neha_emo = extract_template_keys_py(py_text, "Neha") |
|
|
| |
| ts_text = FRONTEND.read_text() |
|
|
| |
| ts_reliability: dict[str, str] = {} |
| for m in re.finditer(r'(calm|happy|narrative|angry|excited|sad|fearful|command):\s*\{[^}]*reliability:\s*"(high|medium)"', ts_text): |
| ts_reliability[m.group(1)] = m.group(2) |
|
|
| |
| def extract_template_keys_ts(text: str, speaker: str) -> set[str]: |
| pattern = rf'\b{speaker}:\s*\{{' |
| m = re.search(pattern, text) |
| if not m: |
| return set() |
| start = m.end() |
| depth = 1 |
| pos = start |
| keys: set[str] = set() |
| while pos < len(text) and depth > 0: |
| if text[pos] == '{': |
| depth += 1 |
| elif text[pos] == '}': |
| depth -= 1 |
| if depth == 0: |
| break |
| m2 = re.match(r'\s*(calm|happy|narrative|angry|excited|sad|fearful|command):\s*', text[pos:pos+30]) |
| if m2: |
| keys.add(m2.group(1)) |
| pos += 1 |
| return keys |
|
|
| ts_yash_emo = extract_template_keys_ts(ts_text, "Yash") |
| ts_neha_emo = extract_template_keys_ts(ts_text, "Neha") |
|
|
| |
| for key in EMOTION_KEYS: |
| py_rel = py_reliability.get(key) |
| ts_rel = ts_reliability.get(key) |
| if py_rel and ts_rel and py_rel != ts_rel: |
| errors.append(f"RELIABILITY MISMATCH '{key}': constants.py={py_rel!r}, constants.ts={ts_rel!r}") |
| if py_rel and not ts_rel: |
| errors.append(f"MISSING reliability for '{key}' in constants.ts") |
| if ts_rel and not py_rel: |
| errors.append(f"MISSING reliability for '{key}' in constants.py") |
|
|
| for label, py_keys, ts_keys in [ |
| ("Yash", py_yash_emo, ts_yash_emo), |
| ("Neha", py_neha_emo, ts_neha_emo), |
| ]: |
| missing_in_ts = EMOTION_KEYS - ts_keys |
| missing_in_py = EMOTION_KEYS - py_keys |
| if missing_in_py: |
| errors.append(f"MISSING EMOTIONS in {label} (constants.py): {sorted(missing_in_py)}") |
| if missing_in_ts: |
| errors.append(f"MISSING EMOTIONS in {label} (constants.ts): {sorted(missing_in_ts)}") |
| if py_keys != ts_keys: |
| errors.append(f"{label} template keys differ: py={sorted(py_keys)}, ts={sorted(ts_keys)}") |
|
|
| if errors: |
| print("CONSTANTS SYNC FAILED:") |
| for e in errors: |
| print(f" {e}") |
| sys.exit(1) |
| else: |
| print("OK β constants.py and constants.ts are in sync.") |
| sys.exit(0) |
|
|