Skip to main content
Version: 3.1

Report template

Technical reference for the Template model in Care EMR, and for the report generation pipeline that renders it. For the plain-language view, read the report template concept.

Source:

The model stores the template as plain character fields plus an opaque options JSON field. The real constraints live in the Pydantic spec layer, which pins the status and format enums, and validates the compatibility of template_type, context, and options.

Models

ModelPurpose
TemplateA facility-scoped or instance-wide report layout, with its report type, context, output format, and render options
ReportUploadOne generated report file produced from a Template. Documented in Report & Templates

Template extends SlugBaseModel, the facility-scoped slug variant of EMRBaseModel. The base gives external_id, audit fields, meta/history JSON, and soft delete through deleted.

Template fields

FieldType (model)Spec constraintNotes
facilityFK → Facility (PROTECT, nullable)UUID4 | None (write)Null means instance-wide. Excluded from base serialization through __exclude__, resolved server-side on write
slugCharField(255)written as slug_value: SlugType, read as slug plus slug_configStored with a prefix: f-<facility_external_id>-<slug> or i-<slug>
nameCharField(255)str, required
statusCharField(255)TemplateStatusOptionsSee the status values below
template_dataTextFieldstr, required on writeThe Jinja2 markup of the layout. Returned only by TemplateRetrieveSpec
template_typeCharField(255)str, validated against ReportTypeRegistrySee the report type values below
default_formatCharField(255)TemplateFormatOptionsSelects the generator that validates options
contextCharField(100), default "encounter_base"str, validated against DataPointRegistrySee the context values below
descriptionTextField, blank, default ""str = ""
optionsJSONField, default {}dict = {}, validated against the generator's options_modelAccepted keys depend on default_format

TemplateStatusOptions values

ValueMeaning
draftIn preparation. Report generation rejects the template
activePublished. Report generation accepts the template
retiredWithdrawn. Report generation rejects the template

TemplateFormatOptions values

ValueGeneratorOptions model
pdfWeasyPrintGeneratorpage_size (A4, A3, A5, Letter, Legal), margin, orientation (portrait, landscape), stylesheets
htmlHTMLGeneratorwrap_document, title, charset

Registered report types

Report types are registered in report_types.py. Each one binds a display name, an associating model, and an authorizer class.

KeyDisplay nameAssociating modelAuthorizer
discharge_summaryDischarge SummaryEncounterDischargeSummaryReportAuthorizer
patient_summaryPatient SummaryPatientPatientReportAuthorizer
account_reportAccount ReportAccountAccountReportAuthorizer
encounter_reportEncounter ReportEncounterEncounterReportAuthorizer

Registered contexts

Contexts are registered in DataPointRegistry by the data point modules under care/emr/reports/context_builder/data_points/.

SlugDisplay nameContext keyAssociating model
encounter_baseEncounter ReportencounterEncounter
patient_basePatient ReportpatientPatient
account_baseAccount ReportaccountAccount

A template is valid only when ReportTypeRegistry.get(template_type).associating_model equals DataPointRegistry.get(context).__associating_model__.

slug_config shape (read)

TemplateReadSpec parses the stored prefixed slug back into a dict:

slug_config (facility-scoped) → { facility: <facility_external_id>, slug_value: <slug> }
slug_config (instance-wide) → { slug_value: <slug> }

Resource specs (API schema)

Spec classRoleFields and behaviour
TemplateBaseSpecsharedid, name, status, default_format, description, options. __exclude__ = ["facility"]
TemplateCreateSpecwrite · createAdds facility, slug_value, template_data, template_type, context
TemplateUpdateSpecwrite · updateIdentical to TemplateCreateSpec
TemplateReadSpecread · listBase fields plus slug, slug_config, template_type, context. No template_data
TemplateRetrieveSpecread · detailExtends the read spec with template_data and a nested facility (FacilityBareMinimumSpec)

Write-side validation:

  • template_type must resolve in ReportTypeRegistry, else Invalid report type.
  • context must resolve in DataPointRegistry, else Invalid Context type.
  • validate_report_type_and_context compares the two associating models, and raises Report Type and Context are not compatible when they differ.
  • options is validated against GeneratorRegistry.get(default_format).options_model.
  • perform_extra_deserialization resolves the facility external ID, and sets obj.slug to the raw slug_value. The viewset prefixes the slug afterwards.
  • The viewset rejects a duplicate slug in the same scope with Slug already exists.

Viewset actions and authorization

TemplateViewSet uses slug as the lookup field, and supports create, retrieve, update, and list. It filters on name, template_type, status, facility, and facility_only, and orders by created_date, name, or template_type.

ActionAuthorization
list (with facility)can_list_facility_templatecan_read_template
list (without facility)Returns instance-wide templates only
retrievecan_list_facility_template for a facility template
create, updatecan_write_facility_templatecan_write_template, checked at the facility root. A template with no facility needs a superuser
GET schemacan_view_template_schema
POST previewcan_preview_template

The permissions and their roles are defined in permissions/template.py.

PermissionDisplay nameRoles
can_read_templateCan Read TemplateFacility Admin, Administrator, Admin, Staff, Doctor, Nurse, Volunteer, Pharmacist
can_write_templateCan Create Template on FacilityFacility Admin, Admin, Doctor, Nurse
can_preview_templateCan Preview TemplateFacility Admin, Admin
can_view_template_schemaCan View Template SchemaFacility Admin, Admin
can_generate_report_from_templateCan generate report from templateFacility Admin, Administrator, Admin, Staff, Doctor, Nurse, Volunteer, Pharmacist

Rendering pipeline

Renderer combines a generator with TemplateEngine.

  • TemplateEngine uses a Jinja2 SandboxedEnvironment with StrictUndefined and autoescape. trim_blocks and lstrip_blocks are on.
  • Filters: date, datetime, time, currency, phone.
  • Globals: current_date, current_datetime, current_time.
  • The generator turns the rendered HTML into the output bytes. WeasyPrintGenerator produces the PDF; HTMLGenerator returns the HTML, and wraps it in a document when wrap_document is true.

GET /schema returns the contexts, output formats, custom types, and report types the builder needs. Each context lists its fields with a display name, a type, and a preview value.

POST /preview renders template_data against a preview context that carries sample values, and returns the rendered file. The preview does not read patient data.

Report generation

ReportUploadViewSet.generate creates the report file. It runs these checks in order:

  1. Resolves the Template from template_id.
  2. Calls the report type's authorizer for write access on associating_id.
  3. Checks can_generate_report_from_template on the template's facility.
  4. Rejects the request when template.status is not active, with Template is not active.
  5. Returns HTTP 409 when a generation for the same report type and associating ID is already in progress. force clears the lock, and status_check returns the progress instead.

Generation then runs in the generate_report_task Celery task, which reports progress and creates a ReportUpload.

care/security/permissions/template.py also defines can_generate_report_for_completed_encounter, granted to Facility Admin and Admin. The encounter authorization module uses it to allow a report on a completed encounter.

API integration notes

  • template_data is Jinja2 markup, rendered in a sandbox. An undefined variable fails the render.
  • template_data is returned only on retrieve, not in the list response.
  • slug is immutable in the frontend builder after creation.
  • options is validated server-side against the format generator, so the accepted keys change with default_format.
  • A template with no facility is instance-wide, and only a superuser can write it.
  • A list request without a facility query parameter returns instance-wide templates only. With facility_only=true, the response holds facility templates only.