Toggle menu
Toggle preferences menu
Toggle personal menu
Not logged in
Your IP address will be publicly visible if you make any edits.

References/Report & Templates

From OHC Network Wiki
Revision as of 09:53, 6 July 2026 by Admin (talk | contribs) (Add Navbox platform (via update-page on MediaWiki MCP Server))
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
referenceplatformCARE 3.0+

A Template defines reusable report markup and render options; a ReportUpload is one generated file (PDF/HTML/etc.) produced from that template and linked to a subject resource. You author templates through the API; report uploads are created for you by the file-upload flow.

Two layers back this:

  • Storage layer — the Django models (care/emr/models/report/). Several fields are opaque (TextField/JSONField); their real shape lives in the spec layer, not the model.
  • API layer — the Pydantic resource specs (care/emr/resources/report/), built on EMRResource. These define the enums, validation, the structure of the JSON fields, and the read/write schemas the API exposes.

Source:

Models

Model Purpose
ReportUpload An uploaded, generated report file (PDF/HTML/etc.) produced from a Template and linked to a subject resource via associating_id
Template A facility-scoped (or instance-wide) reusable report definition (markup + render options) used to generate reports

ReportUpload extends EMRBaseModel, the shared Care EMR base providing external_id, meta/history JSON, audit FKs, and soft-delete semantics.

