#!/usr/bin/env python3
"""
Week 20: Data — Picture & Bar Graphs
Phase 3: Measurement, Time & Money / Data
Standards: 2.MD.D.9, 2.MD.D.10 (picture/bar graphs up to 4 categories)
New Standard:
- Reduced drill (40% fewer problems)
- Estimation before counting/calculating
- Multiple strategies encouraged (count symbols, skip count, compare bars)
- Better word problems (reasoning over keywords)
- Real challenges (open-ended: create your own graph/survey)
- Number talks (compare data WITHOUT counting)
- Friday self-check with reflection
- Fact fluency warmup daily
- Word problems every day
"""
import os
import glob
from weasyprint import HTML
from math_helpers import (
CSS, TEACHER_CSS,
page, page_multi, hdr, sec, b, bw, bl,
tg_page_multi, tg_sec, tg_note, tg_section_title,
bar_graph_svg,
)
from activity_builders import (
build_picture_graph,
build_bar_graph,
build_challenge_box,
)
BASE = os.path.expanduser("~/Home_School/2nd_Grade/Math")
WEEK = os.path.join(BASE, "Week_20")
BLUE = "#4a90d9"
GREEN = "#5cb85c"
RED = "#d9534f"
ORANGE = "#f0ad4e"
def tally_html(count):
"""Simple tally marks in groups of five."""
fives, rest = count // 5, count % 5
marks = ('<span style="text-decoration:line-through; letter-spacing:2px;">||||</span> ' * fives)
marks += '|' * rest
return f'<span style="font-family:monospace; font-size:1.2em;">{marks}</span>'
# ═══════════════════════════════════════════════════
# MONDAY — Reading Picture Graphs
# ═══════════════════════════════════════════════════
def monday():
# 1: Fluency warmup — skip counting (helps read graph keys)
s1 = '<div class="abox" style="background:#e8f5e9;">'\
'<p><b>2-min Fluency Warm-up</b> — Skip count. This helps you read graph keys fast!</p>'\
'<div class="mgrid c2">'
s1 += f'<div class="abox"><p>Count by 2s: 2, 4, {b("2.5em")}, {b("2.5em")}, 10</p></div>'
s1 += f'<div class="abox"><p>Count by 5s: 5, 10, {b("2.5em")}, {b("2.5em")}, 25</p></div>'
s1 += f'<div class="abox"><p>Count by 2s: 6, 8, {b("2.5em")}, {b("2.5em")}, 14</p></div>'
s1 += f'<div class="abox"><p>Count by 5s: 15, 20, {b("2.5em")}, {b("2.5em")}, 35</p></div>'
s1 += '</div></div>'
# 2: Intro — What is a picture graph? Guided example
s2 = '<p class="note">A <b>picture graph</b> uses symbols to show data. '\
'The <b>key</b> tells you what each symbol is worth. '\
'Our class voted for their favorite pets:</p>'
s2 += build_picture_graph({
"categories": [
{"name": "Dogs", "count": 8},
{"name": "Cats", "count": 6},
{"name": "Fish", "count": 3},
{"name": "Birds", "count": 5},
],
"key_value": 1,
"symbol": "★",
"note": "Each ★ stands for 1 vote. Count carefully!"
})
# 3: Questions about the graph (reduced from 6 to 4)
s3 = '<p class="note">Use the pet graph above to answer. Show how you counted.</p>'\
'<div class="mgrid c2">'
questions = [
"How many children voted for dogs?",
"Which pet got the FEWEST votes?",
"How many more voted for dogs than fish?",
"How many children voted in all?",
]
for q in questions:
s3 += f'<div class="abox"><p>{q}</p><p>Answer: {b("5em")}</p></div>'
s3 += '</div>'
# 4: NEW — Number Talk: compare WITHOUT counting
s4 = '<div class="abox" style="background:#e3f2fd;">'\
'<p><b>Number Talk</b> — Answer WITHOUT counting every symbol.</p>'\
'<div class="mgrid c2">'
s4 += '<div class="abox"><p>Just by LOOKING at the pet graph, which row is longest? '\
'How does that tell you the most popular pet?</p>'\
'<p style="font-size:11px;">My thinking: ' + bw() + '</p></div>'
s4 += '<div class="abox"><p>Dogs have 8 votes and cats have 6. Explain how you can see the '\
'difference is 2 without counting all the stars.</p>'\
'<p style="font-size:11px;">My thinking: ' + bw() + '</p></div>'
s4 += '</div></div>'
# 5: Word problem
s5 = '<div class="wp">'\
'<p class="note">Read carefully. Think about what is happening.</p>'\
'<div class="abox"><p>Mrs. Lane\'s class made a picture graph of lunches sold. '\
'Pizza had 7 symbols and tacos had 4 symbols. Each symbol = 1 lunch. '\
'How many MORE pizzas were sold than tacos?</p>'\
'<p style="font-size:11px;">Draw or write your thinking: ' + bw() + '</p>'\
'<p>' + b("5em") + ' more pizzas</p></div></div>'
return page(20, "Monday", "Reading Picture Graphs",
f'''{sec(1, "Warm-up: Skip Counting", s1)}
{sec(2, "What Is a Picture Graph?", s2)}
{sec(3, "Answer Questions About the Graph", s3)}
{sec(4, "Number Talk: Compare Without Counting", s4)}
{sec(5, "Word Problem", s5)}''')
# ═══════════════════════════════════════════════════
# TUESDAY — Picture Graphs with Keys + Estimation
# ═══════════════════════════════════════════════════
def tuesday():
# 1: Fluency warmup
s1 = '<div class="abox" style="background:#e8f5e9;">'\
'<p><b>2-min Fluency Warm-up</b> — If each symbol = 2, how many in all?</p>'\
'<div class="mgrid c2">'
for symbols in [3, 5, 4, 6]:
s1 += f'<div class="abox"><p>{"★" * symbols} ★ = 2 → {b("3em")}</p></div>'
s1 += '</div></div>'
# 2: NEW — Estimate First! (orange box)
s2 = '<div class="abox" style="background:#fff3e0;">'\
'<p><b>Estimate First!</b> Look at the graph below for ONE second. '\
'Before counting anything, estimate how many books were read in all. '\
'Then count and compare.</p>'\
'<p>My estimate: ' + b("5em") + '</p>'\
'<p>My actual count (after Section 3): ' + b("5em") + '</p>'\
'<p style="font-size:11px;">Was your estimate close? ' + bw() + '</p></div>'
# 3: Picture graph with a key of 2 (reduced from 6 to 4 questions)
s3 = '<p class="note"><b>Books We Read in March</b> — Careful! Each 📕 stands for 2 books.</p>'
s3 += build_picture_graph({
"categories": [
{"name": "Maya", "count": 8},
{"name": "Leo", "count": 12},
{"name": "Ava", "count": 6},
{"name": "Sam", "count": 10},
],
"key_value": 2,
"symbol": "📕",
"note": "Skip count by 2s to find each total."
})
s3 += '<div class="mgrid c2">'
for q in [
"How many books did Leo read?",
"How many books did Ava read?",
"Who read exactly 10 books?",
"How many books did Maya and Ava read together?",
]:
s3 += f'<div class="abox"><p>{q}</p><p>Answer: {b("5em")}</p></div>'
s3 += '</div>'
# 4: Two strategies for counting with a key
s4 = '<div class="abox" style="background:#f3e5f5;">'\
'<p><b>Show Two Different Ways</b> — Sam\'s row has 5 symbols and each = 2. '\
'Find Sam\'s total two different ways.</p>'\
'<p style="font-size:11px; color:#666;">Ideas: (1) skip count 2, 4, 6... (2) add 5 + 5, (3) count all symbols then double.</p>'\
'<p>Way 1: ' + bw() + '</p>'\
'<p>Way 2: ' + bw() + '</p>'\
'<p>Total: ' + b("4em") + ' books</p></div>'
# 5: Word problem
s5 = '<div class="wp">'\
'<p class="note">Read carefully. Draw if it helps!</p>'\
'<div class="abox"><p>A picture graph shows 6 sun symbols for sunny days in April. '\
'Each symbol stands for 2 days. Jenna says there were 6 sunny days. '\
'Is she right? Explain and give the correct number.</p>'\
'<p style="font-size:11px;">My thinking: ' + bw() + '</p>'\
'<p>Correct number of sunny days: ' + b("4em") + '</p></div></div>'
return page(20, "Tuesday", "Picture Graphs with Keys",
f'''{sec(1, "Warm-up: Symbol Counting", s1)}
{sec(2, "Estimate First!", s2)}
{sec(3, "Picture Graph: Each Symbol = 2", s3)}
{sec(4, "Show Two Different Ways", s4)}
{sec(5, "Word Problem", s5)}''')
# ═══════════════════════════════════════════════════
# WEDNESDAY — Reading & Making Bar Graphs
# ═══════════════════════════════════════════════════
def wednesday():
# 1: Fluency warmup
s1 = '<div class="abox" style="background:#e8f5e9;">'\
'<p><b>2-min Fluency Warm-up</b> — How many more? Solve in your head.</p>'\
'<div class="mgrid c2">'
for a, c in [(9, 4), (12, 7), (15, 8), (11, 6)]:
s1 += f'<div class="abox"><p>{a} is how many more than {c}? {b("3em")}</p></div>'
s1 += '</div></div>'
# 2: Reading a bar graph (reduced from 6 to 4 questions)
s2 = '<p class="note">A <b>bar graph</b> uses bars instead of symbols. '\
'Taller bar = bigger number. Our class counted playground equipment choices:</p>'
s2 += build_bar_graph({
"entries": [
{"label": "Swings", "value": 9},
{"label": "Slide", "value": 6},
{"label": "Monkey Bars", "value": 12},
{"label": "Sandbox", "value": 4},
],
"max_value": 12,
"note": "Read the height of each bar."
})
s2 += '<div class="mgrid c2">'
for q in [
"Which choice is MOST popular?",
"Which choice is LEAST popular?",
"How many more chose monkey bars than the slide?",
"How many children chose swings or the sandbox?",
]:
s2 += f'<div class="abox"><p>{q}</p><p>Answer: {b("5em")}</p></div>'
s2 += '</div>'
# 3: Make a bar graph from tally data
s3 = '<p class="note">The tally chart shows fruit picked at the orchard. '\
'Draw one bar for each fruit in the empty graph. Label your bars!</p>'
s3 += '<table class="tbl"><tr><th>Fruit</th><th>Tally</th><th>Number</th></tr>'
for name, count in [("Apples", 7), ("Pears", 4), ("Peaches", 9), ("Plums", 5)]:
s3 += f'<tr><td>{name}</td><td>{tally_html(count)}</td><td>{b("3em")}</td></tr>'
s3 += '</table>'
s3 += '<div class="abox" style="min-height:150px;">'\
'<p style="font-size:11px; color:#666;">Draw your bar graph here. '\
'Make a scale up the side (0, 2, 4, 6, 8, 10) and label each bar.</p></div>'
# 4: Word problems from data
s4 = '<div class="wp">'\
'<p class="note">Use the fruit tally chart. Think about what is happening.</p>'\
'<div class="mgrid c2">'
s4 += '<div class="abox"><p>The farmer needs 10 peaches for a pie order. '\
'Does she have enough? How do you know?</p>'\
'<p style="font-size:11px;">My thinking: ' + bw() + '</p></div>'
s4 += '<div class="abox"><p>How many pieces of fruit were picked in all? '\
'Show how you added.</p>'\
'<p style="font-size:11px;">My work: ' + bw() + '</p>'\
'<p>' + b("4em") + ' pieces</p></div>'
s4 += '</div></div>'
# 5: Challenge — Create your own
s5 = '<div class="abox" style="background:#f3e5f5;">'\
'<p><b>Create Your Own</b></p>'\
'<p>Make up your own bar graph data! Pick a topic (favorite colors, animals, games...), '\
'invent numbers for 3 categories, and write one question about your graph.</p>'\
'<p>My topic: ' + bl() + '</p>'\
'<p>Category 1: ' + bw() + ' Number: ' + b("3em") + '</p>'\
'<p>Category 2: ' + bw() + ' Number: ' + b("3em") + '</p>'\
'<p>Category 3: ' + bw() + ' Number: ' + b("3em") + '</p>'\
'<p>My question: ' + bl() + '</p></div>'
return page(20, "Wednesday", "Reading & Making Bar Graphs",
f'''{sec(1, "Warm-up: How Many More?", s1)}
{sec(2, "Read the Bar Graph", s2)}
{sec(3, "Make a Bar Graph from Tallies", s3)}
{sec(4, "Word Problems from Data", s4)}
{sec(5, "Challenge: Create Your Own", s5)}''')
# ═══════════════════════════════════════════════════
# THURSDAY — Solving Problems with Data
# ═══════════════════════════════════════════════════
def thursday():
# 1: Fluency warmup
s1 = '<div class="abox" style="background:#e8f5e9;">'\
'<p><b>2-min Fluency Warm-up</b> — Add three numbers in your head.</p>'\
'<div class="mgrid c2">'
for nums in [(4, 6, 5), (7, 3, 8), (5, 5, 9), (6, 4, 7)]:
s1 += f'<div class="abox"><p>{nums[0]} + {nums[1]} + {nums[2]} = {b("3em")}</p></div>'
s1 += '</div></div>'
# 2: Real-world data word problems (reduced from 4 to 3)
s2 = '<p class="note"><b>Canned Food Drive</b> — Room 2A collected cans for four weeks:</p>'
s2 += build_bar_graph({
"entries": [
{"label": "Week 1", "value": 8},
{"label": "Week 2", "value": 14},
{"label": "Week 3", "value": 11},
{"label": "Week 4", "value": 17},
],
"max_value": 20,
"note": "Use this graph for the problems below."
})
s2 += '<div class="wp"><div class="mgrid c2">'
problems = [
"The class goal was 15 cans in one week. In which week(s) did they reach the goal?",
"How many more cans did they collect in Week 4 than in Week 1?",
"Between which two weeks did the number of cans go DOWN?",
]
for text in problems:
s2 += f'<div class="abox"><p style="font-size:0.95em;">{text}</p>'\
'<p style="font-size:11px;">Draw or write your thinking: ' + bw() + '</p>'\
'<p>Answer: ' + b("6em") + '</p></div>'
s2 += '</div></div>'
# 3: Two-step data problem
s3 = '<div class="abox">'\
'<p class="note">Two steps! Solve one step at a time.</p>'\
'<p>The class collected 8 cans in Week 1 and 14 cans in Week 2. '\
'They need 30 cans total for the first prize. How many MORE cans do they still need?</p>'\
'<p>Step 1 — cans so far: ' + b("4em") + '</p>'\
'<p>Step 2 — cans still needed: ' + b("4em") + '</p></div>'
# 4: NEW — Number Talk
s4 = '<div class="abox" style="background:#e3f2fd;">'\
'<p><b>Number Talk</b> — Explain WITHOUT calculating.</p>'\
'<div class="mgrid c2">'
s4 += '<div class="abox"><p>Look at the food drive graph. Without adding, is the TOTAL '\
'for all 4 weeks more or less than 40? How can you tell quickly?</p>'\
'<p style="font-size:11px;">My thinking: ' + bw() + '</p></div>'
s4 += '<div class="abox"><p>Week 2 has 14 and Week 3 has 11. Explain how the bars '\
'show the difference without counting every can.</p>'\
'<p style="font-size:11px;">My thinking: ' + bw() + '</p></div>'
s4 += '</div></div>'
# 5: Create your own survey question
s5 = '<div class="abox" style="background:#f3e5f5;">'\
'<p><b>Create Your Own Survey</b></p>'\
'<p>Write a survey question you could ask your family (example: "What is your favorite dinner?"). '\
'List 3 answer choices. Tomorrow you will collect real answers!</p>'\
'<p>My survey question: ' + bl() + '</p>'\
'<p>Choice 1: ' + bw() + ' Choice 2: ' + bw() + ' Choice 3: ' + bw() + '</p></div>'
return page(20, "Thursday", "Solving Problems with Data",
f'''{sec(1, "Warm-up: Add Three Numbers", s1)}
{sec(2, "Food Drive Data Problems", s2)}
{sec(3, "Two-Step Problem", s3)}
{sec(4, "Number Talk: Reason from the Bars", s4)}
{sec(5, "Challenge: Create Your Own Survey", s5)}''')
# ═══════════════════════════════════════════════════
# FRIDAY — Review & Self-Check
# ═══════════════════════════════════════════════════
def friday():
# 1: Fluency warmup
s1 = '<div class="abox" style="background:#e8f5e9;">'\
'<p><b>2-min Fluency Warm-up</b> — Skip count and compare. Go fast!</p>'\
'<div class="mgrid c2">'
s1 += f'<div class="abox"><p>4 symbols, each = 2: {b("3em")}</p></div>'
s1 += f'<div class="abox"><p>3 symbols, each = 5: {b("3em")}</p></div>'
s1 += f'<div class="abox"><p>13 is how many more than 8? {b("3em")}</p></div>'
s1 += f'<div class="abox"><p>6 + 9 + 4 = {b("3em")}</p></div>'
s1 += '</div></div>'
# 2: Estimate First! review
s2 = '<div class="abox" style="background:#fff3e0;">'\
'<p><b>Estimate First!</b> Glance at the graph below. Before counting, '\
'estimate the total number of ice creams sold. Then check.</p>'\
'<p>My estimate: ' + b("5em") + ' Actual total: ' + b("5em") + '</p></div>'
# 3: Mixed review graph (reduced from 6 to 4 questions)
s3 = '<p class="note"><b>Ice Cream Stand Sales</b></p>'
s3 += build_bar_graph({
"entries": [
{"label": "Vanilla", "value": 10},
{"label": "Chocolate", "value": 15},
{"label": "Strawberry", "value": 5},
{"label": "Mint", "value": 8},
],
"max_value": 15,
"note": "Use any strategy you like to read the graph."
})
s3 += '<div class="mgrid c2">'
for q in [
"Which flavor sold the most?",
"How many more chocolate than strawberry?",
"Which two flavors together equal 15?",
"Put the flavors in order from least to most sold.",
]:
s3 += f'<div class="abox"><p>{q}</p><p>Answer: {b("6em")}</p></div>'
s3 += '</div>'
# 4: Word problem review
s4 = '<div class="wp">'\
'<p class="note">Read carefully. Show your work.</p>'\
'<div class="abox"><p>The ice cream stand wants to stop selling its two slowest flavors. '\
'Which two flavors should it stop selling? How many total sales did those two flavors have?</p>'\
'<p style="font-size:11px;">My thinking: ' + bw() + '</p>'\
'<p>Flavors: ' + bw() + ' Total: ' + b("4em") + '</p></div></div>'
# 5: Self-check with emoji reflection
s5 = '<div class="selfcheck">'\
'<p><b>Self-Check: How do you feel about graphs?</b></p>'\
'<div style="display:flex; gap:20px; flex-wrap:wrap;">'\
'<div style="background:#fff3e0; padding:10px; border-radius:5px; flex:1; min-width:150px;">'\
'<p><b>Reading Picture Graphs (with keys)</b></p>'\
'<p>😊 I got it! | 😐 Getting there | 😟 Need help</p>'\
'</div>'\
'<div style="background:#e3f2fd; padding:10px; border-radius:5px; flex:1; min-width:150px;">'\
'<p><b>Reading Bar Graphs</b></p>'\
'<p>😊 I got it! | 😐 Getting there | 😟 Need help</p>'\
'</div>'\
'<div style="background:#f3e5f5; padding:10px; border-radius:5px; flex:1; min-width:150px;">'\
'<p><b>Making Graphs from Data</b></p>'\
'<p>😊 I got it! | 😐 Getting there | 😟 Need help</p>'\
'</div>'\
'</div>'\
'<p style="margin-top:10px;"><b>One thing I learned this week:</b> ' + bl() + '</p>'\
'<p><b>One thing I want to practice more:</b> ' + bl() + '</p>'\
'<p><b>My favorite kind of graph and why:</b> ' + bl() + '</p>'\
'</div>'
return page(20, "Friday", "Week 20 Review & Self-Check",
f'''{sec(1, "Warm-up: Graph Fluency", s1)}
{sec(2, "Estimate First!", s2)}
{sec(3, "Ice Cream Graph Review", s3)}
{sec(4, "Word Problem", s4)}
{sec(5, "Self-Check & Reflection", s5)}''')
# ═══════════════════════════════════════════════════
# TEACHER GUIDE
# ═══════════════════════════════════════════════════
def teacher_guide():
return tg_page_multi(
"Week 20", "Data: Picture & Bar Graphs — Teacher Guide",
tg_section_title("Monday: Reading Picture Graphs") +
tg_sec(1, "Warm-up Skip Counting", ["2s: 6, 8 / 5s: 15, 20 / 2s: 10, 12 / 5s: 25, 30"]) +
tg_sec(2, "Pet Graph", ["Dogs 8, Cats 6, Fish 3, Birds 5"]) +
tg_sec(3, "Questions", ["8 dogs; fish fewest; 8−3=5 more; 8+6+3+5=22 total"]) +
tg_sec(4, "Number Talk", ["Longest row = most votes (dogs)", "Dogs row sticks out 2 stars past cats row"]) +
tg_sec(5, "Word Problem", ["7−4=3 more pizzas"]) +
tg_note("Page break"),
tg_section_title("Tuesday: Picture Graphs with Keys") +
tg_sec(1, "Warm-up ★=2", ["6, 10, 8, 12"]) +
tg_sec(2, "Estimate First", ["Accept reasonable estimates; actual total = 36 books"]) +
tg_sec(3, "Book Graph (key=2)", ["Leo 12, Ava 6, Sam read 10, Maya+Ava = 8+6 = 14"]) +
tg_sec(4, "Two Ways", ["Skip count 2,4,6,8,10; or 5+5; or 5×2. Total 10 books"]) +
tg_sec(5, "Word Problem", ["Jenna forgot the key. 6 symbols × 2 = 12 sunny days"]) +
tg_note("Page break"),
tg_section_title("Wednesday: Reading & Making Bar Graphs") +
tg_sec(1, "Warm-up How Many More", ["5, 5, 7, 5"]) +
tg_sec(2, "Playground Graph", ["Monkey bars most; sandbox least; 12−6=6 more; 9+4=13"]) +
tg_sec(3, "Fruit Bar Graph", ["Apples 7, Pears 4, Peaches 9, Plums 5. Check bars match & are labeled"]) +
tg_sec(4, "Word Problems", ["No — only 9 peaches, needs 10 (1 short)", "7+4+9+5=25 pieces"]) +
tg_sec(5, "Create Own", ["Accept any topic with 3 categories and a sensible question"]) +
tg_note("Page break"),
tg_section_title("Thursday: Solving Problems with Data") +
tg_sec(1, "Warm-up Add Three", ["15, 18, 19, 17"]) +
tg_sec(2, "Food Drive", ["Week 4 only (17≥15); 17−8=9 more; down between Week 2 and Week 3"]) +
tg_sec(3, "Two-Step", ["Step 1: 8+14=22, Step 2: 30−22=8 more cans"]) +
tg_sec(4, "Number Talk", ["More than 40: 8+14+11+17 ≈ 10+15+10+15=50", "Week 2 bar is 3 taller than Week 3"]) +
tg_sec(5, "Create Own Survey", ["Accept any survey question with 3 choices"]) +
tg_note("Page break"),
tg_section_title("Friday: Review & Self-Check") +
tg_sec(1, "Warm-up", ["8, 15, 5, 19"]) +
tg_sec(2, "Estimate First", ["Actual total = 10+15+5+8 = 38"]) +
tg_sec(3, "Ice Cream Graph", ["Chocolate most; 15−5=10 more; vanilla+strawberry=15; strawberry, mint, vanilla, chocolate"]) +
tg_sec(4, "Word Problem", ["Stop strawberry (5) and mint (8); total 13 sales"]) +
tg_sec(5, "Self-Check", ["Accept student self-assessment and reflection"])
)
# ═══════════════════════════════════════════════════
# GENERATE
# ═══════════════════════════════════════════════════
if __name__ == "__main__":
os.makedirs(WEEK, exist_ok=True)
for f in glob.glob(os.path.join(WEEK, "*.pdf")):
os.remove(f)
for f in glob.glob(os.path.join(WEEK, "*.html")):
os.remove(f)
days = [
(monday, "Monday", "Reading Picture Graphs"),
(tuesday, "Tuesday", "Picture Graphs with Keys"),
(wednesday, "Wednesday", "Reading & Making Bar Graphs"),
(thursday, "Thursday", "Solving Problems with Data"),
(friday, "Friday", "Week 20 Review & Self-Check"),
]
for lesson_func, day, title in days:
html = lesson_func()
fname = f"Week_20_{day}"
path = os.path.join(WEEK, f"{fname}.html")
with open(path, "w") as f:
f.write(html)
pdf_path = os.path.join(WEEK, f"{fname}.pdf")
HTML(filename=path).write_pdf(pdf_path)
print(f"Generated: {pdf_path}")
html = teacher_guide()
path = os.path.join(WEEK, "Week_20_Teacher_Guide.html")
with open(path, "w") as f:
f.write(html)
pdf_path = os.path.join(WEEK, "Week_20_Teacher_Guide.pdf")
HTML(filename=path).write_pdf(pdf_path)
print(f"Generated: {pdf_path}")
print("\nWeek 20 (new standard) complete!")