#!/usr/bin/env python3
"""Audit all ELA weeks - silent load, comprehensive checks."""
import os, sys, subprocess, importlib.util, glob, io, contextlib
BASE = os.path.expanduser('~/Home_School/2nd_Grade/English_Language_Arts')
PAGE_TARGETS = {'Monday': 4, 'Tuesday': 3, 'Wednesday': 4, 'Thursday': 4, 'Friday': 3}
def get_words(mod):
words = getattr(mod, 'WORDS', None)
if words:
return [w[0] if isinstance(w, tuple) else w for w in words]
word_names = getattr(mod, 'WORD_NAMES', None)
if word_names:
return list(word_names)
return None
def check_pages(week_dir):
pages = {}
for day in PAGE_TARGETS:
pdfs = glob.glob(os.path.join(week_dir, day, '*.pdf'))
if not pdfs:
pages[day] = None
continue
r = subprocess.run(['pdfinfo', pdfs[0]], capture_output=True, text=True, timeout=5)
for line in r.stdout.split('\n'):
if line.startswith('Pages:'):
pages[day] = int(line.split(':')[1].strip())
break
return pages
def word_coverage(text, words):
if not text:
return 0, []
tl = text.lower()
return len([w for w in words if w.lower() in tl]), [w for w in words if w.lower() not in tl]
def audit_week(week_num):
week_dir = os.path.join(BASE, f"Week_{week_num:02d}")
script = os.path.join(week_dir, f"generate_week{week_num}.py")
if not os.path.exists(script):
return f"Week {week_num:02d}: SKIP\n", 0
issues = []
# Load module silently
old_stdout = sys.stdout
old_stderr = sys.stderr
sys.stdout = io.StringIO()
sys.stderr = io.StringIO()
try:
sys.path.insert(0, BASE)
spec = importlib.util.spec_from_file_location(f'gen{week_num}', script)
mod = importlib.util.module_from_spec(spec)
try:
spec.loader.exec_module(mod)
except SystemExit:
pass # Generator validation calls sys.exit - ignore
words = get_words(mod)
except Exception as e:
sys.stdout = old_stdout
sys.stderr = old_stderr
return f"Week {week_num:02d}: LOAD FAIL — {e}\n", 1
finally:
sys.stdout = old_stdout
sys.stderr = old_stderr
if not words:
return f"Week {week_num:02d}: SKIP (no word list)\n", 0
if len(words) != 12:
issues.append(f"⚠ {len(words)} words (expected 12)")
# Page counts
pages = check_pages(week_dir)
page_strs = []
for day, target in PAGE_TARGETS.items():
pc = pages.get(day)
page_strs.append(f"{day}:{pc or 'X'}")
if pc and pc != target:
issues.append(f"📄 {day}: {pc}p (target {target})")
elif not pc:
issues.append(f"📄 {day}: no PDF")
# Word coverage
for attr in ['MONDAY_STORY', 'THURSDAY_STORY', 'FRIDAY_REVIEW_STORY']:
text = getattr(mod, attr, '')
if not text:
continue
cnt, miss = word_coverage(text, words)
if cnt < 12:
lbl = attr.replace('_', ' ').title()
issues.append(f"📖 {lbl}: {cnt}/12 (missing: {', '.join(miss[:4])})")
# Swap words
for attr in ['TUESDAY_SWAP_DATA', 'THURSDAY_SWAP_DATA']:
sd = getattr(mod, attr, [])
for i, item in enumerate(sd):
if isinstance(item, tuple) and len(item) >= 3:
for pair in item[1:]:
if isinstance(pair, tuple):
for w in pair:
if w not in words:
issues.append(f"🔄 {attr}[{i}]: '{w}' not in words")
# Monday sort
sa = getattr(mod, 'TEACHER_MONDAY_SORT_ANSWERS', None)
if isinstance(sa, dict):
all_s = []
for wl in sa.values():
all_s.extend(wl)
if set(all_s) != set(words):
miss = set(words) - set(all_s)
extra = set(all_s) - set(words)
if miss: issues.append(f"📊 Sort missing: {miss}")
if extra: issues.append(f"📊 Sort extra: {extra}")
# Pattern hunt
ph = getattr(mod, 'TUESDAY_PATTERN_HUNT_WORDS', None)
if ph and len(ph) == 12 and all(w in words for w in ph):
issues.append("🔍 Pattern hunt: no decoys")
# Correct sentences
for sent in getattr(mod, 'WEDNESDAY_CORRECT', []):
if sent and sent[0].isupper():
issues.append(f"✏️ Already capitalized: '{sent[:35]}...'")
# Fill mismatch
f1 = getattr(mod, 'MONDAY_FILL_DATA', None)
g1 = getattr(mod, 'TEACHER_MONDAY_FILL_ANSWERS', None)
if f1 and g1:
try:
if [a for _, a in f1] != g1:
issues.append("📝 Monday fill mismatch")
except: pass
fw = getattr(mod, 'WEDNESDAY_FILL', None)
gw = getattr(mod, 'TEACHER_WEDNESDAY_FILL_ANSWERS', None)
if fw and gw:
try:
if [a for _, a in fw] != gw:
issues.append("📝 Wednesday fill mismatch")
except: pass
# Paragraph label
pd = getattr(mod, 'WEDNESDAY_PARAGRAPH_LABEL_DATA', [])
pa = getattr(mod, 'TEACHER_WEDNESDAY_PARAGRAPH_LABEL_ANSWERS', [])
if pd and pa and len(pd) != len(pa):
issues.append(f"📋 Para label mismatch: {len(pd)} vs {len(pa)}")
return f"Week {week_num:02d}: {'✓ PASS' if not issues else f'✗ {len(issues)} issues'} [{', '.join(page_strs)}]\n" + \
''.join(f" • {i}\n" for i in issues), len(issues)
# Run
print("=" * 55)
print("ELA COURSE AUDIT — ALL 20 WEEKS")
print("=" * 55)
total = 0
passes = fails = skips = 0
for w in range(1, 21):
report, n = audit_week(w)
print(report, end='')
lines = report.strip().split('\n')[0]
if 'SKIP' in lines: skips += 1
elif 'PASS' in lines: passes += 1
else: fails += 1
total += n
print("=" * 55)
print(f"PASS: {passes} FAIL: {fails} SKIP: {skips}")
print(f"Total issues: {total}")