#!/usr/bin/env python3
"""
Week 30: Multiplication Facts 6-10
Phase 5: Multiplication Introduction
Standards: 2.OA.A.1 — operations; times tables 6-10, commutative property,
strategies for harder facts (9s finger trick, 6s = 3s doubled)
New Standard:
- Reduced drill (4-6 problems per section)
- Estimation before calculating
- Multiple strategies (commutative flip, doubling, 9s trick)
- Better word problems (reasoning, real-world contexts)
- Open-ended challenges (Create Your Own)
- Number talks (explain WITHOUT calculating)
- Friday self-check with emoji reflection
"""
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,
array_svg, eq_group_svg, grid_svg,
star_svg, medal_svg, trophy_svg, check_svg,
times_table_html, skip_count_html, mini_chart_html,
)
from activity_builders import (
build_equal_groups,
build_skip_counting,
build_times_table,
build_challenge_box,
build_two_step_problems,
)
BASE = os.path.expanduser("~/Home_School/2nd_Grade/Math")
WEEK = os.path.join(BASE, "Week_30")
# ═══════════════════════════════════════════════════
# MONDAY — The Turn-Around Trick (Commutative Property)
# ═══════════════════════════════════════════════════
def monday():
# 1: Fluency warmup — facts you already know (0-5)
s1 = '<div class="abox" style="background:#e8f5e9;">'\
'<p><b>2-min Fluency Warm-up</b> — Facts you already know!</p>'\
'<div class="mgrid c3">'
for f in ["5 × 4", "3 × 6", "2 × 8", "4 × 5", "5 × 7", "3 × 9"]:
s1 += f'<div class="abox"><p>{f} = {b("3em")}</p></div>'
s1 += '</div></div>'
# 2: Commutative property — see it with arrays (guided)
s2 = '<p class="note"><b>The Turn-Around Trick!</b> When you flip the numbers, '\
'the answer stays the same. 6 × 3 = 3 × 6. Look — it\'s the same array, just turned!</p>'\
'<div class="mgrid c2">'
s2 += f'<div class="abox"><p>3 rows of 6</p>{array_svg(3, 6, width="110")}'\
f'<p>3 × 6 = {b("3em")}</p></div>'
s2 += f'<div class="abox"><p>6 rows of 3</p>{array_svg(6, 3, width="60")}'\
f'<p>6 × 3 = {b("3em")}</p></div>'
s2 += '</div>'\
'<p class="note">So if you know your 0-5 facts, you ALREADY know most 6-10 facts!</p>'
# 3: Use the turn-around trick (reduced from 8 to 4)
s3 = '<p class="note">Flip each hard fact into an easy fact you already know, then solve.</p>'\
'<div class="mgrid c2">'
for a, c in [(7, 2), (8, 5), (9, 3), (6, 4)]:
s3 += f'<div class="abox"><p><b>{a} × {c}</b> → flip it: {b("3em")} × {b("3em")}</p>'\
f'<p>Answer: {b("3em")}</p></div>'
s3 += '</div>'
# 4: Number Talk — why flipping works
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>Kim knows 5 × 8 = 40 but is stuck on 8 × 5.</p>'\
'<p>What would you tell Kim?</p>'\
'<p style="font-size:11px;">My thinking: ' + bw() + '</p></div>'
s4 += '<div class="abox"><p>Does the turn-around trick work for SUBTRACTION? Is 8 − 5 the same as 5 − 8?</p>'\
'<p style="font-size:11px;">My thinking: ' + bw() + '</p></div>'
s4 += '</div></div>'
# 5: Challenge
s5 = build_challenge_box({
"problems": [
{"text": "Draw two arrays that prove 4 × 7 = 7 × 4 in the space below.<br><span style='display:block; min-height:80px; border:1px dashed #999; border-radius:4px;'></span>"},
{"text": "Write two more turn-around pairs of your own: " + bw("10em") + " and " + bw("10em")},
]
})
return page(30, "Monday", "The Turn-Around Trick",
f'''{sec(1, "Warm-up: Facts You Know", s1)}
{sec(2, "The Turn-Around Trick (Commutative Property)", s2)}
{sec(3, "Flip It to Solve It", s3)}
{sec(4, "Number Talk: Why Flipping Works", s4)}
{sec(5, "Challenge: Prove It", s5)}''')
# ═══════════════════════════════════════════════════
# TUESDAY — The 6s and 7s
# ═══════════════════════════════════════════════════
def tuesday():
# 1: Fluency warmup — skip count by 6s and 7s
s1 = '<div class="abox" style="background:#e8f5e9;">'\
'<p><b>2-min Fluency Warm-up</b> — Skip count. Fill in the missing numbers.</p>'
s1 += build_skip_counting({
"sequences": [
{"start": 6, "step": 6, "count": 8, "blank_indices": [3, 5, 7]},
{"start": 7, "step": 7, "count": 8, "blank_indices": [2, 4, 6]},
],
"note": "Count by 6s, then by 7s."
})
s1 += '</div>'
# 2: 6s strategy — double the 3s!
s2 = '<p class="note"><b>Strategy: 6s are 3s doubled!</b> To find 6 × 4: '\
'first find 3 × 4 = 12, then double it: 24. Try it:</p>'\
'<div class="mgrid c2">'
for n in [5, 3, 7, 6]:
s2 += f'<div class="abox"><p><b>6 × {n}</b></p>'\
f'<p>3 × {n} = {b("5em")}</p>'\
f'<p>Double it: {b("5em")}</p>'\
f'<p>6 × {n} = {b("3em")}</p></div>'
s2 += '</div>'
# 3: Estimate First! — 6s and 7s
s3 = '<div class="abox" style="background:#fff3e0;">'\
'<p><b>Estimate First!</b> Circle MORE or LESS than 30, then solve.</p>'\
'<div class="mgrid c2">'
for g, n in [(6, 4), (7, 5), (6, 6), (7, 3)]:
s3 += f'<div class="abox"><p><b>{g} × {n}</b></p>'\
'<p>more than 30 / less than 30</p>'\
f'<p>Actual: {b("3em")}</p></div>'
s3 += '</div></div>'
# 4: 7s times table with blanks (reduced drill)
s4 = build_times_table({
"factor": 7,
"blanks": [3, 5, 7, 9],
"note": "Fill in the missing 7s facts. Hint: use the turn-around trick for ones you know!"
})
# 5: Two strategies for one fact
s5 = '<div class="abox" style="background:#f3e5f5;">'\
'<p><b>Show Two Different Ways</b> — Solve <b>6 × 5</b> two ways.</p>'\
'<p>Way 1 (turn-around trick): flip to 5 × 6 and skip count by 5: ' + bw("12em") + '</p>'\
'<p>Way 2 (double the 3s): 3 × 5 = ___, doubled = ___</p>'\
'<p>6 × 5 = ' + b("3em") + '</p>'\
'<p style="font-size:11px; color:#666;">Which strategy do you like best? Why? ' + bw() + '</p>'\
'</div>'
return page(30, "Tuesday", "The 6s and 7s",
f'''{sec(1, "Warm-up: Skip Count by 6s and 7s", s1)}
{sec(2, "Strategy: 6s are 3s Doubled", s2)}
{sec(3, "Estimate First!", s3)}
{sec(4, "The 7s Times Table", s4)}
{sec(5, "Show Two Different Ways", s5)}''')
# ═══════════════════════════════════════════════════
# WEDNESDAY — The 8s and 9s (with the 9s Trick!)
# ═══════════════════════════════════════════════════
def wednesday():
# 1: Fluency warmup — skip count by 8s and 9s
s1 = '<div class="abox" style="background:#e8f5e9;">'\
'<p><b>2-min Fluency Warm-up</b> — Skip count. Fill in the missing numbers.</p>'
s1 += build_skip_counting({
"sequences": [
{"start": 8, "step": 8, "count": 8, "blank_indices": [3, 5, 7]},
{"start": 9, "step": 9, "count": 8, "blank_indices": [2, 4, 6]},
],
"note": "Count by 8s, then by 9s."
})
s1 += '</div>'
# 2: The amazing 9s pattern
s2 = '<p class="note"><b>The 9s Secret!</b> Look at the 9s: 9, 18, 27, 36, 45... '\
'The two digits of every answer <b>add up to 9</b>! (1+8=9, 2+7=9, 3+6=9). '\
'And the tens digit is always ONE LESS than the number you multiplied. Try it:</p>'\
'<div class="mgrid c2">'
for n in [4, 6, 8, 7]:
s2 += f'<div class="abox"><p><b>9 × {n}</b></p>'\
f'<p>Tens digit (one less than {n}): {b("3em")}</p>'\
f'<p>Ones digit (digits add to 9): {b("3em")}</p>'\
f'<p>9 × {n} = {b("3em")}</p></div>'
s2 += '</div>'
# 3: 8s strategy — double, double, double!
s3 = '<p class="note"><b>Strategy: 8s are triple-doubles!</b> To find 8 × 3: '\
'double 3 (=6), double again (=12), double once more (=24). Try it:</p>'\
'<div class="mgrid c2">'
for n in [4, 2, 5, 3]:
s3 += f'<div class="abox"><p><b>8 × {n}</b></p>'\
f'<p>Double {n}: {b("5em")} → Double: {b("5em")} → Double: {b("5em")}</p>'\
f'<p>8 × {n} = {b("3em")}</p></div>'
s3 += '</div>'
# 4: Word problems with 8s and 9s (reduced to 2)
s4 = '<div class="wp">'\
'<p class="note">Write the multiplication sentence and solve. Use any strategy!</p>'\
'<div class="mgrid c2">'
problems = [
("An octopus has 8 arms. How many arms do 3 octopuses have?", "arms"),
("A baseball team has 9 players. How many players are on 4 teams?", "players"),
]
for text, unit in problems:
s4 += f'<div class="abox"><p style="font-size:0.95em;">{text}</p>'\
'<p>Multiplication sentence: ' + bw("9em") + '</p>'\
'<p>' + b("4em") + f' {unit}</p></div>'
s4 += '</div></div>'
# 5: Number Talk — check with the 9s secret
s5 = '<div class="abox" style="background:#e3f2fd;">'\
'<p><b>Number Talk</b> — Explain WITHOUT multiplying.</p>'\
'<div class="mgrid c2">'
s5 += '<div class="abox"><p>Tom says 9 × 6 = 56.</p>'\
'<p>Use the 9s secret to check — could 56 be right?</p>'\
'<p style="font-size:11px;">My thinking: ' + bw() + '</p></div>'
s5 += '<div class="abox"><p>Which is easier for YOU: 8 × 2 or 2 × 8? Why?</p>'\
'<p style="font-size:11px;">My thinking: ' + bw() + '</p></div>'
s5 += '</div></div>'
return page(30, "Wednesday", "The 8s and 9s",
f'''{sec(1, "Warm-up: Skip Count by 8s and 9s", s1)}
{sec(2, "The 9s Secret", s2)}
{sec(3, "Strategy: 8s are Triple-Doubles", s3)}
{sec(4, "Word Problems", s4)}
{sec(5, "Number Talk: Check It!", s5)}''')
# ═══════════════════════════════════════════════════
# THURSDAY — The 10s & Mixed Word Problems
# ═══════════════════════════════════════════════════
def thursday():
# 1: Fluency warmup — 10s (the easiest!)
s1 = '<div class="abox" style="background:#e8f5e9;">'\
'<p><b>2-min Fluency Warm-up</b> — 10s are easy: just add a zero!</p>'\
'<div class="mgrid c3">'
for f in ["10 × 3", "10 × 7", "10 × 5", "10 × 9", "10 × 6", "10 × 10"]:
s1 += f'<div class="abox"><p>{f} = {b("3em")}</p></div>'
s1 += '</div></div>'
# 2: Better word problems (reduced to 3)
s2 = '<div class="wp">'\
'<p class="note">Underline the important numbers. Pick a strategy, write the sentence, and solve.</p>'\
'<div class="mgrid c2">'
problems = [
("A box of crayons has 8 crayons. The art teacher buys 4 boxes. How many crayons did she buy?", "crayons"),
("There are 7 days in a week. How many days are in 3 weeks?", "days"),
("A dime is worth 10 cents. Maria has 6 dimes. How many cents does she have?", "cents"),
]
for text, unit in problems:
s2 += f'<div class="abox"><p style="font-size:0.95em;">{text}</p>'\
'<p>Multiplication sentence: ' + bw("9em") + '</p>'\
'<p>' + b("4em") + f' {unit}</p></div>'
s2 += '</div></div>'
# 3: Two-step problem
s3 = '<div class="abox">'\
'<p class="note">Solve step by step.</p>'\
+ build_two_step_problems({
"problems": [
{
"text": "A garden has 6 rows of 6 carrots. Rabbits eat 10 carrots. How many carrots are left in the garden?",
"blanks": 2
}
],
"note": "Step 1: multiply to find the total. Step 2: subtract."
}) + '</div>'
# 4: Number Talk — pick the smart strategy
s4 = '<div class="abox" style="background:#e3f2fd;">'\
'<p><b>Number Talk</b> — Don\'t solve! Just tell WHICH strategy you would pick and why.</p>'\
'<div class="mgrid c2">'
s4 += '<div class="abox"><p><b>9 × 7</b></p>'\
'<p>9s secret? Turn-around? Skip count?</p>'\
'<p style="font-size:11px;">I would use: ' + bw() + '</p></div>'
s4 += '<div class="abox"><p><b>6 × 8</b></p>'\
'<p>Double the 3s? Triple-double the 8s?</p>'\
'<p style="font-size:11px;">I would use: ' + bw() + '</p></div>'
s4 += '</div></div>'
# 5: Create your own
s5 = '<div class="abox" style="background:#f3e5f5;">'\
'<p><b>Create Your Own Word Problem</b></p>'\
'<p>Write a word problem using a 6, 7, 8, 9, or 10 fact. '\
'Ideas: spider legs (8), days in a week (7), fingers (10)... Then solve it.</p>'\
'<p style="min-height:60px; border-bottom:1px solid #333;"></p>'\
'<p style="min-height:30px; border-bottom:1px solid #333;"></p>'\
'<p>Multiplication sentence: ' + bw("9em") + '</p>'\
'<p>Answer: ' + bl() + '</p>'\
'</div>'
return page(30, "Thursday", "The 10s & Mixed Word Problems",
f'''{sec(1, "Warm-up: The Easy 10s", s1)}
{sec(2, "Word Problems", s2)}
{sec(3, "Two-Step Problem", s3)}
{sec(4, "Number Talk: Pick Your Strategy", s4)}
{sec(5, "Challenge: Create Your Own", s5)}''')
# ═══════════════════════════════════════════════════
# FRIDAY — Review & Self-Check
# ═══════════════════════════════════════════════════
def friday():
# 1: Fluency warmup — mixed 6-10 facts
s1 = '<div class="abox" style="background:#e8f5e9;">'\
'<p><b>2-min Fluency Warm-up</b> — Show what you know! Use any strategy.</p>'\
'<div class="mgrid c3">'
for f in ["6 × 4", "7 × 3", "8 × 5", "9 × 2", "10 × 8", "6 × 6"]:
s1 += f'<div class="abox"><p>{f} = {b("3em")}</p></div>'
s1 += '</div></div>'
# 2: Estimate First!
s2 = '<div class="abox" style="background:#fff3e0;">'\
'<p><b>Estimate First!</b> Circle MORE or LESS than 40, then solve.</p>'\
'<div class="mgrid c2">'
for g, n in [(6, 5), (9, 5), (7, 7), (8, 4)]:
s2 += f'<div class="abox"><p><b>{g} × {n}</b></p>'\
'<p>more than 40 / less than 40</p>'\
f'<p>Actual: {b("3em")}</p></div>'
s2 += '</div></div>'
# 3: Strategy match-up review
s3 = '<div class="abox">'\
'<p class="note">Solve each fact AND name the strategy you used '\
'(turn-around, 9s secret, doubling, skip count, add a zero).</p>'\
'<div class="mgrid c2">'
for f in ["9 × 8", "6 × 3", "10 × 7", "8 × 3"]:
s3 += f'<div class="abox"><p><b>{f}</b> = {b("3em")}</p>'\
'<p style="font-size:11px;">Strategy I used: ' + bw() + '</p></div>'
s3 += '</div></div>'
# 4: Word problem review (reduced to 2)
s4 = '<div class="wp">'\
'<p class="note">Write the multiplication sentence and solve.</p>'\
'<div class="mgrid c2">'
problems = [
("A carton holds 6 eggs. How many eggs are in 5 cartons?", "eggs"),
("Each pack of trading cards has 9 cards. Kai buys 3 packs. How many cards does he get?", "cards"),
]
for text, unit in problems:
s4 += f'<div class="abox"><p style="font-size:0.95em;">{text}</p>'\
'<p>Multiplication sentence: ' + bw("9em") + '</p>'\
'<p>' + b("4em") + f' {unit}</p></div>'
s4 += '</div></div>'
# 5: Self-check with emoji reflection + celebration
s5 = '<div class="selfcheck">'\
f'<p>{trophy_svg()} <b>You finished ALL the times tables 0-10! Self-Check time:</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>Turn-Around Trick</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>6s, 7s, 8s Facts</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>9s Secret & 10s</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>My favorite multiplication strategy:</b> ' + bl() + '</p>'\
'<p><b>The fact I want to practice more:</b> ' + b("6em") + '</p>'\
'</div>'
return page(30, "Friday", "Week 30 Review & Self-Check",
f'''{sec(1, "Warm-up: Mixed Facts", s1)}
{sec(2, "Estimate First!", s2)}
{sec(3, "Strategy Match-Up", s3)}
{sec(4, "Word Problems", s4)}
{sec(5, "Self-Check & Celebration", s5)}''')
# ═══════════════════════════════════════════════════
# TEACHER GUIDE — Combined for all days
# ═══════════════════════════════════════════════════
def teacher_guide():
return tg_page_multi(
"Week 30", "Multiplication Facts 6-10 — Teacher Guide",
# Monday
tg_section_title("Monday: The Turn-Around Trick") +
tg_sec(1, "Warm-up", ["20, 18, 16, 20, 35, 27"]) +
tg_sec(2, "Commutative", ["Both arrays = 18 — same dots, turned sideways"]) +
tg_sec(3, "Flip It", ["7×2→2×7=14; 8×5→5×8=40; 9×3→3×9=27; 6×4→4×6=24"]) +
tg_sec(4, "Number Talk", ["8×5 = 5×8 = 40 (turn-around)", "No! Subtraction is NOT commutative"]) +
tg_sec(5, "Challenge", ["4×7 and 7×4 arrays both show 28; accept any valid pairs"]) +
tg_note("Page break"),
# Tuesday
tg_section_title("Tuesday: 6s and 7s") +
tg_sec(1, "Warm-up", ["6s blanks: 24, 36, 48 | 7s blanks: 21, 35, 49"]) +
tg_sec(2, "6s = 3s doubled", ["6×5: 15→30; 6×3: 9→18; 6×7: 21→42; 6×6: 18→36"]) +
tg_sec(3, "Estimate First", ["6×4=24 less; 7×5=35 more; 6×6=36 more; 7×3=21 less"]) +
tg_sec(4, "7s Table blanks", ["7×3=21, 7×5=35, 7×7=49, 7×9=63"]) +
tg_sec(5, "Two Ways", ["Skip count: 5,10,15,20,25,30; 3×5=15 doubled=30; answer 30"]) +
tg_note("Page break"),
# Wednesday
tg_section_title("Wednesday: 8s and 9s") +
tg_sec(1, "Warm-up", ["8s blanks: 32, 48, 64 | 9s blanks: 27, 45, 63"]) +
tg_sec(2, "9s Secret", ["9×4=36 (3,6); 9×6=54 (5,4); 9×8=72 (7,2); 9×7=63 (6,3)"]) +
tg_sec(3, "8s Triple-Double", ["8×4: 8→16→32; 8×2: 4→8→16; 8×5: 10→20→40; 8×3: 6→12→24"]) +
tg_sec(4, "Word Problems", ["3×8=24 arms; 4×9=36 players"]) +
tg_sec(5, "Number Talk", ["56: 5+6=11≠9, so wrong (9×6=54)", "Accept either — same answer by turn-around"]) +
tg_note("Page break"),
# Thursday
tg_section_title("Thursday: 10s & Mixed Word Problems") +
tg_sec(1, "Warm-up 10s", ["30, 70, 50, 90, 60, 100"]) +
tg_sec(2, "Word Problems", ["4×8=32 crayons; 3×7=21 days; 6×10=60 cents"]) +
tg_sec(3, "Two-Step", ["Step 1: 6×6=36. Step 2: 36−10=26 carrots"]) +
tg_sec(4, "Number Talk", ["9×7: any valid strategy (9s secret gives 63)", "6×8: any valid strategy (48)"]) +
tg_sec(5, "Create Own", ["Accept student problems with a 6-10 fact"]) +
tg_note("Page break"),
# Friday
tg_section_title("Friday: Review & Self-Check") +
tg_sec(1, "Warm-up", ["24, 21, 40, 18, 80, 36"]) +
tg_sec(2, "Estimate First", ["6×5=30 less; 9×5=45 more; 7×7=49 more; 8×4=32 less"]) +
tg_sec(3, "Strategy Match", ["9×8=72; 6×3=18; 10×7=70; 8×3=24 — any valid strategy name"]) +
tg_sec(4, "Word Problems", ["5×6=30 eggs; 3×9=27 cards"]) +
tg_sec(5, "Self-Check", ["Accept student self-assessment; celebrate finishing 0-10!"])
)
# ═══════════════════════════════════════════════════
# 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", "The Turn-Around Trick"),
(tuesday, "Tuesday", "The 6s and 7s"),
(wednesday, "Wednesday", "The 8s and 9s"),
(thursday, "Thursday", "The 10s & Mixed Word Problems"),
(friday, "Friday", "Week 30 Review & Self-Check"),
]
for lesson_func, day, title in days:
html = lesson_func()
fname = f"Week_30_{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_30_Teacher_Guide.html")
with open(path, "w") as f:
f.write(html)
pdf_path = os.path.join(WEEK, "Week_30_Teacher_Guide.pdf")
HTML(filename=path).write_pdf(pdf_path)
print(f"Generated: {pdf_path}")
print("\nWeek 30 (Multiplication Facts 6-10) complete!")