#!/usr/bin/env python3
"""
Week 21: 2D Shapes & Attributes
Phase 4: Geometry & Fractions
Standards: 2.G.A.1 (recognize and draw shapes with specified attributes:
sides, vertices, angles; triangles, quadrilaterals, pentagons, hexagons)
New Standard:
- Reduced drill (40% fewer problems)
- Estimation before counting ("Estimate First!" — guess vertices before counting)
- Multiple strategies (two ways to sort/describe shapes)
- Better word problems (reasoning, draw-a-picture, real-world scenarios)
- Open-ended challenges (Create Your Own shapes/riddles)
- Number talks (reason about shapes WITHOUT counting)
- Friday self-check with emoji 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,
shape_svg,
)
from activity_builders import (
build_shape_attributes,
build_challenge_box,
)
BASE = os.path.expanduser("~/Home_School/2nd_Grade/Math")
WEEK = os.path.join(BASE, "Week_21")
BLUE = "#4a90d9"
GREEN = "#5cb85c"
RED = "#d9534f"
ORANGE = "#f0ad4e"
PURPLE = "#9b59b6"
def poly_svg(n_sides, color=BLUE, size=44):
"""Draw a regular polygon with n sides."""
import math as m
cx, cy, r = 22, 22, 18
pts = []
for i in range(n_sides):
ang = -m.pi / 2 + 2 * m.pi * i / n_sides
pts.append(f"{cx + r * m.cos(ang):.1f},{cy + r * m.sin(ang):.1f}")
return (f'<svg viewBox="0 0 44 44" width="{size}" height="{size}">'
f'<polygon points="{" ".join(pts)}" fill="{color}" stroke="#333" '
f'stroke-width="1.5" opacity="0.6"/></svg>')
def open_shape_svg(color=RED, size=44):
"""A NOT-closed figure (non-polygon example)."""
return (f'<svg viewBox="0 0 44 44" width="{size}" height="{size}">'
f'<polyline points="6,38 6,10 38,10 38,30" fill="none" stroke="{color}" '
f'stroke-width="2.5"/></svg>')
def curved_shape_svg(color=RED, size=44):
"""A curved figure (non-polygon example)."""
return (f'<svg viewBox="0 0 44 44" width="{size}" height="{size}">'
f'<path d="M 22 4 C 40 12 40 34 22 40 C 4 34 4 12 22 4 Z" fill="{color}" '
f'stroke="#333" stroke-width="1.5" opacity="0.6"/></svg>')
# ═══════════════════════════════════════════════════
# MONDAY — Sides, Vertices & Angles
# ═══════════════════════════════════════════════════
def monday():
# 1: Fluency warmup — shape facts
s1 = '<div class="abox" style="background:#e8f5e9;">'\
'<p><b>2-min Fluency Warm-up</b> — How many sides does each shape have? Answer fast!</p>'\
'<div class="mgrid c2">'
for name in ["triangle", "square", "rectangle", "circle"]:
s1 += f'<div class="abox"><p>A {name} has {b("3em")} sides.</p></div>'
s1 += '</div></div>'
# 2: New concept — sides, vertices, angles with guided example
s2 = '<p class="note">Every flat shape has three things we can count:<br>'\
'• <b>Sides</b> — the straight lines<br>'\
'• <b>Vertices</b> — the corners where two sides meet (one is called a <b>vertex</b>)<br>'\
'• <b>Angles</b> — the openings inside each corner</p>'
s2 += f'<div class="abox"><p style="text-align:center;">{poly_svg(3, BLUE, 60)}</p>'\
'<p style="text-align:center;">A triangle has <b>3 sides</b>, <b>3 vertices</b>, and <b>3 angles</b>. '\
'Sides, vertices, and angles always match!</p></div>'
# 3: Count attributes (reduced from 8 to 5 shapes)
s3 = build_shape_attributes({
"shapes": [
{"name": "Triangle", "sides": 3, "vertices": 3, "blanks": ["sides", "vertices"]},
{"name": "Square", "sides": 4, "vertices": 4, "blanks": ["sides", "vertices"]},
{"name": "Rectangle", "sides": 4, "vertices": 4, "blanks": ["sides", "vertices"]},
{"name": "Star", "sides": 10, "vertices": 10, "blanks": ["sides", "vertices"]},
{"name": "Circle", "sides": 0, "vertices": 0, "blanks": ["sides", "vertices"]},
],
"note": "Count the sides and vertices. Touch each side with your pencil as you count! (Careful: the circle is tricky.)"
})
# 4: NEW — Number Talk: reason WITHOUT counting
s4 = '<div class="abox" style="background:#e3f2fd;">'\
'<p><b>Number Talk</b> — Explain WITHOUT counting.</p>'\
'<div class="mgrid c2">'
s4 += '<div class="abox"><p>A shape has 5 vertices. Without drawing it, how many sides '\
'must it have? How do you know?</p>'\
'<p style="font-size:11px;">My thinking: ' + bw() + '</p></div>'
s4 += '<div class="abox"><p>Can a shape have 4 sides but only 3 vertices? '\
'Explain why or why not.</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. Draw a picture to help!</p>'\
'<div class="abox"><p>Maria is making a garden bed shaped like a triangle. '\
'She puts one fence post at every vertex and one flower in the middle of every side. '\
'How many fence posts and how many flowers does she need in all?</p>'\
'<p style="font-size:11px;">Draw your thinking: ' + bw() + '</p>'\
'<p>Posts: ' + b("3em") + ' Flowers: ' + b("3em") + ' Total: ' + b("3em") + '</p></div></div>'
return page(21, "Monday", "Sides, Vertices & Angles",
f'''{sec(1, "Warm-up: Shape Facts", s1)}
{sec(2, "New Words: Sides, Vertices, Angles", s2)}
{sec(3, "Count Sides & Vertices", s3)}
{sec(4, "Number Talk: Think Without Counting", s4)}
{sec(5, "Word Problem", s5)}''')
# ═══════════════════════════════════════════════════
# TUESDAY — Polygons vs. Non-Polygons
# ═══════════════════════════════════════════════════
def tuesday():
# 1: Fluency warmup
s1 = '<div class="abox" style="background:#e8f5e9;">'\
'<p><b>2-min Fluency Warm-up</b> — Vertices in a flash!</p>'\
'<div class="mgrid c2">'
for name, _ in [("triangle", 3), ("square", 4), ("rectangle", 4), ("hexagon", 6)]:
s1 += f'<div class="abox"><p>A {name} has {b("3em")} vertices.</p></div>'
s1 += '</div></div>'
# 2: NEW — Estimate First!
s2 = '<div class="abox" style="background:#fff3e0;">'\
'<p><b>Estimate First!</b> Glance at each shape for ONE second. '\
'Guess how many sides — then count to check.</p>'\
'<div class="mgrid c2">'
for n in [5, 6, 8, 7]:
s2 += f'<div class="abox"><p style="text-align:center;">{poly_svg(n, ORANGE, 50)}</p>'\
f'<p>My guess: {b("3em")} Actual count: {b("3em")}</p></div>'
s2 += '</div></div>'
# 3: Polygon or not? (reduced from 8 to 6)
s3 = '<p class="note">A <b>polygon</b> is a closed shape made only of straight sides. '\
'If a shape is open or has curves, it is NOT a polygon. Write <b>yes</b> or <b>no</b>.</p>'\
'<div class="mgrid c3">'
items = [
(poly_svg(3, BLUE), "yes"),
(curved_shape_svg(RED), "no"),
(poly_svg(6, GREEN), "yes"),
(open_shape_svg(RED), "no"),
(poly_svg(4, PURPLE), "yes"),
(shape_svg("circle", RED), "no"),
]
for svg, _ in items:
s3 += f'<div class="abox"><p style="text-align:center;">{svg}</p>'\
f'<p>Polygon? {b("3em")}</p></div>'
s3 += '</div>'
# 4: Two ways to explain
s4 = '<div class="abox" style="background:#f3e5f5;">'\
'<p><b>Show Two Different Ways</b> — A circle is not a polygon. '\
'Give two different reasons a shape might fail the polygon test.</p>'\
'<p>Reason 1: ' + bl() + '</p>'\
'<p>Reason 2: ' + bl() + '</p></div>'
# 5: Word problem
s5 = '<div class="wp">'\
'<p class="note">Think about what is happening.</p>'\
'<div class="abox"><p>Jake drew a shape with 4 straight sides, but he forgot to '\
'connect the last side back to the start. His sister says it is a quadrilateral. '\
'Is she right? Explain.</p>'\
'<p style="font-size:11px;">My thinking: ' + bw() + '</p>'\
'<p>Right or wrong? ' + b("5em") + '</p></div></div>'
return page(21, "Tuesday", "Polygons vs. Non-Polygons",
f'''{sec(1, "Warm-up: Vertex Fluency", s1)}
{sec(2, "Estimate First: How Many Sides?", s2)}
{sec(3, "Polygon or Not?", s3)}
{sec(4, "Show Two Different Ways", s4)}
{sec(5, "Word Problem", s5)}''')
# ═══════════════════════════════════════════════════
# WEDNESDAY — Triangles, Quadrilaterals, Pentagons & Hexagons
# ═══════════════════════════════════════════════════
def wednesday():
# 1: Fluency warmup
s1 = '<div class="abox" style="background:#e8f5e9;">'\
'<p><b>2-min Fluency Warm-up</b> — Match the number of sides to the name.</p>'\
'<div class="mgrid c2">'
for sides in [3, 4, 5, 6]:
s1 += f'<div class="abox"><p>{sides} sides = {b("7em")}</p></div>'
s1 += '<p style="font-size:11px; color:#666; grid-column:1 / -1;">Word bank: triangle, quadrilateral, pentagon, hexagon</p>'
s1 += '</div></div>'
# 2: Name that shape (reduced from 8 to 5)
s2 = '<p class="note">Polygons are named by their number of sides: '\
'<b>tri</b>angle = 3, <b>quad</b>rilateral = 4, <b>penta</b>gon = 5, <b>hexa</b>gon = 6. '\
'Name each shape and count its vertices.</p>'\
'<div class="mgrid c3">'
for n, color in [(5, BLUE), (3, GREEN), (6, ORANGE), (4, PURPLE), (5, RED)]:
s2 += f'<div class="abox"><p style="text-align:center;">{poly_svg(n, color, 48)}</p>'\
f'<p>Name: {b("6em")}</p><p>Vertices: {b("3em")}</p></div>'
s2 += '</div>'
# 3: Mixed practice — sort by attribute (two ways)
s3 = '<div class="abox" style="background:#f3e5f5;">'\
'<p><b>Show Two Different Ways</b> — Here are five shapes: '\
'triangle, square, pentagon, rectangle, hexagon.</p>'\
'<p>Way 1 — Sort them into "3 or 4 sides" and "5 or more sides":</p>'\
'<p>3 or 4 sides: ' + bl() + '</p>'\
'<p>5 or more sides: ' + bl() + '</p>'\
'<p>Way 2 — Think of your OWN sorting rule and sort them again:</p>'\
'<p>My rule: ' + bl() + '</p>'\
'<p>Group 1: ' + bw() + ' Group 2: ' + bw() + '</p></div>'
# 4: Word problems (reduced from 3 to 2)
s4 = '<div class="wp">'\
'<p class="note">Read carefully. Think about what is happening. Draw if it helps!</p>'\
'<div class="mgrid c2">'
s4 += '<div class="abox"><p>A stop sign has 8 sides. A yield sign is a triangle. '\
'How many MORE sides does a stop sign have than a yield sign?</p>'\
'<p style="font-size:11px;">My thinking: ' + bw() + '</p>'\
'<p>' + b("3em") + ' more sides</p></div>'
s4 += '<div class="abox"><p>Nora traced 2 pentagons and 1 hexagon. '\
'How many vertices did she trace in all?</p>'\
'<p style="font-size:11px;">My thinking: ' + bw() + '</p>'\
'<p>' + b("3em") + ' vertices</p></div>'
s4 += '</div></div>'
# 5: Challenge — Create your own shape riddle
s5 = '<div class="abox" style="background:#f3e5f5;">'\
'<p><b>Create Your Own Shape Riddle</b></p>'\
'<p>Write a riddle about a polygon. Example: "I have 6 vertices and my name starts '\
'with H. What am I?" Then draw the answer.</p>'\
'<p>My riddle: ' + bl() + '</p>'\
'<p style="min-height:60px; border:1px dashed #999; border-radius:4px; padding:4px;">'\
'<span style="font-size:11px; color:#666;">Draw the answer here:</span></p></div>'
return page(21, "Wednesday", "Naming Polygons: Triangle to Hexagon",
f'''{sec(1, "Warm-up: Sides & Names", s1)}
{sec(2, "Name That Polygon", s2)}
{sec(3, "Sort Shapes Two Different Ways", s3)}
{sec(4, "Word Problems", s4)}
{sec(5, "Challenge: Create Your Own Riddle", s5)}''')
# ═══════════════════════════════════════════════════
# THURSDAY — Drawing Shapes & Real-World Application
# ═══════════════════════════════════════════════════
def thursday():
# 1: Fluency warmup
s1 = '<div class="abox" style="background:#e8f5e9;">'\
'<p><b>2-min Fluency Warm-up</b> — Shape math in your head.</p>'\
'<div class="mgrid c2">'
s1 += f'<div class="abox"><p>Sides on 2 triangles: {b("3em")}</p></div>'
s1 += f'<div class="abox"><p>Vertices on 2 squares: {b("3em")}</p></div>'
s1 += f'<div class="abox"><p>Sides on a pentagon + a triangle: {b("3em")}</p></div>'
s1 += f'<div class="abox"><p>Vertices on 3 triangles: {b("3em")}</p></div>'
s1 += '</div></div>'
# 2: Draw shapes with specific attributes (reduced from 6 to 4)
s2 = '<p class="note">Read the attributes, then draw the shape. Use a ruler for straight sides!</p>'\
'<div class="mgrid c2">'
prompts = [
"Draw a polygon with exactly 3 vertices.",
"Draw a quadrilateral that is NOT a square.",
"Draw a polygon with 5 sides.",
"Draw a closed shape that is NOT a polygon.",
]
for p in prompts:
s2 += f'<div class="abox"><p style="font-size:0.95em;">{p}</p>'\
'<p style="min-height:80px; border:1px dashed #999; border-radius:4px;"></p></div>'
s2 += '</div>'
# 3: Real-world shape hunt word problems
s3 = '<div class="wp">'\
'<p class="note">Shapes are everywhere! Read carefully and think about what is happening.</p>'\
'<div class="mgrid c2">'
s3 += '<div class="abox"><p>A soccer ball pattern uses pentagons and hexagons. '\
'One pentagon and one hexagon are side by side. How many sides do they have altogether?</p>'\
'<p style="font-size:11px;">My thinking: ' + bw() + '</p>'\
'<p>' + b("3em") + ' sides</p></div>'
s3 += '<div class="abox"><p>Dad cuts a square sandwich along one line from corner to corner. '\
'What two shapes does he make? How many vertices does each new shape have?</p>'\
'<p style="font-size:11px;">Draw your thinking: ' + bw() + '</p>'\
'<p>Shapes: ' + bw() + ' Vertices each: ' + b("3em") + '</p></div>'
s3 += '</div></div>'
# 4: NEW — Number Talk
s4 = '<div class="abox" style="background:#e3f2fd;">'\
'<p><b>Number Talk</b> — Explain WITHOUT drawing or counting.</p>'\
'<div class="mgrid c2">'
s4 += '<div class="abox"><p>Which has more vertices in all: 3 triangles or 2 quadrilaterals? '\
'Explain how you know quickly.</p>'\
'<p style="font-size:11px;">My thinking: ' + bw() + '</p></div>'
s4 += '<div class="abox"><p>Rosa says every square is a quadrilateral. Leo says every '\
'quadrilateral is a square. Who is right? Explain.</p>'\
'<p style="font-size:11px;">My thinking: ' + bw() + '</p></div>'
s4 += '</div></div>'
# 5: Create your own
s5 = '<div class="abox" style="background:#f3e5f5;">'\
'<p><b>Create Your Own Shape Picture</b></p>'\
'<p>Draw a picture (a robot, house, rocket...) using at least 1 triangle, '\
'1 quadrilateral, and 1 hexagon. Then count your shapes.</p>'\
'<p style="min-height:90px; border:1px dashed #999; border-radius:4px;"></p>'\
'<p>Triangles: ' + b("3em") + ' Quadrilaterals: ' + b("3em") + ' Hexagons: ' + b("3em") + '</p></div>'
return page(21, "Thursday", "Drawing Shapes & Real-World Shapes",
f'''{sec(1, "Warm-up: Shape Math", s1)}
{sec(2, "Draw the Shape", s2)}
{sec(3, "Real-World Word Problems", s3)}
{sec(4, "Number Talk: Squares & Quadrilaterals", s4)}
{sec(5, "Challenge: Create Your Own", 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> — Go fast!</p>'\
'<div class="mgrid c2">'
s1 += f'<div class="abox"><p>Sides on a hexagon: {b("3em")}</p></div>'
s1 += f'<div class="abox"><p>Vertices on a pentagon: {b("3em")}</p></div>'
s1 += f'<div class="abox"><p>A shape with 4 sides is called a: {b("8em")}</p></div>'
s1 += f'<div class="abox"><p>Vertices on a triangle: {b("3em")}</p></div>'
s1 += '</div></div>'
# 2: Estimate First! review
s2 = '<div class="abox" style="background:#fff3e0;">'\
'<p><b>Estimate First!</b> Glance for one second — guess, then count.</p>'\
'<div class="mgrid c2">'
for n in [7, 8]:
s2 += f'<div class="abox"><p style="text-align:center;">{poly_svg(n, ORANGE, 54)}</p>'\
f'<p>Guess: {b("3em")} Count: {b("3em")}</p></div>'
s2 += '</div></div>'
# 3: Mixed review table (reduced from 8 to 4)
s3 = build_shape_attributes({
"shapes": [
{"name": "Triangle", "sides": 3, "vertices": 3, "blanks": ["sides", "vertices"]},
{"name": "Square", "sides": 4, "vertices": 4, "blanks": ["sides", "vertices"]},
{"name": "Rectangle", "sides": 4, "vertices": 4, "blanks": ["sides", "vertices"]},
{"name": "Circle", "sides": 0, "vertices": 0, "blanks": ["sides", "vertices"]},
],
"note": "Review: fill in the sides and vertices. Use any strategy you prefer."
})
# 4: Word problem review
s4 = '<div class="wp">'\
'<p class="note">Read carefully. Show your work.</p>'\
'<div class="abox"><p>Emma is decorating a hexagon-shaped mirror. She glues one gem '\
'at each vertex and one ribbon on each side. Gems cost 2 dollars each. '\
'How many gems does she need, and how much do they cost in all?</p>'\
'<p style="font-size:11px;">Draw or write your thinking: ' + bw() + '</p>'\
'<p>Gems: ' + b("3em") + ' Cost: ' + b("4em") + ' dollars</p></div></div>'
# 5: Self-check with emoji reflection
s5 = '<div class="selfcheck">'\
'<p><b>Self-Check: How do you feel about 2D shapes?</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>Counting Sides & Vertices</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>Naming Polygons (triangle → hexagon)</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>Drawing Shapes with Attributes</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 shape and why:</b> ' + bl() + '</p>'\
'</div>'
return page(21, "Friday", "Week 21 Review & Self-Check",
f'''{sec(1, "Warm-up: Shape Facts", s1)}
{sec(2, "Estimate First!", s2)}
{sec(3, "Sides & Vertices Review", s3)}
{sec(4, "Word Problem", s4)}
{sec(5, "Self-Check & Reflection", s5)}''')
# ═══════════════════════════════════════════════════
# TEACHER GUIDE
# ═══════════════════════════════════════════════════
def teacher_guide():
return tg_page_multi(
"Week 21", "2D Shapes & Attributes — Teacher Guide",
tg_section_title("Monday: Sides, Vertices & Angles") +
tg_sec(1, "Warm-up Sides", ["triangle 3, square 4, rectangle 4, circle 0"]) +
tg_sec(2, "New Words", ["Vocabulary intro — no answers needed"]) +
tg_sec(3, "Count Attributes", ["Triangle 3/3, Square 4/4, Rectangle 4/4, Star 10/10, Circle 0/0"]) +
tg_sec(4, "Number Talk", ["5 sides — sides always equal vertices in a polygon", "No — every side needs a corner to connect to the next"]) +
tg_sec(5, "Word Problem", ["3 posts + 3 flowers = 6 in all"]) +
tg_note("Page break"),
tg_section_title("Tuesday: Polygons vs. Non-Polygons") +
tg_sec(1, "Warm-up Vertices", ["3, 4, 4, 6"]) +
tg_sec(2, "Estimate First", ["Shapes have 5, 6, 8, 7 sides — accept close guesses"]) +
tg_sec(3, "Polygon or Not", ["yes, no (curved), yes, no (open), yes, no (circle)"]) +
tg_sec(4, "Two Reasons", ["Has curves; is not closed (open shape)"]) +
tg_sec(5, "Word Problem", ["Wrong — shape must be CLOSED to be a quadrilateral"]) +
tg_note("Page break"),
tg_section_title("Wednesday: Naming Polygons") +
tg_sec(1, "Warm-up Names", ["3=triangle, 4=quadrilateral, 5=pentagon, 6=hexagon"]) +
tg_sec(2, "Name That Polygon", ["pentagon/5, triangle/3, hexagon/6, quadrilateral/4, pentagon/5"]) +
tg_sec(3, "Sort Two Ways", ["3-4: triangle, square, rectangle; 5+: pentagon, hexagon. Way 2: accept any consistent rule"]) +
tg_sec(4, "Word Problems", ["8−3=5 more sides", "5+5+6=16 vertices"]) +
tg_sec(5, "Riddle", ["Accept any correct riddle + drawing"]) +
tg_note("Page break"),
tg_section_title("Thursday: Drawing Shapes & Real-World") +
tg_sec(1, "Warm-up Shape Math", ["6, 8, 8, 9"]) +
tg_sec(2, "Draw the Shape", ["Any triangle; any non-square quadrilateral; any pentagon; e.g. circle or oval"]) +
tg_sec(3, "Real-World", ["5+6=11 sides", "Two triangles, 3 vertices each"]) +
tg_sec(4, "Number Talk", ["3 triangles: 9 vertices > 2 quads: 8", "Rosa is right — all squares have 4 sides; not all 4-sided shapes are squares"]) +
tg_sec(5, "Create Own", ["Accept any picture with required shapes counted"]) +
tg_note("Page break"),
tg_section_title("Friday: Review & Self-Check") +
tg_sec(1, "Warm-up", ["6, 5, quadrilateral, 3"]) +
tg_sec(2, "Estimate First", ["7 sides, 8 sides"]) +
tg_sec(3, "Review Table", ["3/3, 4/4, 4/4, 0/0"]) +
tg_sec(4, "Word Problem", ["6 gems × 2 = 12 dollars"]) +
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", "Sides, Vertices & Angles"),
(tuesday, "Tuesday", "Polygons vs. Non-Polygons"),
(wednesday, "Wednesday", "Naming Polygons: Triangle to Hexagon"),
(thursday, "Thursday", "Drawing Shapes & Real-World Shapes"),
(friday, "Friday", "Week 21 Review & Self-Check"),
]
for lesson_func, day, title in days:
html = lesson_func()
fname = f"Week_21_{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_21_Teacher_Guide.html")
with open(path, "w") as f:
f.write(html)
pdf_path = os.path.join(WEEK, "Week_21_Teacher_Guide.pdf")
HTML(filename=path).write_pdf(pdf_path)
print(f"Generated: {pdf_path}")
print("\nWeek 21 (new standard) complete!")