#!/usr/bin/env python3
"""Build: The Internship Toolkit - Course Design Document v1.2 (the certification client schools).
Revision focus: tuned for students aged 15-17 - gentler on-ramp, one deliverable
per lesson, fixed session ritual, softened panel, twelve-pass wargame record."""
from reportlab.lib.pagesizes import A4
from reportlab.lib.units import mm
from reportlab.lib.colors import HexColor, white
from reportlab.lib.styles import ParagraphStyle
from reportlab.lib.enums import TA_LEFT
from reportlab.platypus import (BaseDocTemplate, PageTemplate, Frame, Paragraph,
Spacer, Table, TableStyle, KeepTogether)
FOREST = HexColor('#1B4332')
DEEP = HexColor('#0B2A1C')
GOLD = HexColor('#B08425')
CREAM = HexColor('#F4F7F4')
INK = HexColor('#1A1A1A')
OUT = '/mnt/user-data/outputs/PECB_Internship_Toolkit_Course_v1.2.pdf'
S = {}
S['h1'] = ParagraphStyle('h1', fontName='Helvetica-Bold', fontSize=13.5, leading=17,
textColor=DEEP, spaceBefore=14, spaceAfter=5)
S['h2'] = ParagraphStyle('h2', fontName='Helvetica-Bold', fontSize=11, leading=14,
textColor=FOREST, spaceBefore=9, spaceAfter=3)
S['body'] = ParagraphStyle('body', fontName='Helvetica', fontSize=9.8, leading=13.6,
textColor=INK, spaceAfter=5)
S['bullet'] = ParagraphStyle('bullet', parent=S['body'], leftIndent=14,
bulletIndent=4, spaceAfter=3)
S['cell'] = ParagraphStyle('cell', fontName='Helvetica', fontSize=8.8, leading=11.6,
textColor=INK)
S['cellb'] = ParagraphStyle('cellb', fontName='Helvetica-Bold', fontSize=8.8,
leading=11.6, textColor=INK)
S['cellh'] = ParagraphStyle('cellh', fontName='Helvetica-Bold', fontSize=8.8,
leading=11.6, textColor=white)
S['tag'] = ParagraphStyle('tag', fontName='Helvetica-Bold', fontSize=8.4, leading=11,
textColor=GOLD, spaceBefore=2, spaceAfter=1)
S['note'] = ParagraphStyle('note', fontName='Helvetica-Oblique', fontSize=8.8,
leading=12, textColor=FOREST, spaceAfter=5)
def on_page(canv, doc):
canv.saveState()
w, h = A4
if doc.page == 1:
canv.setFillColor(DEEP)
canv.rect(0, h - 62*mm, w, 62*mm, stroke=0, fill=1)
canv.setFillColor(GOLD)
canv.rect(0, h - 63.6*mm, w, 1.6*mm, stroke=0, fill=1)
else:
canv.setFillColor(DEEP)
canv.rect(0, h - 14*mm, w, 14*mm, stroke=0, fill=1)
canv.setFillColor(GOLD)
canv.rect(0, h - 14.8*mm, w, 0.8*mm, stroke=0, fill=1)
canv.setFillColor(white)
canv.setFont('Helvetica-Bold', 8.6)
canv.drawString(18*mm, h - 9.2*mm, 'The Internship Toolkit - Course Design Document v1.2')
canv.setFillColor(CREAM)
canv.setFont('Helvetica', 8.2)
canv.drawRightString(w - 18*mm, h - 9.2*mm, 'Pakistan Educational & Cultural Board')
canv.setFillColor(FOREST)
canv.setFont('Helvetica', 8)
canv.drawString(18*mm, 10*mm, 'the school | the school | Internship Readiness')
canv.drawRightString(w - 18*mm, 10*mm, f'Page {doc.page}')
canv.setStrokeColor(GOLD)
canv.setLineWidth(0.6)
canv.line(18*mm, 14*mm, w - 18*mm, 14*mm)
canv.restoreState()
doc = BaseDocTemplate(OUT, pagesize=A4,
leftMargin=18*mm, rightMargin=18*mm, topMargin=20*mm, bottomMargin=20*mm,
title='The Internship Toolkit - Course Design Document v1.2',
author='Khurram Badar')
frame_p1 = Frame(18*mm, 20*mm, A4[0]-36*mm, A4[1]-20*mm-66*mm, id='f1')
frame_rest = Frame(18*mm, 20*mm, A4[0]-36*mm, A4[1]-20*mm-18*mm, id='f2')
doc.addPageTemplates([
PageTemplate(id='first', frames=[frame_p1], onPage=on_page),
PageTemplate(id='later', frames=[frame_rest], onPage=on_page),
])
story = []
E = story.append
def on_page_first(canv, doc):
on_page(canv, doc)
w, h = A4
canv.saveState()
canv.setFillColor(white)
canv.setFont('Helvetica-Bold', 21)
canv.drawString(18*mm, h - 26*mm, 'The Internship Toolkit')
canv.setFillColor(CREAM)
canv.setFont('Helvetica', 11.5)
canv.drawString(18*mm, h - 34*mm, 'Student articulation training for internships and opportunities')
canv.setFont('Helvetica', 9.6)
canv.drawString(18*mm, h - 42*mm,
'Pakistan Educational & Cultural Board - Pakistan Education Academy - English Language Private School')
canv.setFillColor(GOLD)
canv.setFont('Helvetica-Bold', 9.6)
canv.drawString(18*mm, h - 50*mm, 'Course Design Document - v1.2 - 9 August 2026')
canv.setFillColor(CREAM)
canv.setFont('Helvetica', 9)
canv.drawString(18*mm, h - 57*mm, 'Prepared by Khurram Badar, Platform Architect')
canv.restoreState()
doc.pageTemplates[0].onPage = on_page_first
def bullet(txt):
E(Paragraph(txt, S['bullet'], bulletText='\u2013'))
def tbl(data, widths, header=True, fs=8.8):
t = Table(data, colWidths=widths, repeatRows=1 if header else 0)
style = [
('VALIGN', (0,0), (-1,-1), 'TOP'),
('LEFTPADDING', (0,0), (-1,-1), 5),
('RIGHTPADDING', (0,0), (-1,-1), 5),
('TOPPADDING', (0,0), (-1,-1), 3.5),
('BOTTOMPADDING', (0,0), (-1,-1), 3.5),
('LINEBELOW', (0,0), (-1,-2), 0.4, HexColor('#D8E2DA')),
('LINEBELOW', (0,-1), (-1,-1), 0.9, GOLD),
]
if header:
style += [('BACKGROUND', (0,0), (-1,0), FOREST)]
for r in range(1, len(data)):
if r % 2 == 0:
style.append(('BACKGROUND', (0,r), (-1,r), CREAM))
t.setStyle(TableStyle(style))
return t
def C(txt, bold=False):
return Paragraph(txt, S['cellb'] if bold else S['cell'])
def CH(txt):
return Paragraph(txt, S['cellh'])
================= 1. Purpose =================
================= 2. Design principles =================
================= 3. Architecture =================
arch = [
[CH('No.'), CH('Lesson'), CH('Focus'), CH('Recorded task')],
[C('1'), C('Baseline Day', True), C('60-second baseline self-introduction; own filler tally; two personal targets set'), C('Baseline recording (private)')],
[C('2'), C('The Pause Is Power', True), C('Replacing filler words with silent pauses; one-breath answers; the pause as think-time'), C('45-second low-filler answer')],
[C('3'), C('Voice Control', True), C('Pace toward the 140-160 words-per-minute window; volume; landing statements downward'), C('Paced read + freestyle')],
[C('4'), C('Structure Before Speech', True), C('PREP framework - Point, Reason, Example, Point; answer-first habit; signposting'), C('One PREP answer')],
[C('5'), C('The 60-Second Self-Introduction', True), C('Who I am, one proof of strength, what I am looking for, why this placement'), C('Polished introduction')],
[C('6'), C('Interview Answers with STAR', True), C('STAR as PREP for stories; story mining from school life; mock rounds'), C('Two STAR answers')],
[C('7'), C('Asking Well', True), C('Clarifying vague instructions; asking for help without apology; question quality'), C('30-second clarify-back clip')],
[C('8'), C('The Status Update & Presenting Work', True), C('Done / Doing / Blocked / Next in 90 seconds; the two-minute presentation as a drill only'), C('90-second update')],
[C('9'), C('Professional Presence', True), C('Posture, eye contact, greeting; phone manners; the three-line written message'), C('Phone role-play clip')],
[C('10'), C('Feedback & Difficult Moments', True), C('The composure kit: receiving correction; the strong "I do not know yet"; respectful disagreement; ends with the Mock Final'), C('Correction-response clip')],
[C('11'), C('Final Check - The Panel', True), C('Live assessment before a two-member panel; rubric-scored; certificate on pass'), C('Final recording vs baseline')],
]
E(tbl(arch, [12*mm, 38*mm, 84*mm, 40*mm]))
E(Spacer(1, 4))
================= 4. Session ritual =================
================= 5. Age design =================
================= 6. Lesson specifications =================
def lesson(num, title, obj, concept, drills, task):
E(Paragraph(f'6.{num} Lesson {num} - {title}', S['h2']))
E(Paragraph(f'<b>Objective.</b> {obj}', S['body']))
E(Paragraph(f'<b>Concept (max 10 min).</b> {concept}', S['body']))
E(Paragraph(f'<b>Drills (approx. 21 min).</b> {drills}', S['body']))
E(Paragraph(f'<b>Recorded task.</b> {task}', S['body']))
lesson(1, 'Baseline Day',
'Every student records a 60-second baseline self-introduction, hears it privately, and names their two habits to change.',
'Articulation is thought, then structure, then delivery. It is a motor skill: the baseline is a starting point, not a verdict. Two ground rules are taught and repeated: we tally habits, never people; and no recording is ever played to the class.',
'Record the baseline in self-chosen pairs; each student listens back once, privately - earphones or a quiet corner - and tallies their own fillers on this first pass. Partner tallies begin from Lesson 2, once the room has proven safe. Each student writes a target card with two habits (for example: fillers, rushing, trailing endings, buried answers).',
'The baseline recording itself - private to the student, their chosen partner, and the teacher; kept under the Section 10 rules and reused at the Final Check.')
lesson(2, 'The Pause Is Power',
'Students replace filler words with silent pauses in short answers.',
'Fillers are the brain buying time; a silent pause buys the same time and sounds composed. Silence is not a mistake - and the same pause is think-time: planning before speaking is a legitimate move, not a weakness.',
'Pause-instead rounds: 30-second answers to easy prompts while the partner raises a hand at each filler; one-breath answers - say the point in a single breath, then stop; three rounds with the chosen partner before any rotation.',
'A 45-second answer to a familiar prompt with two fillers or fewer.')
lesson(3, 'Voice Control',
'Students speak toward a 140-160 words-per-minute window, at audible volume, ending statements downward.',
'Pace, volume, and the landing. Nervous speech rushes and rises; controlled speech sits in the pace window and ends statements down, not up. The window is a target zone to train toward across the course, not a bar to clear today.',
'Paced read-aloud with pencil pace marks; whisper-to-projection ladder across the room; the landing drill - the same sentence delivered first as a question, then as a statement; emphasis rounds on key words.',
'A 60-second read plus a 30-second freestyle, aimed at the pace window.')
lesson(4, 'Structure Before Speech',
'Students answer first and build the answer with PREP.',
'PREP: Point, Reason, Example, Point. Lead with the answer, then support it. Signposts - first, second, the reason is - carry the listener. PREP is the spine of the course; everything after this lesson reuses it.',
'PREP sprints: 60 seconds to prepare, 45 seconds to answer an opinion prompt; rebuild rounds - a deliberately rambling model answer is restructured answer-first; signpost tally in pairs.',
'One recorded PREP answer; the partner ticks all four beats present.')
lesson(5, 'The 60-Second Self-Introduction',
'Every student can deliver a fluent 60-second self-introduction.',
'The formula: who I am; what I am good at, with one concrete proof; what I am looking for; why this placement. Close with a forward line, not a fade.',
'Draft, then cut to 60 seconds; three delivery rounds - full notes in round one, keywords in round two, nothing in round three; partners tally fillers and check the four parts. Repetition builds the fluency; the wean off notes is stated before it starts.',
'The polished introduction - this becomes the student portfolio piece.')
lesson(6, 'Interview Answers with STAR',
'Students answer "tell me about a time" questions with STAR and handle inexperience honestly.',
'STAR is PREP for stories: Situation, Task, Action, Result - the same answer-first habit applied to experience questions. School projects, sport, and family responsibilities all count as material. Where there is no experience: "I have not yet, but here is how I would start."',
'Story mining: each student lists three real episodes; pair mock rounds from a ten-question bank (Tell me about yourself; Why this placement; A time you worked in a team; A mistake and what you did; What do you want to learn here; and five more on the prompt cards); partner scores with a mini rubric card.',
'Two STAR answers recorded.')
lesson(7, 'Asking Well',
'Students clarify vague instructions and ask for help cleanly.',
'At a placement, asking well beats guessing. The clarify-back loop: "So you would like me to [task], by [time] - is that right?" Asking for help without the apology spiral: state the attempt, then the ask.',
'Instruction-relay game: the teacher issues deliberately vague tasks; students must clarify before acting; help-request rounds using the attempt-then-ask pattern; question ladder from closed to open to sharp.',
'A role-play clip: receive a vague task and clarify it completely in 30 seconds or less.')
lesson(8, 'The Status Update & Presenting Work',
'Students deliver a 90-second status update in the four-part format.',
'The update format: Done, Doing, Blocked, Next. The work presentation: what it is, why it matters, one thing I would improve. Both are containers - fill them and stop.',
'Update sprints on real current schoolwork, peer-timed; a two-minute show-and-tell of any project or assignment as a drill only - practised and enjoyed, not recorded and not assessed; second pass after feedback.',
'One recorded 90-second update in the four-part format.')
lesson(9, 'Professional Presence',
'Students manage the first physical, spoken, and written impressions of a placement.',
'Posture, steady eye contact, a confident greeting. Phone answering: greeting, name, offer to help. The three-line written message to a supervisor: greeting, one clear ask or update, sign-off. Safe small talk at work.',
"Entrance-and-greeting rounds at the door; back-to-back phone role-plays so no visual cues exist; each student writes one three-line message in three minutes and peer-checks a partner's - the message is kept by the student as their template, not collected.",
'A phone role-play clip.')
lesson(10, 'Feedback & Difficult Moments',
'Students respond well to correction, say "I do not know" strongly, and disagree respectfully.',
'The composure kit - three sentence stems, one family. Receiving correction: thank, confirm, act. The strong unknown: "I do not know yet - I will find out and come back by [time]." Respectful disagreement: "I see it differently - may I explain?"',
'Correction role-plays with the teacher as supervisor; impossible-question rounds forcing the strong unknown; low-stakes disagreement pairs with the sentence stems. The final ten minutes run the Mock Final: trios rotate candidate, panelist, and observer using the real rubric card, so the scoring sheet holds no surprises on panel day.',
'A recorded correction-response role-play.')
================= 7. Final Check =================
rub = [
[CH('No.'), CH('Dimension'), CH('Emerging'), CH('Developing'), CH('Secure'), CH('Excellent')],
[C('1'), C('Structure', True), C('Ideas unordered; answer buried'), C('Some order; answer arrives late'), C('Answer first; clear beats'), C('Answer first with signposts; nothing wasted')],
[C('2'), C('Clarity - fillers & pace', True), C('Fillers in most sentences; rushed or dragging'), C('Frequent fillers; uneven pace'), C('Few fillers; pace inside the window'), C('Pauses replace fillers; pace controlled for effect')],
[C('3'), C('Voice & delivery', True), C('Flat or inaudible; endings trail up'), C('Audible; little variation'), C('Clear volume; statements land'), C('Emphasis and variation hold attention')],
[C('4'), C('Presence', True), C('Avoids eye contact; closed posture'), C('Intermittent eye contact'), C('Steady eye contact; open posture; confident greeting'), C('Presence puts the listener at ease')],
[C('5'), C('Content & honesty', True), C('Guesses or blanks'), C('Relevant but thin'), C('Relevant, specific, honest about limits'), C('Specific proof; strong "not yet" where true')],
]
E(tbl(rub, [10*mm, 28*mm, 33*mm, 33*mm, 35*mm, 35*mm]))
E(Spacer(1, 4))
E(Paragraph(
'Nerves are scored kindly by design: the Presence descriptors reward steadiness '
'and openness, never the absence of nerves. A student who does not pass books a '
'second sitting within two weeks; the rubric sheet marks exactly what to practise. '
'The second sitting is a normal path through the course, not a penalty, and is '
'recorded no differently.', S['body']))
E(Paragraph('7.1 Scheduling the panel honestly', S['h2']))
E(Paragraph(
'Eight to ten minutes per student means a class of twenty needs roughly three '
'hours of panel time; a single class session cannot hold it. The Final Check '
'therefore runs as a scheduled window - a half-day, or panel slots spread across '
'free periods in one week - and two parallel panels halve the window where '
'staffing allows. Students not yet called run peer warm-ups with their prompt '
'cards; the Mock Final in Lesson 10 has already made the format familiar.', S['body']))
================= 8. Progress evidence =================
================= 9. Delivery and cadence =================
================= 10. Data protection =================
================= 11. KHDA evidence line =================
================= 12. Certificates =================
================= 13. Wargame record =================
================= 14. Open decisions =================
doc.build(story)
print('BUILT:', OUT)