#!/usr/bin/env python3
"""Scan all files for 'from app.models import X', find anything not re-exported, and patch models.py."""
import re
import glob
MODELS = "app/models.py"
# Read models.py
with open(MODELS) as f:
models_text = f.read()
# Find all 'from app.models import ...' across the codebase
needed = set()
for pattern in ["app/routes/*.py", "app/services/*.py", "app/app.py", "tests/*.py"]:
for path in glob.glob(pattern):
with open(path) as f:
content = f.read()
for m in re.finditer(r"from\s+app\.models\s+import\s+\((.*?)\)", content, re.DOTALL):
block = m.group(1)
for name in re.findall(r"\b([a-zA-Z_]\w*)\b", block):
if not name.startswith("_"):
needed.add(name)
# Also handle inline imports
for m in re.finditer(r"from\s+app\.models\s+import\s+([\w,\s]+)", content):
names = m.group(1).split(",")
for name in names:
name = name.strip()
if name and not name.startswith("_"):
needed.add(name)
# Find what's currently re-exported in models.py (from app.XXX import ...)
# Also find what's defined locally in models.py
defined = set(re.findall(r"^def\s+([a-zA-Z_]\w*)", models_text, re.MULTILINE))
# Check for class/constant definitions too
constants = set(re.findall(r"^(TIERS|STRIPE_PRICE_IDS|STRIPE_WEBHOOK_SECRET)\s*=", models_text, re.MULTILINE))
local_names = defined | constants
# Find what's in the re-export blocks
reexported = set()
for m in re.finditer(r"from\s+app\.(\w+)\s+import\s+\((.*?)\)", models_text, re.DOTALL):
module = m.group(1)
block = m.group(2)
for name in re.findall(r"\b([a-zA-Z_]\w*)\b", block):
reexported.add(name)
# What's needed but NOT in models.py at all (neither defined nor re-exported)?
missing = needed - local_names - reexported
if not missing:
print("All imports accounted for!")
else:
# Find which module each missing name lives in
module_map = {
"migrations": [],
"helpers": [],
"analytics": [],
"model_documents": [],
"model_email_campaigns": [],
}
for mod, path in [
("migrations", "app/migrations.py"),
("helpers", "app/helpers.py"),
("analytics", "app/analytics.py"),
("model_documents", "app/model_documents.py"),
("model_email_campaigns", "app/model_email_campaigns.py"),
]:
with open(path) as f:
content = f.read()
for name in missing:
if re.search(rf"^def\s+{name}\s*\(", content, re.MULTILINE):
module_map[mod].append(name)
print(f"Missing re-exports: {sorted(missing)}")
for mod, names in module_map.items():
if names:
print(f" -> app.{mod}: {sorted(names)}")