Skip to content

[WIP] - Templates: redesign to the v1 API contract + visual layout support#596

Draft
chetanr25 wants to merge 13 commits into
fireform-core:developmentfrom
chetanr25:template_registry
Draft

[WIP] - Templates: redesign to the v1 API contract + visual layout support#596
chetanr25 wants to merge 13 commits into
fireform-core:developmentfrom
chetanr25:template_registry

Conversation

@chetanr25

Copy link
Copy Markdown
Collaborator

Templates: redesign to the v1 API contract + visual layout support

What this does

This rebuilds the whole templates layer to match the v1 API contract. The old prototype template endpoints are gone, and in their place there is a proper form template registry with the five contract endpoints, a service layer, a new DB model and migration, and strict request validation. I also updated the API contract itself while doing this, since two things in it needed fixing (a confusing name, and a missing piece that the visual editor on the website actually needs).

Closes the templates CRUD work (issue #549 / 08_templates_crud).

Why the old code had to go

The previous app/api/routes/templates.py was prototype code: upload a flat PDF, let the backend auto detect AcroForm widgets, store name/fields/pdf_path. The contract models a template completely differently. It is a registry keyed by form_type that holds field definitions, validation rules, how each field maps from the incident schema, and (after this PR) where each field sits on the PDF. The form generation and extraction validation layers both depend on this shape, so it had to be redone before either of those can be built.

Contract changes

Two edits to contracts/:

  1. Renamed canonical to incident everywhere it referred to the data schema. canonical_mapping became incident_mapping. The word "canonical" was accurate but kept tripping people up, and "incident" says exactly what it is (the FireForm incident schema). I left the broader "canonical" wording alone in the extraction/system specs where renaming it would read worse.

  2. Added visual layout to each field, which is the part the website template editor was already producing but the contract never had. Each TemplateField now carries a nested layout object with page, x, y, width, height, font, font_size, color, align. Coordinates are in PDF points with the origin at the bottom left, so the box runs from start (x, y) to end (x + width, y + height). I also added static_text for fields that print fixed text instead of mapped data.

While consolidating, I dropped the top level field_mappings_from_incident dict because it was just the inverse of the per field incident_mapping, so it was duplicate information. jurisdiction and agency_type are now optional (the editor does not capture jurisdiction today), and form_type is a plain string in the summary so custom jurisdictions like state_texas validate.

Endpoints

All five contract endpoints, live under /api/v1/templates:

  • GET /templates lists registered templates (summary view)
  • POST /templates registers one, returns 201, returns 409 if the form_type
    already exists
  • GET /templates/{id} returns the full template
  • PUT /templates/{id} replaces a template
  • GET /templates/{id}/fields returns just the fields, with a required_only
    query flag

What got removed

app/api/routes/templates.py is deleted along with its prototype routes (upload, preview, create, make-fillable, delete) and the schemas/repo helpers that only served them. The only forced change was the old GET /templates list, which sat
on the same path the contract list now owns.

The legacy Template model and table are kept on purpose. The fill pipeline (forms.py, jobs.py, tasks/fill.py) still reads from it, so removing it now would break those. They get replaced properly by the contract forms and input layers in later issues, so I left them alone here.

Code structure

Kept to the project's routes to services to repositories layering:

  • app/api/routes/form_templates.py is thin. Each handler is one call into the service.
  • app/services/form_templates.py holds the actual work: the 409/404 checks, building the ORM object, mapping the ORM back to the response schema, and the required_only filtering. It has no FastAPI imports.
  • app/db/repositories.py got the FormTemplate queries.

Model and migration

New FormTemplate model with a UUID primary key, a unique form_type, the fields stored as JSON (each field carries its nested layout), and the usual status/version/timestamps. field_count and last_updated are derived in the response, not stored, so there is nothing to keep in sync.
Migration 003_form_templates creates the form_templates table with a unique index on form_type.

Note for reviewers: migration 003 was edited a couple of times while building this branch. That is fine here because it was never pushed before this PR and the only DB that ran it is my local dev one, which I re-ran from base each time. Once this merges it should be treated as frozen, and any further change goes in a new migration.

Fix: #549
Part of: #541

@chetanr25 chetanr25 force-pushed the template_registry branch 2 times, most recently from a23ba45 to 0d0ef6f Compare June 27, 2026 19:30
@chetanr25 chetanr25 force-pushed the template_registry branch from 0d0ef6f to f188299 Compare June 27, 2026 19:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Templates CRUD - API Contracts

1 participant