#!/usr/bin/env python3
"""
Teacher Guide Generator - Printable 1-page-per-day teaching guide
Usage:
from teacher_guide import generate_teacher_guide
generate_teacher_guide(week_dir=..., week_num=..., phonics_label=..., ...)
Generates a 5-page PDF (one per day) with:
- What We're Learning (plain-English explanation)
- Key Teaching Points (what to emphasize, common mistakes)
- Activity Walkthroughs (with answer keys)
- Extension Ideas (optional extra practice)
"""
import os, sys
sys.path.insert(0, os.path.dirname(__file__))
from weasyprint import HTML
# ─── Shared CSS for Teacher Guide ──────────────────────────────────────────────
TEACHER_CSS = """
@page {
size: letter;
margin: 0.5in 0.6in;
}
body {
font-family: 'Segoe UI', Arial, Helvetica, sans-serif;
font-size: 10px;
line-height: 1.4;
color: #333;
}
h1 {
font-size: 16px;
color: #1a237e;
border-bottom: 3px solid #1a237e;
padding-bottom: 4px;
margin: 0 0 2px 0;
}
h2 {
font-size: 12px;
color: #26A69A;
margin: 8px 0 4px 0;
border-bottom: 1.5px solid #B2DFDB;
padding-bottom: 2px;
}
h3 {
font-size: 10px;
color: #E65100;
margin: 6px 0 2px 0;
}
.day-badge {
display: inline-block;
background: #1a237e;
color: white;
font-size: 9px;
font-weight: bold;
padding: 2px 8px;
border-radius: 3px;
margin-right: 6px;
vertical-align: middle;
}
.section {
margin-bottom: 6px;
page-break-inside: avoid;
}
.learning-box {
background: #E8EAF6;
border-left: 4px solid #1a237e;
padding: 6px 8px;
border-radius: 0 6px 6px 0;
font-size: 10px;
line-height: 1.4;
}
.teaching-points {
background: #FFF8E1;
border-left: 4px solid #FFA000;
padding: 6px 8px;
border-radius: 0 6px 6px 0;
font-size: 10px;
}
.teaching-points ul {
margin: 2px 0;
padding-left: 16px;
}
.teaching-points li {
margin: 1px 0;
}
.activity-table {
width: 100%;
border-collapse: separate;
border-spacing: 0;
font-size: 9px;
margin: 4px 0;
}
.activity-table th {
background: #E0E0E0;
padding: 3px 6px;
text-align: left;
font-size: 9px;
border-bottom: 1.5px solid #999;
}
.activity-table td {
padding: 3px 6px;
border-bottom: 1px solid #EEE;
vertical-align: top;
}
.activity-table tr:nth-child(even) {
background: #FAFAFA;
}
.answer {
color: #2E7D32;
font-weight: bold;
}
.word-list {
font-size: 9px;
line-height: 1.5;
}
.word-list span {
display: inline-block;
background: #E0F2F1;
padding: 1px 6px;
border-radius: 3px;
margin: 1px 2px;
}
.grammar-focus {
background: #FFF3E0;
border-left: 4px solid #FF6D00;
padding: 6px 8px;
border-radius: 0 6px 6px 0;
font-size: 10px;
}
.page-break {
page-break-before: always;
}
footer {
font-size: 8px;
color: #999;
text-align: center;
margin-top: 8px;
border-top: 1px solid #EEE;
padding-top: 4px;
}
"""
# ─── Helper Functions ──────────────────────────────────────────────────────────
def _word_span(word):
return f'<span>{word}</span>'
def _answer_row(activity, answers):
return f'<tr><td><strong>{activity}</strong></td><td class="answer">{answers}</td></tr>'
def _activity_table(rows):
return f'<table class="activity-table"><thead><tr><th style="width:45%">Activity</th><th>Answer / Notes</th></tr></thead><tbody>{"".join(rows)}</tbody></table>'
def _monday_page(week_num, day_num, phonics_name, phonics_explanation,
words, grammar_focus, grammar_explanation,
story_text, sort_labels, sort_answers,
fill_answers, opposite_answers, sentence_words):
word_spans = " ".join(_word_span(w[0]) for w in words)
word_defs = "<br>".join(f"{w[0]}: {w[2]}" for w in words)
# Build sort answer key
sort_answer_html = "<br>".join(
f"<strong>{label}:</strong> {', '.join(answers)}"
for label, answers in zip(sort_labels, sort_answers)
)
# Fill-in-blank answers
fill_html = "<br>".join(f"<strong>{i+1}.</strong> {a}" for i, a in enumerate(fill_answers))
# Opposite answers
opp_html = "<br>".join(f"<strong>{w}:</strong> {a}" for w, a in opposite_answers)
return f"""
<h1><span class="day-badge">MONDAY</span> Week {week_num} — {phonics_name}</h1>
<div class="section">
<div class="learning-box">
<strong>What We're Learning:</strong><br>
{phonics_explanation}
</div>
</div>
<div class="section">
<div class="teaching-points">
<strong>📋 Key Teaching Points:</strong>
<ul>
<li>Emphasize that students should SLOW down when reading these words</li>
<li>Have students SAY the sound each time they see the pattern</li>
<li>Point out the pattern visually — circle or underline it on the whiteboard</li>
<li>Watch for: Students may try to sound out each letter separately instead of the pattern</li>
</ul>
</div>
</div>
<div class="section">
<div class="grammar-focus">
<strong>Grammar Focus:</strong> {grammar_focus}<br>
{grammar_explanation}
</div>
</div>
<h2>Activity Walkthrough & Answer Key</h2>
{_activity_table([
'<tr><td><strong>Word Cards</strong><br>Intro — students read each word, see definition, copy it</td><td>Words: ' + word_spans + '<br><br>Definitions:<br>' + word_defs + '</td></tr>',
'<tr><td><strong>Spelling Story</strong><br>Students read the story and find circle the spelling words</td><td>Read aloud first, then have student find words independently. Story contains all 12 words.</td></tr>',
'<tr><td><strong>Grammar Practice</strong><br>Apply grammar rule to example sentences</td><td>Demonstrate on whiteboard first. Have student read sentences aloud with correct punctuation.</td></tr>',
f'<tr><td><strong>Vowel Pattern Sort</strong><br>Sort words into pattern columns</td><td>{sort_answer_html}</td></tr>',
'<tr><td><strong>Copy Practice</strong><br>Student copies each word on writing lines</td><td>Focus on handwriting. Have student say the word AND the pattern sound before copying.</td></tr>',
f'<tr><td><strong>Fill in the Blank</strong><br>Choose the correct word from the bank</td><td>{fill_html}</td></tr>',
f'<tr><td><strong>Opposite Match-Up</strong><br>Write the opposite of each word</td><td>{opp_html}</td></tr>',
f'<tr><td><strong>Sentence Writing</strong><br>Write a sentence using each word</td><td>Words: {", ".join(sentence_words)}<br>Check: capital letter, complete thought, period at end.</td></tr>',
])}
</div>
<footer>Teacher Guide — Week {week_num}, Day {day_num} | {phonics_name}</footer>
"""
def _tuesday_page(week_num, day_num, phonics_name, phonics_explanation,
words, scramble_answers, reading_text, comp_questions, comp_answers,
pattern_hunt_answers, swap_answers, builder_prompts):
word_spans = " ".join(_word_span(w[0]) for w in words)
scramble_html = "<br>".join(
f"<strong>{scrambled}:</strong> <span class='answer'>{unscrambled}</span>"
for scrambled, unscrambled in scramble_answers
)
comp_html = "<br>".join(
f"<strong>Q{i+1}: {q}</strong><br> <span class='answer'>{a}</span>"
for i, (q, a) in enumerate(zip(comp_questions, comp_answers))
)
pattern_yes = ", ".join(pattern_hunt_answers.get("match", []))
pattern_no = ", ".join(pattern_hunt_answers.get("decoy", []))
swap_html = "<br>".join(
f"<strong>{i+1}.</strong> Underline: <span class='answer'>{a}</span>"
for i, a in enumerate(swap_answers)
)
builder_html = "<br>".join(
f"<strong>{p[0]}</strong> → Must include: <span class='answer'>{p[1]}</span>"
for p in builder_prompts
)
return f"""
<h1><span class="day-badge">TUESDAY</span> Week {week_num} — {phonics_name}</h1>
<div class="section">
<div class="learning-box">
<strong>What We're Learning:</strong><br>
{phonics_explanation}<br><br>
<strong>Today's Focus:</strong> Practice identifying the pattern in reading and apply it in new contexts.
</div>
</div>
<div class="section">
<div class="teaching-points">
<strong>📋 Key Teaching Points:</strong>
<ul>
<li>By Tuesday, students should be recognizing the pattern more quickly</li>
<li>Encourage self-correction: "Does that word really have the pattern, or something else?"</li>
<li>Pattern Hunt is discrimination practice — praise students who correctly skip decoy words</li>
<li>Reading passage: Read aloud together first, then student reads independently</li>
</ul>
</div>
</div>
<h2>Activity Walkthrough & Answer Key</h2>
{_activity_table([
f'<tr><td><strong>Remember This!</strong><br>Review box — quick recap of the pattern rules</td><td>Have student read the rule aloud. Fill in the "Try This" prompt together.</td></tr>',
f'<tr><td><strong>Word Scramble</strong><br>Unscramble the spelling words</td><td>{scramble_html}</td></tr>',
f'<tr><td><strong>Reading Comprehension</strong><br>Read passage, answer questions</td><td>{comp_html}<br><br>Tip: Have student point to where in the text they found the answer.</td></tr>',
f'<tr><td><strong>Pattern Hunt</strong><br>Check words that match the pattern</td><td><strong>✓ Check these:</strong> {pattern_yes}<br><strong>✗ Skip these:</strong> {pattern_no}<br><br>Ask: "Why did you check/skip that word?"</td></tr>',
f'<tr><td><strong>Word Swap</strong><br>Underline the pattern words in each sentence</td><td>{swap_html}</td></tr>',
f'<tr><td><strong>Sentence Builder</strong><br>Write sentences with given prompts</td><td>{builder_html}<br><br>Check for: capital letter, complete sentence, correct punctuation.</td></tr>',
])}
</div>
<footer>Teacher Guide — Week {week_num}, Day {day_num} | {phonics_name}</footer>
"""
def _wednesday_page(week_num, day_num, phonics_name, phonics_explanation,
words, reading_text, vocab_q_answer, vocab_q_question,
syn_ant_words, word_web_center, word_web_suggestions,
sort_answers, correct_answers, fill_answers,
context_clue_answer, context_clue_question, dictation_words,
context_clue_detective_answers=None, paragraph_label_answers=None):
word_spans = " ".join(_word_span(w[0]) for w in words)
syn_ant_html = "<br>".join(
f"<strong>{w}:</strong> synonym examples: ______, antonym examples: ______"
for w in syn_ant_words
)
sort_html = "<br>".join(
f"<strong>{s}</strong><br> <span class='answer'>{a}</span>"
for s, a in zip(sort_answers.get("sentences", []), sort_answers.get("correct_order", []))
) if sort_answers else "N/A"
correct_html = "<br>".join(
f"<strong>Before:</strong> {wrong}<br> <span class='answer'>After: {fixed}</span>"
for wrong, fixed in correct_answers
)
fill_html = "<br>".join(
f"<strong>{i+1}.</strong> {a}"
for i, a in enumerate(fill_answers)
)
web_html = "<br>".join(f"• {s}" for s in word_web_suggestions)
return f"""
<h1><span class="day-badge">WEDNESDAY</span> Week {week_num} — {phonics_name}</h1>
<div class="section">
<div class="learning-box">
<strong>What We're Learning:</strong><br>
{phonics_explanation}<br><br>
<strong>Today's Focus:</strong> Deepen understanding through vocabulary work, sentence analysis, and listening skills.
</div>
</div>
<div class="section">
<div class="teaching-points">
<strong>📋 Key Teaching Points:</strong>
<ul>
<li>Wednesday is about APPLICATION — moving from recognition to understanding</li>
<li>Synonyms/Antonyms: Accept ANY reasonable answer — there's no single correct response</li>
<li>Word Web: Encourage creative connections, not just obvious ones</li>
<li>Dictation: Read each word 3x — normal speed, slow, then normal again</li>
<li>Context clues: Teach student to look at SURROUNDING words for meaning</li>
</ul>
</div>
</div>
<h2>Activity Walkthrough & Answer Key</h2>
{_activity_table([
f'<tr><td><strong>Reading Passage</strong><br>Read and understand context</td><td>Read aloud together first. Student identifies pattern words as they read.</td></tr>',
f'<tr><td><strong>Vocabulary Question</strong><br>Infer meaning from context</td><td>Q: {vocab_q_question}<br><span class="answer">Answer: {vocab_q_answer}</span><br><br>Ask: "What words helped you figure that out?"</td></tr>',
f'<tr><td><strong>Synonyms & Antonyms</strong><br>Think of similar and opposite words</td><td>{syn_ant_html}<br><br>Accept any reasonable answers. Help with examples if stuck.</td></tr>',
f'<tr><td><strong>Word Web</strong><br>Connect related words to center word</td><td>Center: <strong>{word_web_center}</strong><br>Suggestions: {web_html}<br><br>Student should come up with their own — these are just guides.</td></tr>',
f'<tr><td><strong>Sentence Sort</strong><br>Put mixed-up words in correct order</td><td>{sort_html}</td></tr>',
f'<tr><td><strong>Correct the Sentence</strong><br>Fix capitalization and punctuation</td><td>{correct_html}</td></tr>',
f'<tr><td><strong>Fill in the Blank</strong><br>Choose the right word</td><td>{fill_html}</td></tr>',
f'<tr><td><strong>Context Clues</strong><br>Use surrounding words to find meaning</td><td>Q: {context_clue_question}<br><span class="answer">Answer: {context_clue_answer}</span></td></tr>',
f'<tr><td><strong>Dictation</strong><br>Listen and write each word</td><td>Words: {", ".join(dictation_words)}<br><br>Read each 3x: normal, slow, normal. Student self-checks after.</td></tr>',
])}
</div>
<footer>Teacher Guide — Week {week_num}, Day {day_num} | {phonics_name}</footer>
"""
def _thursday_page(week_num, day_num, phonics_name, phonics_explanation,
words, story_text, comp_questions, comp_answers,
sort_answers, detective_answers, swap_answers,
map_word, map_suggestions, para_prompts,
context_clue_writing_words=None, fix_paragraph_topic=None, fix_paragraph_conclusion=None):
word_spans = " ".join(_word_span(w[0]) for w in words)
comp_html = "<br>".join(
f"<strong>Q{i+1}: {q}</strong><br> <span class='answer'>{a}</span>"
for i, (q, a) in enumerate(zip(comp_questions, comp_answers))
)
detective_html = "<br>".join(
f"<strong>{misspelled}</strong> → <span class='answer'>{correct}</span>"
for misspelled, correct in detective_answers
)
swap_html = "<br>".join(
f"<strong>{i+1}.</strong> <span class='answer'>{a}</span>"
for i, a in enumerate(swap_answers)
)
map_html = "<br>".join(f"• {s}" for s in map_suggestions)
para_html = "<br>".join(
f"<strong>{p}:</strong> Should include 3-4 sentences with 2+ spelling words."
for p in para_prompts
)
return f"""
<h1><span class="day-badge">THURSDAY</span> Week {week_num} — {phonics_name}</h1>
<div class="section">
<div class="learning-box">
<strong>What We're Learning:</strong><br>
{phonics_explanation}<br><br>
<strong>Today's Focus:</strong> PRODUCTION — using the pattern actively in writing and creative expression.
</div>
</div>
<div class="section">
<div class="teaching-points">
<strong>📋 Key Teaching Points:</strong>
<ul>
<li>Thursday is the heaviest writing day — praise effort over perfection</li>
<li>Spelling Detective: This builds spelling awareness through ERROR recognition</li>
<li>Spelling Map: Break words into chunks (onset-rime, syllables)</li>
<li>Paragraph Builder: Model a paragraph first on the whiteboard if needed</li>
<li>Writing prompts: Accept creative interpretations — focus on pattern usage</li>
</ul>
</div>
</div>
<h2>Activity Walkthrough & Answer Key</h2>
{_activity_table([
f'<tr><td><strong>Remember This!</strong><br>Pattern review box</td><td>Quick review before writing activities. Have student recite the rules.</td></tr>',
f'<tr><td><strong>Spelling Story</strong><br>Read story, find and circle pattern words</td><td>All 12 words appear in the story. Read aloud first.</td></tr>',
f'<tr><td><strong>Comprehension Questions</strong><br>Answer questions about the story</td><td>{comp_html}</td></tr>',
f'<tr><td><strong>Sentence Sort</strong><br>Unscramble the sentences</td><td>Model the first one. Have student read the scrambled words, then arrange logically.</td></tr>',
f'<tr><td><strong>Spelling Detective</strong><br>Find and fix the misspelled words</td><td>{detective_html}<br><br>Ask: "What sound is wrong? What letter is missing?"</td></tr>',
f'<tr><td><strong>Word Swap</strong><br>Underline the pattern words</td><td>{swap_html}</td></tr>',
f'<tr><td><strong>Spelling Map</strong><br>Break the word into parts</td><td>Word: <strong>{map_word}</strong><br>Suggestions: {map_html}<br><br>Teach: breaking words into manageable chunks for spelling.</td></tr>',
f'<tr><td><strong>Paragraph Builder</strong><br>Write short paragraphs</td><td>{para_html}<br><br>Check: topic sentence, 2-3 details, at least 2 spelling words used correctly.</td></tr>',
f'<tr><td><strong>Write Your Own Story</strong><br>Creative writing with pattern words</td><td>Student chooses their own topic. Must use at least 4 spelling words. Focus on creativity and pattern usage.</td></tr>',
])}
</div>
<footer>Teacher Guide — Week {week_num}, Day {day_num} | {phonics_name}</footer>
"""
def _friday_page(week_num, day_num, phonics_name, phonics_explanation,
words, test_answers, review_story, story_questions, story_answers,
opposite_answers, builder_words, journal_words,
guess_meaning_answers=None, paragraph_parts_answers=None):
word_spans = " ".join(_word_span(w[0]) for w in words)
test_html = "<br>".join(
f"<strong>{i+1}.</strong> <span class='answer'>{a}</span>"
for i, a in enumerate(test_answers)
)
story_q_html = "<br>".join(
f"<strong>Q{i+1}: {q}</strong><br> <span class='answer'>{a}</span>"
for i, (q, a) in enumerate(zip(story_questions, story_answers))
)
opp_html = "<br>".join(
f"<strong>{w}:</strong> <span class='answer'>{a}</span>"
for w, a in opposite_answers
)
return f"""
<h1><span class="day-badge">FRIDAY</span> Week {week_num} — {phonics_name}</h1>
<div class="section">
<div class="learning-box">
<strong>What We're Learning:</strong><br>
{phonics_explanation}<br><br>
<strong>Today's Focus:</strong> REVIEW & ASSESSMENT — showing what was learned all week.
</div>
</div>
<div class="section">
<div class="teaching-points">
<strong>📋 Key Teaching Points:</strong>
<ul>
<li>Friday is assessment day — keep the mood positive and encouraging</li>
<li>Spelling Test: Read each word 3x (normal, slow, normal), then say it in a sentence</li>
<li>Don't correct spelling test errors immediately — mark them and review as a group after</li>
<li>Word Journal: This is a great chance for reflection — what was easy/hard this week?</li>
<li>Celebrate progress! Even one more word correct than last week is a win</li>
</ul>
</div>
</div>
<h2>Activity Walkthrough & Answer Key</h2>
{_activity_table([
f'<tr><td><strong>Spelling Test</strong><br>Dictation of all 12 words</td><td>{test_html}<br><br>Read each word 3x. Say in a sentence between 2nd and 3rd reading. Score: ___/12</td></tr>',
f'<tr><td><strong>Review Reading</strong><br>Read story containing all spelling words</td><td>Student reads aloud. Note any words they stumble on — these need extra practice next week.</td></tr>',
f'<tr><td><strong>Story Questions</strong><br>Comprehension check</td><td>{story_q_html}</td></tr>',
f'<tr><td><strong>Opposite Pairs</strong><br>Write the opposite of each word</td><td>{opp_html}<br><br>Accept reasonable alternatives.</td></tr>',
f'<tr><td><strong>Sentence Builder</strong><br>Write sentences with given words</td><td>Words: {", ".join(builder_words)}<br>Check: complete sentence, correct spelling, proper punctuation.</td></tr>',
f'<tr><td><strong>Show What You Know</strong><br>Identify the pattern in each word</td><td>All 12 words — student writes the pattern (e.g., "police" → "silent e, long o").</td></tr>',
f'<tr><td><strong>Word Journal</strong><br>Reflective writing about the week</td><td>Prompts: {", ".join(journal_words)}<br>Student writes a sentence or two about each. Encourage honesty about what was hard.</td></tr>',
])}
</div>
<div class="section">
<div style="background: #E8F5E9; border: 2px solid #4CAF50; border-radius: 8px; padding: 8px; text-align: center; font-size: 12px;">
<strong>🎉 Great Job This Week!</strong><br>
Score: ___/12 | Words to practice: ________________<br>
<em>Celebrate the effort, not just the score!</em>
</div>
</div>
<footer>Teacher Guide — Week {week_num}, Day {day_num} | {phonics_name}</footer>
"""
# ─── Main Generator ────────────────────────────────────────────────────────────
def generate_teacher_guide(**kw):
"""Generate a printable teacher guide PDF (1 page per day, 5 pages total).
Required kwargs:
week_dir, week_num, phonics_label, words
Monday kwargs:
monday_phonics_explanation (or monday_vocabulary_explanation for vocab weeks)
monday_grammar_focus, monday_grammar_explanation
monday_story_text, monday_sort_labels, monday_sort_answers
monday_fill_answers, monday_opposite_answers, monday_sentence_words
monday_context_clue_sentences (optional: [(sentence, word), ...])
Tuesday kwargs:
tuesday_phonics_explanation (or tuesday_vocabulary_explanation)
tuesday_scramble_answers
tuesday_comp_questions, tuesday_comp_answers
tuesday_pattern_hunt_answers, tuesday_swap_answers, tuesday_builder_prompts
tuesday_paragraph_example (optional: paragraph text for parts analysis)
Wednesday kwargs:
wednesday_phonics_explanation (or wednesday_vocabulary_explanation)
wednesday_vocab_q_answer, wednesday_vocab_q_question
wednesday_syn_ant_words, wednesday_word_web_center, wednesday_word_web_suggestions
wednesday_sort_answers, wednesday_correct_answers, wednesday_fill_answers
wednesday_context_clue_answer, wednesday_context_clue_question, wednesday_dictation_words
wednesday_context_clue_detective_answers (optional: [(sentence, clue_type), ...])
wednesday_paragraph_label_answers (optional: [(sentence, label), ...])
Thursday kwargs:
thursday_phonics_explanation (or thursday_vocabulary_explanation)
thursday_comp_questions, thursday_comp_answers
thursday_sort_answers, thursday_detective_answers, thursday_swap_answers
thursday_map_word, thursday_map_suggestions, thursday_para_prompts
thursday_context_clue_writing_words (optional: [word, ...])
thursday_fix_paragraph_topic (optional: topic sentence answer)
thursday_fix_paragraph_conclusion (optional: conclusion sentence answer)
Friday kwargs:
friday_phonics_explanation (or friday_vocabulary_explanation)
friday_test_answers
friday_story_questions, friday_story_answers
friday_opposite_answers, friday_builder_words, friday_journal_words
friday_guess_meaning_answers (optional: [(word, meaning), ...])
friday_paragraph_parts_answers (optional: {topic: ..., details: [...], conclusion: ...})
"""
week_dir = kw["week_dir"]
week_num = kw["week_num"]
phonics_label = kw["phonics_label"]
words = kw["words"]
phonics_name = kw.get("phonics_name", phonics_label.replace("_", " ").title())
day_dir = os.path.join(week_dir, "Teacher_Guide")
os.makedirs(day_dir, exist_ok=True)
pages = []
# ── MONDAY ──
pages.append(_monday_page(
week_num=week_num, day_num=1,
phonics_name=phonics_name,
phonics_explanation=kw.get("monday_phonics_explanation") or kw.get("monday_vocabulary_explanation", ""),
words=words,
grammar_focus=kw.get("monday_grammar_focus", ""),
grammar_explanation=kw.get("monday_grammar_explanation", ""),
story_text=kw.get("monday_story_text", ""),
sort_labels=kw.get("monday_sort_labels", []),
sort_answers=kw.get("monday_sort_answers", [[]]),
fill_answers=kw.get("monday_fill_answers", []),
opposite_answers=kw.get("monday_opposite_answers", []),
sentence_words=kw.get("monday_sentence_words", []),
))
# ── TUESDAY ──
pages.append(_tuesday_page(
week_num=week_num, day_num=2,
phonics_name=phonics_name,
phonics_explanation=kw.get("tuesday_phonics_explanation") or kw.get("tuesday_vocabulary_explanation", ""),
words=words,
scramble_answers=kw.get("tuesday_scramble_answers", []),
reading_text=kw.get("tuesday_reading_text", ""),
comp_questions=kw.get("tuesday_comp_questions", []),
comp_answers=kw.get("tuesday_comp_answers", []),
pattern_hunt_answers=kw.get("tuesday_pattern_hunt_answers", {"match": [], "decoy": []}),
swap_answers=kw.get("tuesday_swap_answers", []),
builder_prompts=kw.get("tuesday_builder_prompts", []),
))
# ── WEDNESDAY ──
pages.append(_wednesday_page(
week_num=week_num, day_num=3,
phonics_name=phonics_name,
phonics_explanation=kw.get("wednesday_phonics_explanation") or kw.get("wednesday_vocabulary_explanation", ""),
words=words,
reading_text=kw.get("wednesday_reading_text", ""),
vocab_q_answer=kw.get("wednesday_vocab_q_answer", ""),
vocab_q_question=kw.get("wednesday_vocab_q_question", ""),
syn_ant_words=kw.get("wednesday_syn_ant_words", []),
word_web_center=kw.get("wednesday_word_web_center", ""),
word_web_suggestions=kw.get("wednesday_word_web_suggestions", []),
sort_answers=kw.get("wednesday_sort_answers", {"sentences": [], "correct_order": []}),
correct_answers=kw.get("wednesday_correct_answers", []),
fill_answers=kw.get("wednesday_fill_answers", []),
context_clue_answer=kw.get("wednesday_context_clue_answer", ""),
context_clue_question=kw.get("wednesday_context_clue_question", ""),
dictation_words=kw.get("wednesday_dictation_words", []),
context_clue_detective_answers=kw.get("wednesday_context_clue_detective_answers", []),
paragraph_label_answers=kw.get("wednesday_paragraph_label_answers", []),
))
# ── THURSDAY ──
pages.append(_thursday_page(
week_num=week_num, day_num=4,
phonics_name=phonics_name,
phonics_explanation=kw.get("thursday_phonics_explanation") or kw.get("thursday_vocabulary_explanation", ""),
words=words,
story_text=kw.get("thursday_story_text", ""),
comp_questions=kw.get("thursday_comp_questions", []),
comp_answers=kw.get("thursday_comp_answers", []),
sort_answers=kw.get("thursday_sort_answers", {}),
detective_answers=kw.get("thursday_detective_answers", []),
swap_answers=kw.get("thursday_swap_answers", []),
map_word=kw.get("thursday_map_word", ""),
map_suggestions=kw.get("thursday_map_suggestions", []),
para_prompts=kw.get("thursday_para_prompts", []),
context_clue_writing_words=kw.get("thursday_context_clue_writing_words", []),
fix_paragraph_topic=kw.get("thursday_fix_paragraph_topic", ""),
fix_paragraph_conclusion=kw.get("thursday_fix_paragraph_conclusion", ""),
))
# ── FRIDAY ──
pages.append(_friday_page(
week_num=week_num, day_num=5,
phonics_name=phonics_name,
phonics_explanation=kw.get("friday_phonics_explanation") or kw.get("friday_vocabulary_explanation", ""),
words=words,
test_answers=kw.get("friday_test_answers", [w[0] for w in words]),
review_story=kw.get("friday_review_story", ""),
story_questions=kw.get("friday_story_questions", []),
story_answers=kw.get("friday_story_answers", []),
opposite_answers=kw.get("friday_opposite_answers", []),
builder_words=kw.get("friday_builder_words", []),
journal_words=kw.get("friday_journal_words", []),
guess_meaning_answers=kw.get("friday_guess_meaning_answers", []),
paragraph_parts_answers=kw.get("friday_paragraph_parts_answers", {}),
))
# Assemble HTML
body = "".join(
f"{page}" + ('<div style="page-break-before:always;"></div>' if i < len(pages) - 1 else '')
for i, page in enumerate(pages)
)
html = f"""<!DOCTYPE html>
<html><head><meta charset="utf-8">
<style>{TEACHER_CSS}</style></head>
<body>{body}</body></html>"""
pdf_path = os.path.join(day_dir, f"Week{week_num:02d}_Teacher_Guide_{phonics_label}.pdf")
HTML(string=html).write_pdf(pdf_path)
print(f"[ok] Teacher Guide generated: {pdf_path}")
return pdf_path