Pipeline Steps Reference¶
Detailed reference for each of the nine pipeline steps. For an overview and flow diagram, see Architecture. For configuration keys, see the Configuration Reference.
Step 1 — Output Preparation¶
Module: pipeline/prepare_output.py
Prepares the output directory before the pipeline run begins. This step runs before any data is processed.
Configuration keys read:
| Key | Type | Description |
|---|---|---|
pipeline.before_run.clear_output_directory |
bool | When true, removes all output except logs |
Behavior:
- If the output directory does not exist, creates it and the logs subdirectory.
- If
clear_output_directory: true, removes all contents ofoutput/exceptoutput/logs/(logs are always preserved for audit trail). - If
clear_output_directory: false, prompts the user interactively before deleting. - Returns
False(cancels the pipeline) if the user declines the prompt.
Output: Clean output/ directory with output/logs/ ready for writing.
Step 2 — Preprocessing¶
Module: pipeline/preprocess.py
Reads the raw Excel input, validates the schema, normalizes all client and vaccination data, and writes a single canonical JSON artifact.
Configuration keys read:
| Key | Type | Description |
|---|---|---|
chart_diseases_header |
list | Diseases to include as chart columns; others collapse to "Other" |
preprocess.include_dose |
bool | Include dose numbers in overdue vaccine lists (default: false) |
preprocess.show_validity_markers |
bool | Render validity markers in the immunization chart (default: false) |
phix_validation.enabled |
bool | Validate school names against PHIX reference mapping (default: true) |
phix_validation.target_phu |
str | Exact PHU name as it appears in phix_mapping.json |
phix_validation.unmatched_behavior |
str | Action on unmatched schools: warn, error, or skip |
date_notice_delivery |
ISO 8601 | Reference date for age-based eligibility (16+ threshold) |
date_data_cutoff |
ISO 8601 | Date the source data was extracted from Panorama |
Inputs:
- Excel file from
input/(single worksheet,.xlsx) config/vaccine_reference.json— maps vaccine codes to disease namesconfig/disease_normalization.json— normalizes raw disease name variantsconfig/phix_mapping.json— PHU-keyed school name → PHIX facility ID mapping (when PHIX validation enabled)
Outputs:
output/artifacts/preprocessed_clients_<run_id>.json— canonical client artifactoutput/logs/preprocess_<run_id>.log— processing logphix_exact.csv,phix_inexact.csv,phix_no_match.csv— school match audit CSVs (when PHIX validation enabled)
Processing:
- Validates school/daycare names against
phix_mapping.jsonfor the configured PHU (whenphix_validation.enabled: true) - Reads and validates Excel schema (required columns, data types)
- Normalizes disease names using
disease_normalization.json - Expands vaccine codes to disease names using
vaccine_reference.json - Filters diseases against
chart_diseases_header; collapses unlisted diseases to "Other" - Computes client ages relative to
date_notice_delivery(determines parent vs. student addressing) - Sorts clients deterministically: school → last name → first name → client ID
- Assigns stable sequence numbers (
00001,00002, …) - Synthesizes missing school/board identifiers where needed
- Writes the canonical JSON artifact
Step 3 — QR Code Generation (optional)¶
Module: pipeline/generate_qr_codes.py
Generates QR code PNG images for each client from a configurable URL payload template. Skipped entirely when qr.enabled: false.
Configuration keys read:
| Key | Type | Description |
|---|---|---|
qr.enabled |
bool | Enable/disable QR code generation |
qr.payload_template |
str | URL template with {field} placeholders |
Inputs: output/artifacts/preprocessed_clients_<run_id>.json
Outputs: PNG files in output/artifacts/qr_codes/<sequence>.png
All template placeholders are validated at config-load time against the TemplateField enum. See the Template Field Reference for available placeholders.
Step 4 — Notice Generation¶
Module: pipeline/generate_notices.py
Renders Typst source files (.typ) for each client by combining the preprocessed JSON artifact with PHU-specific language templates. This step is always run.
Configuration keys read:
| Key | Type | Description |
|---|---|---|
chart_diseases_header |
list | Chart column disease names (translated per language) |
Inputs:
output/artifacts/preprocessed_clients_<run_id>.jsonoutput/artifacts/qr_codes/<sequence>.png(if QR codes were generated)- Language template module (
templates/orphu_templates/<name>/) config/translations/{lang}_diseases_chart.jsonconfig/translations/{lang}_diseases_overdue.json
Outputs: .typ source files in output/artifacts/typst/
Templates are loaded dynamically at runtime. The --template CLI argument selects a directory under phu_templates/. When omitted, the built-in templates/ directory is used. Each template module must define a render_notice() function.
Disease names are translated into the target language in Python before being passed to Typst — no runtime lookups occur in the Typst templates themselves.
Step 5 — PDF Compilation¶
Module: pipeline/compile_notices.py
Compiles the generated .typ Typst source files into individual PDF notices by invoking the typst command-line tool as a subprocess.
Configuration keys read:
| Key | Type | Description |
|---|---|---|
typst.bin |
str | Path to the Typst binary (default: typst) |
Inputs: .typ files from output/artifacts/typst/
Outputs: PDF files in output/pdf_individual/
This step is fail-fast: if any .typ file fails to compile, the pipeline halts immediately. Typst v0.14.2 is required; the binary must be on PATH or configured via typst.bin.
Step 6 — PDF Validation¶
Module: pipeline/validate_pdfs.py
Validates compiled PDFs against configurable rules using invisible markers embedded by the Typst templates and pypdf text extraction.
Configuration keys read:
| Key | Type | Description |
|---|---|---|
pdf_validation.rules.exactly_two_pages |
severity | Ensure each notice has exactly 2 pages |
pdf_validation.rules.signature_overflow |
severity | Signature block must end on page 1 |
pdf_validation.rules.envelope_window_1_125 |
severity | Contact table height ≤ 1.125 inches |
Severity levels: disabled (skip), warn (log only), error (halt pipeline).
Inputs:
- PDFs from
output/pdf_individual/ output/artifacts/preprocessed_clients_<run_id>.json(source of truth for expected client IDs)
Outputs:
- Console summary with per-rule pass/fail counts
output/metadata/<lang>_validation_<run_id>.json— per-PDF results and measurements
For a full explanation of how markers work and how to add new rules, see PDF Validation.
Step 7 — PDF Encryption (optional)¶
Module: pipeline/encrypt_notice.py
Encrypts individual PDFs using a per-client password generated from a configurable template. Skipped when encryption.enabled: false.
Configuration keys read:
| Key | Type | Description |
|---|---|---|
encryption.enabled |
bool | Enable/disable PDF encryption |
encryption.password.template |
str | Password template with {field} placeholders |
Inputs: PDFs from output/pdf_individual/
Outputs: Encrypted PDFs in output/pdf_individual/ (replace originals or alongside them)
Per-item recovery: if individual PDF encryption fails, the failure is logged and processing continues for remaining PDFs.
Step 8 — PDF Bundling (optional)¶
Module: pipeline/bundle_pdfs.py
Combines individual PDFs into multi-client bundles, optionally grouping by school or board. Skipped when bundling.bundle_size: 0.
Configuration keys read:
| Key | Type | Description |
|---|---|---|
bundling.bundle_size |
int | Max clients per bundle (0 = disabled) |
bundling.group_by |
str|null | Grouping strategy: null, school, or board |
Inputs: PDFs from output/pdf_individual/
Outputs: Bundle PDFs in output/pdf_combined/ with a manifest JSON
This step runs independently of encryption — both can be enabled simultaneously. Per-item recovery applies.
Step 9 — Cleanup¶
Module: pipeline/cleanup.py
Removes intermediate files after a successful pipeline run. Behavior is controlled entirely by configuration flags.
Configuration keys read:
| Key | Type | Description |
|---|---|---|
pipeline.after_run.remove_artifacts |
bool | Remove output/artifacts/ (QR codes, Typst files) |
pipeline.after_run.remove_unencrypted_pdfs |
bool | Remove unencrypted individual PDFs after encryption/bundling |
When remove_unencrypted_pdfs: true and neither encryption nor bundling is enabled, individual PDFs are assumed to be the final output and are preserved regardless of this setting.