Laboratory & radiology
Specimen Definition
A SpecimenDefinition is a reusable, facility-scoped template for a kind of specimen: what material to collect, how to prepare the patient, how to collect it, and how it's held in a container for testing. A lab maintains a repository of these,…
3.1 · versioned documentation · 7 min read
A SpecimenDefinition is a reusable, facility-scoped template for a kind of specimen: what material to collect, how to prepare the patient, how to collect it, and how it's held in a container for testing. A lab maintains a repository of these, and an Activity Definition or Service Request references one so the same container and handling rules apply every time. When a concrete Specimen is instantiated from a definition, the link is kept and the specimen copies the definition's data for history and integrity.
The Django model is only the storage layer: type_collected, patient_preparation, collection, and type_tested are opaque JSONFields. Their real structure — the enums, value-set bindings, and read/write API schemas — lives in the Pydantic resource specs documented below.
Source:
- Model:
care/emr/models/specimen_definition.py - Resource spec:
care/emr/resources/specimen_definition/spec.py - Value sets:
care/emr/resources/specimen_definition/valueset.py - Conversion helper:
care/emr/resources/specimen_definition/specimen.py - ViewSet:
care/emr/api/viewsets/specimen_definition.py
Models
| Model | Purpose |
|---|---|
SpecimenDefinition |
Reusable definition of a kind of specimen: what to collect, how to prepare the patient, how to collect it, and how it is contained and tested |
SpecimenDefinition extends SlugBaseModel: the MANSOQ EMR base with FACILITY_SCOPED = True, a facility-scoped slug, external_id, history/meta JSON, audit fields, and soft-delete semantics.
SpecimenDefinition fields
Identity & status
| Field | Type | Required | Notes |
|---|---|---|---|
facility |
FK → facility.Facility (PROTECT) |
model: optional | null=True, blank=True, default=None. Never client-supplied — the ViewSet sets it from the facility_external_id URL kwarg, so every API-created definition is facility-scoped. Excluded from both spec schemas (__exclude__ = ["facility"]) |
version |
IntegerField |
default 1 |
Definition version. Read-only over the API — exposed by SpecimenDefinitionReadSpec, never accepted on write |
slug |
CharField(255) |
yes (server-built) | Stored fully-qualified as f-<facility_external_id>-<slug_value> (see Methods & save behaviour). Clients send the bare slug_value instead |
title |
CharField(1024) |
yes | Human-readable name |
derived_from_uri |
TextField |
optional | null=True, blank=True. URI of the canonical/external definition this was derived from |
status |
CharField(255) |
yes | Lifecycle status, constrained by SpecimenDefinitionStatusOptions — see enum |
description |
TextField |
yes | Natural-language description (markdown) |
Collection & testing detail (JSON fields)
The model stores these as raw JSON; the spec defines their true shape and value-set bindings.
| Field | Model type | Spec type | Notes |
|---|---|---|---|
type_collected |
JSONField (null=True) |
Coding bound to Specimen Type Code value set |
Required on write. The kind of material collected. See value sets |
patient_preparation |
JSONField (default=list) |
list[Coding] bound to Prepare Patient Prior Specimen Code value set |
Defaults to []. Steps the patient follows before collection |
collection |
JSONField (null=True) |
Coding bound to Specimen Collection Code value set, optional |
The specimen collection procedure |
type_tested |
JSONField (default=dict) |
TypeTestedSpec (nested), optional |
Container, handling, and testing detail. See TypeTestedSpec |
Each bound coded field accepts a
Codingobject ({ system, version?, code, display? });ValueSetBoundCodingvalidates thecodeagainst the bound value set at de-serialization time.
Related models
| Type | Where | Shape |
|---|---|---|
Coding |
resources/common/coding.py |
{ system: str?, version: str?, code: str (required), display: str? }, extra="forbid" |
QuantitySpec |
spec.py | { value: Decimal (max_digits=20, decimal_places=0), unit: Coding } — decimal_places=0, so integer-valued |
MinimumVolumeSpec |
spec.py | { quantity: QuantitySpec?, string: str? }; a validator forbids supplying both |
ContainerSpec |
spec.py | { description: str?, capacity: QuantitySpec?, minimum_volume: MinimumVolumeSpec?, cap: Coding? (bound to Container Cap), preparation: str? } |
DurationSpec |
spec.py | { value: Decimal (max_digits=20, decimal_places=0), unit: Coding } — unit not yet restricted to datetime units |
RangeSpec |
spec.py | { low: QuantitySpec?, high: QuantitySpec? } |
HandlingSpec |
spec.py | { temperature_qualifier: HandlingConditionOptions?, temperature_range: RangeSpec?, max_duration: DurationSpec?, instruction: str? } |
TypeTestedSpec |
spec.py | see TypeTestedSpec below |
TypeTestedSpec
The structured shape of the type_tested JSON field. A definition holds a single container; for multiple containers per test, repeat the definition in the Activity Definition spec.
| Field | Type | Required | Notes |
|---|---|---|---|
is_derived |
bool |
yes | false for a primary specimen, true for a secondary/derived one |
preference |
PreferenceOptions |
yes | preferred / alternate — see enum |
container |
ContainerSpec |
optional | The specimen's container — description, capacity, minimum volume, cap, preparation |
requirement |
str |
optional | Delivery requirements / special handling (markdown) |
retention_time |
DurationSpec |
optional | Usual time this kind of specimen is retained |
single_use |
bool |
optional | Specimen for single use only |
handling |
HandlingSpec |
optional | Temperature qualifier/range, max duration, instruction |
Enums
SpecimenDefinitionStatusOptions values
| Value | Meaning |
|---|---|
draft |
Being authored, not yet in use |
active |
In use |
retired |
Withdrawn from use |
PreferenceOptions values
| Value | Meaning |
|---|---|
preferred |
Preferred specimen for the test |
alternate |
Acceptable alternative specimen |
HandlingConditionOptions values
| Value | Meaning |
|---|---|
room |
Room temperature |
refrigerated |
Refrigerated |
frozen |
Frozen |
Resource specs (API schema)
All specs build on EMRResource (serialize/de_serialize, perform_extra_serialization/perform_extra_deserialization). __exclude__ = ["facility"] keeps facility out of both directions. The ViewSet wires pydantic_model = SpecimenDefinitionWriteSpec for create/update and pydantic_read_model = SpecimenDefinitionReadSpec for list/retrieve.
| Spec class | Role | Fields / behaviour |
|---|---|---|
BaseSpecimenDefinitionSpec |
shared base | id, title, derived_from_uri, status, description, type_collected, patient_preparation, collection, type_tested. Holds the coded-field value-set bindings and the nested type_tested shape |
SpecimenDefinitionWriteSpec |
write · create & update | Base fields plus slug_value: SlugType. perform_extra_deserialization copies slug_value → obj.slug (the ViewSet then fully-qualifies it). facility, version, and the stored slug are never client-supplied |
SpecimenDefinitionReadSpec |
read · list & detail | Base fields plus version: int?, slug: str, slug_config: dict. perform_extra_serialization sets id = obj.external_id and slug_config = obj.parse_slug(obj.slug), decomposing the qualified slug into { facility, slug_value } |
Nested specs used by the schemas: TypeTestedSpec, ContainerSpec, MinimumVolumeSpec, QuantitySpec, DurationSpec, RangeSpec, HandlingSpec (see Related models).
Validation rules
slug_value(SlugType): string, length 5–50, matching^[a-zA-Z0-9][a-zA-Z0-9_-]*[a-zA-Z0-9]$(URL-safe; starts and ends alphanumeric).- Coded fields
type_collected,patient_preparation[*],collection, andcontainer.cap:ValueSetBoundCodingvalidates eachCoding.codeagainst its bound value set during de-serialization and rejects unknown codes. MinimumVolumeSpec:quantityandstringare mutually exclusive — supplying both raises"Only one of quantity or string should be provided".QuantitySpec/DurationSpecvalue:Decimal,max_digits=20,decimal_places=0(integer-valued).- Slug uniqueness (ViewSet
validate_data): the fully-qualifiedf-<facility>-<slug_value>must be unique within the facility (case-insensitive), else"Specimen Definition with this slug already exists."On update, the current record is excluded from the check.
Bound value sets
| Field | Value set | Slug | Source system(s) |
|---|---|---|---|
type_collected |
Specimen Type Code | system-specimen_type-code |
HL7 v2-0487 (unbounded include) |
patient_preparation[*] |
Prepare Patient Prior Specimen Code | system-prepare_patient_prior_specimen_code |
SNOMED CT, is-a 703763000 |
collection |
Specimen Collection Code | system-specimen_collection_code |
SNOMED CT (curated list: aspiration, biopsy, puncture, excision, scraping, clean-catch/timed/catheterized urine, coughed sputum, finger-prick) |
container.cap |
Container Cap | system-container_cap-code |
HL7 container-cap code system |
Methods & save behaviour
SpecimenDefinition overrides no save()/delete() and defines no custom methods. Slug helpers, audit fields, and soft-delete come from SlugBaseModel. The side effects below live in the resource spec and the ViewSet, not the model:
- Slug qualification (write). Clients send a bare
slug_value.SpecimenDefinitionWriteSpec.perform_extra_deserializationsetsobj.slug = slug_value; then the ViewSet'sperform_create/perform_updaterewrites it viaSpecimenDefinition.calculate_slug_from_facility(facility.external_id, slug)→f-<facility_external_id>-<slug_value>. - Facility binding (create).
perform_createsetsinstance.facilityfrom thefacility_external_idURL kwarg before saving. - Slug decomposition (read).
SpecimenDefinitionReadSpec.perform_extra_serializationexposesslug_config = obj.parse_slug(obj.slug), splitting the stored slug back into{ facility, slug_value }. - Specimen instantiation.
convert_sd_to_specimen(specimen.py) builds aSpecimenfrom a definition withstatus="available",specimen_type = definition.type_collected, andspecimen_definition = <definition>, preserving the link and a copy of the type.
API integration notes
- Endpoints are facility-scoped. The ViewSet supports create, retrieve, update, list, and upsert with
lookup_field = "slug". Every operation requiresfacility_external_idin the URL. - Authorization. Writes require
can_write_facility_specimen_definition; list/retrieve requirecan_list_facility_specimen_definitionon the facility — otherwise403 PermissionDenied. The queryset is filtered to the URL facility. - Filtering / ordering.
status(iexact) andtitle(icontains) filters; ordering bycreated_date/modified_date. - Write payload (
SpecimenDefinitionWriteSpec): sendslug_value,title,status,description, the coded fields (type_collected,patient_preparation,collection) asCodingobjects, andtype_tested. Don't sendfacility,version, or the qualifiedslug— they're server-managed. - Read payload (
SpecimenDefinitionReadSpec): addsid(=external_id),version, the qualifiedslug, andslug_config. - The model maps to the FHIR
SpecimenDefinitionresource. MANSOQ's spec is intentionally minimal: one container per definition, and behaviour changes bumpversionrather than mutate published records.
Related
- Reference: Specimen
- Reference: Service Request
- Reference: Observation Definition
- Reference: Activity Definition
- Reference: Base model
- Reference: Valueset
- Source: model on GitHub · spec on GitHub · value sets on GitHub
Source evidence
References are pinned to the private MANSOQ source repository. Repository access is required to open these links.