This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
This is a conference badge generation system for PyCon Ireland that creates print-ready, CMYK-safe PDF badges by combining:
- Tito ticket data (attendee information via API)
- Sessionize speaker data (from exported Excel files)
The output is a professional PDF suitable for printing on A4/A5 paper with proper color profiles for commercial printing.
All commands use Task (taskfile.dev). Run task --list to see all available tasks.
task workflow:complete # Automated: convert → download → generatetask sessionize:convert # Convert Excel → JSON (requires pycon-ireland-YYYY-sessionize.xlsx)
task tito:download:tickets # Download tickets from Tito API
task badges:generate # Generate PDF from tickets + speakers JSON
task badges:blank # Generate blank badges for last-minute attendees
task report:generate # Create Excel report comparing tickets and speakerstask environment:create # Create venv
task environment:install # Install dependencies
task environment:reset # Full reset (drop + create + install)task format:ruff # Format all Python files
task format:ruff -- file.py # Format specific filetask check # Verify setup (fonts, config, tools)
task show:files # List generated files for current year
task clean # Remove generated files for current year
task tickets:update-references # Update ticket references from mapping fileConfiguration is managed via Dynaconf (config.py:1-6) which loads settings from:
settings.toml- Project settings (API account, event name, fonts, paper size, etc.).secret.toml- API tokens (gitignored)
settings.API.account- Tito account name (default: "python-ireland")settings.API.event- Event slug (e.g., "pycon-ireland-2025")settings.TITO_TOKEN- Tito API token (stored in .secret.toml)settings.fonts.*- Font file names (must exist in ./fonts/)settings.printout.paper_size- "A4" or "A5"settings.printout.debug- Boolean for debug mode
The Taskfile.yaml uses a YEAR variable (default: 2025) to construct file names like pycon-ireland-2025-tickets.json.
Script: convert-sessionize-to-json.py
- Input:
pycon-ireland-YYYY-sessionize.xlsx(downloaded from Sessionize export)- Direct URL: https://sessionize.com/app/organizer/export/excel/19814/Accepted_sessions
- Contains "Accepted speakers" sheet with Speaker Id, FirstName, LastName, Email
- Process:
- Reads "Accepted speakers" sheet using pandas/openpyxl
- Normalizes emails to lowercase
- Validates against SpeakerModel (pydantic)
- Output:
pycon-ireland-YYYY-speakers.jsonwith structure:[{"speaker_id": "...", "first_name": "...", "last_name": "...", "email": "..."}]
Script: build_badge.py download-tickets (uses get_tickets.py)
- API: Tito v3 REST API with pagination
- Process:
- Fetches from
https://api.tito.io/v3/{account}/{event}/tickets?page={N}&view=extended - Uses bearer token auth from settings
- Paginates through all tickets using
meta.next_page - Validates against TicketModel (pydantic)
- Fetches from
- Output:
pycon-ireland-YYYY-tickets.jsonwith structure:[{"first_name": "...", "last_name": "...", "email": "...", "reference": "...", "release_title": "...", "responses": {...}, "created_at": "...", "speaker": false, "exhibitor": false}]
File: emails.mapping.csv (gitignored)
- Purpose: Maps mismatched emails between Sessionize and Tito
- Format: CSV with columns
tito_email,sessionize_email - Usage: Automatically loaded by
build_badge.pyandmerge_sessionize_tito_emails.pyif exists - When needed: Speaker uses different email on Sessionize vs. Tito purchase
Script: build_badge.py build
- Inputs: tickets JSON + speakers JSON + (optional) emails.mapping.csv
- Process:
- Loads tickets and marks speakers based on email matching (with mapping support)
- Registers fonts from ./fonts/ directory (build_badge.py:36-57)
- Creates LayoutParameters based on paper size from settings (build_badge.py:65-142)
- Uses ReportLab to draw badges with:
- CMYK colors (irish_green, irish_orange, banner_blue defined at build_badge.py:60-62)
- QR codes with ticket reference
- Font hierarchy: conference font (headers), name font (names), reference font (codes)
- Handles two-per-page layout for A5 or single-per-page for other sizes
- Output:
pycon-ireland-YYYY-badges.pdf(CMYK print-ready)
Script: merge_sessionize_tito_emails.py
- Purpose: Identify speakers without tickets
- Process:
- Joins tickets and speakers DataFrames on email (with mapping support)
- Generates 4 sheets: All Tickets, All Speakers, Missing Speakers, Speakers with Ticket
- Output:
pycon-ireland-YYYY-report.xlsx
Script: update-ticket-references.py
- Purpose: Modify ticket reference codes based on a mapping file
- Input: tickets JSON + reference-mapping.json
- Process:
- Loads and validates tickets using TicketModel
- Applies reference transformations from JSON mapping
- Supports dry-run mode for previewing changes
- Uses Pydantic TypeAdapter for validation
- Output: Updated tickets JSON with new reference codes
- Use Cases:
- Anonymizing ticket data for testing
- Creating custom reference codes
- Migrating between reference systems
- Mapping Format: JSON object with
{"OLD-REF": "NEW-REF"}pairs
- Represents a Tito ticket/attendee
- Key properties:
display_name: Formatted as "FirstName L." for badgeslevel: Python experience level from responses dictspeaker: Boolean flag set during badge generationexhibitor: Boolean flag
- Represents a Sessionize speaker
- Fields: speaker_id, first_name, last_name, email
- Property
full_name: Combined first + last name
- Wrapper for paginated Tito API responses
- Contains list of tickets + pagination metadata
Required fonts in ./fonts/ directory:
- UbuntuMono-R.ttf - Reference font (clearly distinguishes 0/O, 1/l/I)
- Bree fonts (BreeBold.ttf, BreeSerif-Regular.ttf) - Licensed, stored in Google Drive
Font registration happens in build_badge.py:register_fonts() before PDF generation.
All colors are CMYK for professional printing (build_badge.py:60-62):
irish_green = PCMYKColor(71, 0, 72, 40)irish_orange = PCMYKColor(0, 43, 91, 0)banner_blue = PCMYKColor(98, 82, 0, 44)
Never use RGB colors in badge generation code.
Currently using requirements.txt but transitioning to pyproject.toml + uv:
- dynaconf - Configuration management
- marshmallow - Data serialization (legacy, being replaced by pydantic)
- openpyxl - Excel file reading
- pydantic - Data validation and models
- reportlab - PDF generation
- requests - Tito API calls
- typer - CLI framework
- pandas - Data processing
External tools (optional but recommended):
- jq - JSON processing for ticket counting
- httpie - Alternative API testing
Many tasks have preconditions that check for required files. If a task fails with a precondition error, the error message indicates which command to run first (e.g., "Run 'task tito:download:tickets' first").
Task tracks file dependencies (sources/generates). If source files haven't changed, dependent tasks skip execution automatically.
The YEAR variable in Taskfile.yaml controls which event files are processed. Update it when preparing for a new conference year.
When settings.printout.debug = true, output files use simple names (e.g., "tickets.pdf" instead of timestamped names).
Speakers are identified by matching their Sessionize email against Tito ticket emails. The emails.mapping.csv file solves mismatches. The badge generation code sets ticket.speaker = True for matched emails.