Generate printable, CMYK-safe conference name badges from Tito tickets and Sessionize speakers data.
We are transitioning from requirements.txt to pyproject.toml managed by uv. Use one of the following methods.
Use this today.
python -m venv .venv
# bash/zsh:
source .venv/bin/activate
# fish:
# source .venv/bin/activate.fish
pip install -U pip
pip install -r requirements.txtOnce dependencies are declared in pyproject.toml, uv will manage the environment and installs.
- Install uv (choose one):
- Homebrew:
brew install uv - pipx:
pipx install uv
- Create the virtual environment and install:
uv syncIf you want to use uv today with the existing requirements file:
uv venv
# bash/zsh:
source .venv/bin/activate
# fish:
# source .venv/bin/activate.fish
uv pip install -r requirements.txtAdd your Tito API token to .secret.toml:
TITO_TOKEN = "your_token_here"You can find your API key by signing in at https://api.tito.io.
Project settings live in settings.toml. Adjust values there to match your event and output needs.
Place required TTF files in the ./fonts directory.
- UbuntuMono-R.ttf - Used because it clearly distinguishes 0 vs O, and 1 vs l vs I
- Bree font - Licensed font (not free). The TTF files are stored in our Google Drive
If you have the Sessionize Excel file ready, you can run the complete workflow with a single command:
task workflow:completeThis will automatically:
- Convert the Sessionize Excel file to JSON
- Download tickets from Tito
- Generate the badges PDF
Prerequisites:
pycon-ireland-YYYY-sessionize.xlsxmust exist in the current directory.secret.tomlconfigured with your Tito API token
If you prefer to run each step individually, follow these steps:
- Go to the Sessionize website and select your event (e.g., PyCon Ireland 2025)
- Navigate to the Export section
- In the Session and Speaker area, download the Accepted sessions file
- This ensures you only get accepted sessions without unnecessary noise
- Save the downloaded Excel file as
pycon-ireland-YYYY-sessionize.xlsx(replace YYYY with the current year)
Direct URL: https://sessionize.com/app/organizer/export/excel/19814/Accepted_sessions
The Excel file will contain multiple sheets with session and speaker information ans save it as pycon-ireland-YYYY-sessionize.xlsx (replace YYYY with the current year).
The Excel file from Sessionize needs to be converted to JSON format. This is done using the convert-sessionize-to-json.py script, which extracts the necessary information from the multiple sheets and validates the output structure.
Run the conversion:
task sessionize:convertThis command:
- Takes the
pycon-ireland-YYYY-sessionize.xlsxfile as input - Extracts speaker and session information from the Excel sheets
- Outputs a validated
pycon-ireland-YYYY-speakers.jsonfile
Download the list of sold tickets for the current event from Tito. This uses the build_badge.py script with the download-tickets command.
Run the download:
task tito:download:ticketsThis command:
- Connects to the Tito API using your configured token
- Downloads all ticket data for the current event (defined by the YEAR variable in
Taskfile.yaml) - Creates a
pycon-ireland-YYYY-tickets.jsonfile with all attendee information
Generate the print-ready PDF with all badges. This uses the build_badge.py script with the build command, combining both the tickets and speakers data.
task badges:generateThis command:
- Automatically removes any existing
pycon-ireland-YYYY-badges.pdffile - Takes the
pycon-ireland-YYYY-tickets.jsonfile (attendee data) - Takes the
pycon-ireland-YYYY-speakers.jsonfile (speaker data) - Generates a
pycon-ireland-YYYY-badges.pdffile with all badges - Uses CMYK colors suitable for professional printing
- Applies the configured fonts and styling
For last-minute attendees or walk-ins at the conference, you can generate blank badges that can be filled in by hand.
task badges:blankThis command generates a single blank badge by default. To generate multiple blank badges, use:
task badges:blank -- --limit 5This command:
- Generates
blank-tickets.pdfwith the specified number of blank badges - Uses the same styling and layout as regular badges
- Creates badges without attendee names or QR codes
- Perfect for on-site registrations or replacements
The Taskfile.yaml provides convenient commands for all operations. Use task --list to see all available tasks, or task --summary <task-name> for detailed information about a specific task.
task workflow:complete- Complete automated workflow (convert → download → generate)task check- Verify environment setup and required toolstask show- Display current YEAR and EVENT variablestask show:files- List all generated files for current eventtask clean- Remove all generated files for current eventtask clean:all- Remove all generated files for all years (with confirmation)
task environment:create- Create Python virtual environmenttask environment:install- Install dependencies (with precondition check)task environment:drop- Remove virtual environmenttask environment:reset- Reset environment (drop + create + install)
task format:ruff- Format all Python files with ruff
task tito:download:tickets- Download tickets for current event →pycon-ireland-YYYY-tickets.jsontask tito:download:all- Download tickets from all previous yearstask tito:download:checkins- Download check-in data →checkins.jsontask tito:count:api- Count tickets via Tito API
task sessionize:convert- Convert Sessionize Excel to JSON (with validation)task badges:generate- Generate badge PDF (with precondition checks)task badges:blank- Generate blank badges for last-minute attendees →blank-tickets.pdftask report:generate- Generate Excel report merging Tito and Sessionize data
task tickets:count- Count tickets from local JSON file (requires jq)task tickets:graph- Generate ticket graphstask tickets:update-references- Update ticket references from mapping file →pycon-ireland-YYYY-tickets-updated.jsontask tshirts:distribution:current- T-shirt size distribution for current yeartask tshirts:distribution:all- T-shirt size distribution for all years
Before generating badges, verify your environment is properly configured:
task checkThis will check for:
.secret.tomlconfiguration file- Virtual environment
- Required fonts (UbuntuMono-R.ttf)
- External tools (jq, httpie)
To see which files exist for the current event:
task show:filesTo see detailed information about any task, including prerequisites and outputs:
task --summary <task-name>Example:
task --summary badges:generateTo remove generated files for the current event:
task cleanTo remove ALL generated files across all years:
task clean:allTo format all Python files in the project with ruff:
task format:ruffTo format a specific file:
task format:ruff -- build_badge.pyRuff is a fast Python linter and formatter that ensures consistent code style across the project.
Sometimes speakers register on Sessionize with a different email address than the one they use to purchase tickets on Tito. This creates a mismatch when trying to identify which speakers have tickets.
To solve this, create an emails.mapping.csv file to map between the two email addresses:
tito_email,sessionize_email
john.doe@company.com,john.personal@gmail.com
jane.smith@work.com,jane@example.org
Problem: A speaker registered on Sessionize with speaker@personal.com but bought their ticket with speaker@work.com.
Solution: Add a mapping:
tito_email,sessionize_email
speaker@work.com,speaker@personal.com
- The
badges:generateandreport:generatetasks automatically loademails.mapping.csvif it exists - When matching speakers to tickets, the system will:
- First check for direct email matches
- Then apply mappings from the CSV file
- Mark the person as a speaker if either email matches
- Speaker uses different emails on Sessionize vs Tito
- Speaker changed email addresses between registration and ticket purchase
- Corporate vs personal email situations
- Misspelled emails that need correction
Place the emails.mapping.csv file in the root directory of the project (same location as Taskfile.yaml).
Template: A template file emails.mapping.csv.example is provided in the repository. Copy it to emails.mapping.csv and add your mappings.
cp emails.mapping.csv.example emails.mapping.csv
# Then edit emails.mapping.csv with your actual mappingsNote: The actual emails.mapping.csv file is automatically ignored by git (see .gitignore) as it may contain personal information.
Sometimes you need to modify ticket reference codes (the unique identifiers like "BBGQ-1"). This is useful for:
- Anonymizing ticket data for testing or demonstrations
- Creating custom reference codes for internal use
- Migrating between different reference systems
Create a reference-mapping.json file in the project root with the old and new reference codes:
{
"BBGQ-1": "KARA-TE",
"XYZA-2": "JUDO-42",
"TEST-3": "DEMO-99"
}# Preview changes without modifying files
task tickets:update-references -- --dry-run
# Apply the changes
task tickets:update-referencesThis will:
- Read your tickets from
pycon-ireland-YYYY-tickets.json - Apply the reference mappings from
reference-mapping.json - Output the updated tickets to
pycon-ireland-YYYY-tickets-updated.json
You can also use the script directly:
# Update references
python update-ticket-references.py \
pycon-ireland-2025-tickets.json \
reference-mapping.json \
pycon-ireland-2025-tickets-updated.json
# Preview changes (dry run)
python update-ticket-references.py \
pycon-ireland-2025-tickets.json \
reference-mapping.json \
pycon-ireland-2025-tickets-updated.json \
--dry-runNote: The reference-mapping.json file is automatically ignored by git to prevent accidental commits of potentially sensitive mappings. An example template is provided in reference-mapping.example.json.
All colors used for PDF generation are CMYK-based, ensuring compatibility with professional printing services. Color definitions are in the badge-building code.
Task tracks file dependencies automatically. If source files haven't changed, tasks won't re-run unnecessarily. This is especially useful for badges:generate which checks if tickets or speakers JSON files have been updated.
All critical tasks include precondition checks. If a required file is missing, you'll get a clear error message indicating which command to run first.
The project includes several Python scripts:
convert-sessionize-to-json.py- Converts Sessionize Excel exports to JSON formatbuild_badge.py- Main script with subcommands:download-tickets- Downloads tickets from Tito APIbuild- Generates the badge PDF from tickets and speakers JSON filesblank-tickets- Generates blank badges for last-minute attendees
update-ticket-references.py- Updates ticket reference codes based on a JSON mapping file
You can also run the scripts directly without using Task:
# Convert Sessionize data
python convert-sessionize-to-json.py pycon-ireland-2025-sessionize.xlsx pycon-ireland-2025-speakers.json
# Download tickets
python build_badge.py download-tickets --event pycon-ireland-2025 --store-name pycon-ireland-2025-tickets.json
# Generate badges
python build_badge.py build pycon-ireland-2025-tickets.json --speakers pycon-ireland-2025-speakers.json --output pycon-ireland-2025-badges.pdf
# Generate blank badges
python build_badge.py blank-tickets --limit 1
# Update ticket references
python update-ticket-references.py \
pycon-ireland-2025-tickets.json \
reference-mapping.json \
pycon-ireland-2025-tickets-updated.json