Template extends SlugBaseModel, the facility-scoped slug variant (FACILITY_SCOPED = True, which adds slug handling on top of the EMR base). Slugs carry a prefix in storage: f-<facility_external_id>-<slug> when facility-scoped, i-<slug> when instance-wide (see [https://github.com/ohcnetwork/care/blob/develop/care/emr/models/base.py base.py]).

ReportUpload fields

Core

Field Type Notes
template FK → Template (PROTECT) Template the report was generated from. The template cannot be deleted while reports reference it
name CharField(2000) Display name of the report. Required in every spec

{{Field|model=Report&Templates|section=Core|name=internal_name|type=CharField(2000)|notes=Randomized storage key, auto-generated on save so the file name never leaks PII (see [[#methods--save-behaviour}}

associating_id CharField(100) Non-null. Free-form identifier linking the report to its subject resource (e.g. an encounter)
upload_completed BooleanField Default False. Flips to True once the file upload to storage finishes
report_type CharField(50) File/content type of the report; also exposed via the file_type property for S3FilesManager

Archival

Field Type Notes
is_archived BooleanField Default False
archive_reason TextField Blank allowed
archived_datetime DateTimeField Nullable; set when the report is archived
archived_by FK → User (PROTECT) Nullable. related_name="archived_reports"

Inherited / metadata

Field Type Notes
meta JSONField (default {}) Inherited from EMRBaseModel. Holds mime_type (surfaced by specs as mime_type) and other free-form metadata
history JSONField (default {}) Inherited audit history
created_by / updated_by FK → User (SET_NULL) Inherited audit FKs; serialized as created_by/updated_by UserSpec

Storage

ReportUpload declares a class-level files_manager = S3FilesManager(BucketType.REPORT). The file body lives in object storage (REPORT bucket), not the database — the model row holds only metadata and the internal_name storage key.

Template fields

The Django model stores most config as plain strings plus an opaque options JSONField. The constraints live in the spec layer: it pins status, default_format, and the slug, and validates the structure and mutual compatibility of template_type, context, and options.

Field Type (model) Spec constraint Notes
facility FK → Facility (PROTECT, nullable) UUID4
slug CharField(255) written via slug_value: SlugType; read as slug + parsed slug_config
name CharField(255) str, required
status CharField(255) TemplateStatusOptions enum
template_data TextField str, required (write)
template_type CharField(255) str, validated against ReportTypeRegistry
default_format CharField(255) TemplateFormatOptions enum
context CharField(100) (default "encounter_base") str, validated against DataPointRegistry
description TextField (blank, default "") str = ""
options JSONField (default {}) dict = {}, validated against the format generator's options_model

TemplateStatusOptions values

status is bound to this enum (care/emr/resources/report/template/spec.py):

Value Meaning
draft Template being authored, not yet usable
active Published, usable for generating reports
retired Withdrawn from use

TemplateFormatOptions values

default_format is bound to this enum:

Value Meaning
pdf Render to PDF (uses the PDF generator's options_model)
html Render to HTML (uses the HTML generator's options_model)

slug_config shape (read)

On read, TemplateReadSpec parses the stored prefixed slug back into a slug_config dict:

slug_config (facility-scoped) → { facility: <facility_external_id>, slug_value: <slug> }
slug_config (instance-wide)   → { slug_value: <slug> }   # parsed from "i-<slug>"

Resource specs (API schema)

All specs subclass EMRResource. Read runs serialize() → DB-field copy → perform_extra_serialization() hook. Write runs de_serialize() → DB-field copy → perform_extra_deserialization() hook.

ReportUpload specs (report/report_upload/spec.py)

Spec class Role Fields / behaviour
ReportUploadBaseSpec shared None, name: str
ReportUploadListSpec read · list Adds template (nested TemplateReadSpec), report_type, associating_id, archived_by (UserSpec), archived_datetime, upload_completed, is_archived, archive_reason, created_date, extension, uploaded_by, mime_type
ReportUploadRetrieveSpec read · detail Extends list spec; adds signed_url, read_signed_url, internal_name

Server-maintained read behaviour (perform_extra_serialization):

  • idobj.external_id.
  • extensionobj.get_extension() (dotted extension parsed from internal_name).
  • mime_typeobj.meta["mime_type"].
  • template ← serialized via TemplateReadSpec (full nested template object).
  • created_by / updated_by populated from the user cache via serialize_audit_users.
  • Signed URLs (retrieve only): a freshly created object (_just_created) gets a write/upload signed_url via files_manager.signed_url(obj); any other read gets a read_signed_url via files_manager.read_signed_url(obj). Exactly one is populated per response.

ReportUpload has no CreateSpec/UpdateSpec in this module. Uploads come from the file-upload flow (References/File Upload), and the report metadata and internal_name are platform-maintained. See save behaviour.

Template specs (report/template/spec.py)

Spec class Role Fields / behaviour
TemplateBaseSpec shared id, name, status (TemplateStatusOptions), default_format (TemplateFormatOptions), description = "", options = {}. __exclude__ = ["facility"]
TemplateCreateSpec write · create None, slug_value: SlugType, template_data: str, template_type: str, context: str
TemplateUpdateSpec write · update Identical to TemplateCreateSpec (pass subclass)
TemplateReadSpec read · list Base fields + slug_config: dict, slug: str, template_type: str, context: str (no template_data)
TemplateRetrieveSpec read · detail Extends read spec; adds nested facility (FacilityBareMinimumSpec) and template_data: str

Write-side validation and side effects (TemplateCreateSpec):

  • slug_valueSlugType: 5–50 chars, pattern ^[a-zA-Z0-9][a-zA-Z0-9_-]*[a-zA-Z0-9]$.
  • template_type (field_validator) — non-empty and resolvable via ReportTypeRegistry, else Invalid report type.
  • context (field_validator) — non-empty and resolvable via DataPointRegistry, else Invalid Context type.
  • validate_report_type_and_context (model_validator, after):
    • both template_type and context must resolve, else Invalid report type or context;
    • their associating_model must match (template_type.associating_model == context.__associating_model__), else Report Type and Context are not compatible;
    • options is validated against GeneratorRegistry.get(default_format).options_model, so the allowed options keys depend on whether default_format is pdf or html.
  • perform_extra_deserialization (side effects): resolves facility external ID to the Facility row (get_object_or_404) when provided, and sets obj.slug = self.slug_value (the raw value; the model prefixes it in calculate_slug).

Read-side behaviour:

  • TemplateReadSpec.perform_extra_serialization: id ← external_id; slug_config ← obj.parse_slug(obj.slug) (see slug_config shape).
  • TemplateRetrieveSpec: additionally serializes facility via FacilityBareMinimumSpec when set.

Methods & save behaviour

ReportUpload.save()

On create (no id) or when internal_name is empty, save() generates a random internal_name from uuid4() plus the current epoch. This intermediate name decouples the stored object from the human-readable name, so a storage or data leak never exposes PII in file names.

  • Pass skip_internal_name=True to save() to bypass generation and keep an explicitly set internal_name.
  • If internal_name already held a value, its file extension is parsed (via parse_file_extension) and appended to the new randomized name.

ReportUpload.get_extension()

Returns the dotted extension(s) parsed from internal_name (e.g. .pdf), or "" if none. Surfaced as the extension field in read specs.

ReportUpload.file_type (property)

Alias for report_type, kept for compatibility with S3FilesManager.

Template slug methods (inherited from SlugBaseModel)

  • calculate_slug() — returns the prefixed slug: f-<facility.external_id>-<slug> when facility-scoped, else i-<slug>.
  • parse_slug(slug) — the inverse; returns slug_config (used by TemplateReadSpec).

API integration notes

  • Report files live in object storage (S3-compatible REPORT bucket) and are read through S3FilesManager; the database row is metadata only.
  • internal_name is platform-maintained — never set it from a client. Reserve skip_internal_name for controlled migrations. It is exposed only via ReportUploadRetrieveSpec.
  • A freshly created report returns an upload signed_url; subsequent reads return a read_signed_url instead.
  • Treat a report as available only once upload_completed is True.
  • Populate associating_id when generating a report so the file can be retrieved in its subject resource's context.
  • Template.options is validated server-side against the chosen format's generator schema, not an arbitrary bag; valid keys depend on default_format (pdf/html).
  • template_type and context must form a compatible pair (same associating_model). The default context is encounter_base, so templates target encounters unless changed.
  • Template.facility is nullable: null means instance-wide (i- slug), set means facility-scoped (f- slug).