""" Generate the Lesson 4 EmotionMap Word fallback template. The primary submission route is the single-page PDF the toy app exports. This Word document is the paper fallback for students who cannot run the app — six labelled slots in a 2x3 grid, a Format checkbox, the privacy confirm, and a short reflection prompt. Run with: uv run python scratch/build_emotionmap_template.py """ from __future__ import annotations from pathlib import Path from docx import Document from docx.enum.table import WD_ALIGN_VERTICAL from docx.shared import Cm, Pt, RGBColor REPO_ROOT = Path(__file__).resolve().parent.parent # The Word doc is a teaching-bundle artefact, not a Space asset. # Default output is the lesson folder; override with $TOTES_TEMPLATE_OUT # if running from a different machine. DEFAULT_OUT = ( Path.home() / "Code/teaching/online-teaching-for-bence" / "Lesson4-EmotionMap-template.docx" ) OUT_PATH = Path( __import__("os").environ.get("TOTES_TEMPLATE_OUT", DEFAULT_OUT) ) GREY = RGBColor(0x55, 0x55, 0x55) EMOTIONS = ["Happy", "Sad", "Fear", "Disgust", "Anger", "Surprise"] def add_heading(doc, text, level): return doc.add_heading(text, level=level) def add_para(doc, text, italic=False, grey=False, bold=False, size=None): p = doc.add_paragraph() run = p.add_run(text) run.italic = italic run.bold = bold if grey: run.font.color.rgb = GREY if size is not None: run.font.size = Pt(size) return p def add_instruction(doc, text): add_para(doc, text, italic=True, grey=True, size=10) def add_kv_table(doc, fields): tbl = doc.add_table(rows=1 + len(fields), cols=2) tbl.style = "Table Grid" hdr = tbl.rows[0].cells hdr[0].text = "Field" hdr[1].text = "Your entry" for cell in hdr: for p in cell.paragraphs: for run in p.runs: run.bold = True for i, field in enumerate(fields, start=1): tbl.rows[i].cells[0].text = field tbl.rows[i].cells[1].text = "" doc.add_paragraph() def add_replication_grid(doc): """2x3 grid of slot cells. Each cell has the emotion name, a paste-image placeholder, and a one-line classifier-result field.""" tbl = doc.add_table(rows=2, cols=3) tbl.style = "Table Grid" for row in range(2): for col in range(3): idx = row * 3 + col emo = EMOTIONS[idx] cell = tbl.rows[row].cells[col] cell.vertical_alignment = WD_ALIGN_VERTICAL.TOP cell.text = "" # Slot number and intended emotion p_title = cell.paragraphs[0] r_title = p_title.add_run(f"{idx + 1}. {emo.upper()}") r_title.bold = True r_title.font.size = Pt(11) # Image placeholder p_img = cell.add_paragraph() r_img = p_img.add_run("[paste face or wireframe here]") r_img.italic = True r_img.font.color.rgb = GREY r_img.font.size = Pt(9) # Classifier line p_cls = cell.add_paragraph() r_cls = p_cls.add_run("Classifier said: ____________ (___ .__ )") r_cls.font.size = Pt(9) # Verdict p_v = cell.add_paragraph() r_v = p_v.add_run("Agreed / Disagreed (circle one)") r_v.font.size = Pt(9) r_v.font.color.rgb = GREY doc.add_paragraph() def build(): doc = Document() for section in doc.sections: section.top_margin = Cm(1.5) section.bottom_margin = Cm(1.5) section.left_margin = Cm(1.8) section.right_margin = Cm(1.8) doc.add_heading("Lesson 4 EmotionMap — six-emotion replication challenge", level=0) add_para( doc, "Pose each of the six basic emotions in turn. Note what the toy " "app's classifier read on each attempt. This Word document is " "the paper fallback — the primary submission is the single-page " "PDF exported from the app.", italic=True, grey=True, ) add_heading(doc, "Your details", 1) add_kv_table(doc, ["Name", "Student number", "Format chosen (A or B)", "Date"]) add_instruction( doc, "Format A — Face: paste cropped face thumbnails in each slot. " "Format B — Wireframe: paste the anonymised landmark wireframes " "the app generates when Wireframe mode is on. Both formats " "carry equal weight.", ) add_heading(doc, "Replication grid", 1) add_replication_grid(doc) add_heading(doc, "Privacy ground rules — confirm", 1) add_para(doc, "[ ] I chose my format before I started posing.") add_para(doc, "[ ] I understand that the toy app uploads nothing " "beyond the model inference call and stores nothing on " "a server.") add_para(doc, "[ ] (Format A only) I consent to my tutor reviewing " "my face-bearing captures.") add_heading(doc, "Reflection (2–3 sentences)", 1) add_para(doc, "Pick ONE of the prompts and answer in 2–3 sentences.") add_para( doc, "1. Which of the six basic emotions was easiest for the " "classifier to recognise on your face? Which was hardest? What " "do you think drives that gap — your face, the model, or both?", ) add_para( doc, "2. Look across your six slots. Are some emotions consistently " "recognised, and others much more variable? What does that " "variability look like for you, and how might it differ for " "someone else in the cohort?", ) add_para( doc, "3. Did the classifier ever pick a different top emotion from " "the one you intended? When it did, what did it pick instead — " "and what does that suggest about how 'readable' a felt state " "is on a face?", ) # blank space for the reflection answer rtbl = doc.add_table(rows=5, cols=1) rtbl.style = "Table Grid" for r in rtbl.rows: r.cells[0].text = "" doc.save(OUT_PATH) print(f"Wrote {OUT_PATH}") if __name__ == "__main__": build()