Laboratory & radiology
Specimen
A Specimen is a physical sample — blood, tissue, swab — collected from a patient for laboratory analysis. You create one to fulfill a lab order, then update it as the specimen moves through collection, receipt, and processing. Several of the…
3.1 · versioned documentation · 8 min read
A Specimen is a physical sample — blood, tissue, swab — collected from a patient for laboratory analysis. You create one to fulfill a lab order, then update it as the specimen moves through collection, receipt, and processing.
Several of the model's fields (care/emr/models/specimen.py) are opaque JSONFields whose structure lives only in the Pydantic resource specs (care/emr/resources/specimen/). The specs define the enums, nested JSON shapes, value-set bindings, validation, and the read/write schemas, so you need both layers to work with this resource.
Source:
- Model:
care/emr/models/specimen.py - Resource specs:
care/emr/resources/specimen/spec.py,valueset.py - Viewset:
care/emr/api/viewsets/specimen.py
Models
Specimen extends EMRBaseModel, the shared MANSOQ EMR base providing external_id, audit fields, soft-delete via deleted, and history/meta JSON.
Specimen fields
Identity & status
| Field | Model type | Spec shape / values | Notes |
|---|---|---|---|
accession_identifier |
CharField(255), db_index=True |
str, default "" |
Lab accession/barcode identifier; indexed for lookup. Defaults to "", so it's optional on write. |
status |
CharField(20) |
SpecimenStatusOptions enum |
Required on base/create, optional on update. See status values. |
specimen_type |
JSONField |
Coding bound to value set system-specimen_type-code |
Kind of material collected, as a single coding. Required on base/create, optional on update. |
Clinical links
| Field | Model type | Notes |
|---|---|---|
facility |
FK → Facility |
PROTECT, nullable; the facility the specimen belongs to. Excluded from all specs (__exclude__). |
patient |
FK → Patient |
CASCADE; the patient the specimen was collected from. Set on create via subject_patient (UUID). |
encounter |
FK → Encounter |
CASCADE, nullable; the encounter during which collection occurred. Set on create via subject_encounter (UUID). |
service_request |
FK → ServiceRequest |
CASCADE, nullable; the order this specimen fulfills. Set on create via request (UUID); excluded from base/read field copy. |
specimen_definition |
FK → SpecimenDefinition |
CASCADE, nullable; the definition/template this specimen was derived from. |
Collection & processing
| Field | Model type | Spec shape | Notes |
|---|---|---|---|
collection |
JSONField default=dict |
CollectionSpec | None, default None |
A single collection event: collector, method, body site, quantity, fasting. See CollectionSpec. |
received_time |
DateTimeField, nullable |
str | None, default None |
When the lab received the specimen; ISO 8601 string in the spec. |
condition |
JSONField default=list |
list[Coding] bound to system-specimen-condition-code, default [] |
Coded conditions/state of the specimen on receipt. |
processing |
JSONField default=list |
list[ProcessingSpec], default [] |
Processing steps applied. See ProcessingSpec. |
note |
TextField, nullable |
str | None, default None |
Free-text annotation. |
Enums
SpecimenStatusOptions values
The status enum (str). Two things to watch: entered_in_error uses an underscore rather than the FHIR hyphen, and draft is a MANSOQ addition with no FHIR equivalent.
| Value | Meaning |
|---|---|
draft |
MANSOQ-specific; not from FHIR |
available |
Specimen available for testing |
unavailable |
Specimen no longer available |
unsatisfactory |
Specimen unsuitable for testing |
entered_in_error |
Recorded in error |
Nested JSON specs
These define the real structure behind the model's JSONFields.
CollectionSpec
Shape of the collection JSON field — a single collection event.
| Field | Type | Required | Notes |
|---|---|---|---|
collector |
UUID4 | None |
optional | External ID of the collecting User. Validated to exist (User.objects.filter(external_id=...)), else rejected with "Collector user not found". Serialized output adds collector_object, the resolved UserSpec from cache. |
collected_date_time |
datetime | None |
optional | When the specimen was collected. |
quantity |
QuantitySpec | None |
optional | Amount collected. See QuantitySpec. |
method |
Coding | None bound to system-collection-method-code |
optional | Collection method (SNOMED). See collection method values. |
procedure |
UUID4 | None |
optional | Reference to a procedure. |
body_site |
Coding | None bound to system-body-site-observation |
optional | Anatomical site collected from. |
fasting_status_codeable_concept |
Coding | None bound to system-fasting-status-code |
optional | Patient fasting status (HL7 v2-0916). |
fasting_status_duration |
DurationSpec | None |
optional | Fasting duration. See DurationSpec. |
ProcessingSpec
One element of the processing list.
| Field | Type | Required | Notes |
|---|---|---|---|
description |
str |
required | Description of the processing step. |
method |
Coding | None bound to system-specimen-processing-method-code |
optional | Processing method (SNOMED, descendants of 9265001). |
performer |
UUID4 | None |
optional | External ID of the performing User. Validated to exist, else rejected with "Performer user not found". Serialized output adds performer_object, the resolved UserSpec from cache. |
time_date_time |
str |
required | When the step was performed. |
QuantitySpec
| Field | Type | Notes |
|---|---|---|
value |
Decimal |
max_digits=20, decimal_places=0, so integer-valued. |
unit |
Coding |
Required unit coding. |
This is a specimen-local quantity spec, distinct from the shared Quantity common type — which allows decimal_places=6, optional value/unit, plus code/meta.
DurationSpec
| Field | Type | Notes |
|---|---|---|
value |
int |
Duration magnitude. |
unit |
Coding |
Required unit coding. |
Coding (shared common type)
Every coded field above uses Coding (extra="forbid"):
| Field | Type | Required |
|---|---|---|
system |
str | None |
optional |
version |
str | None |
optional |
code |
str |
required |
display |
str | None |
optional |
Bound value sets
Coded fields bind to MANSOQ value sets via ValueSetBoundCoding[<slug>], which validates the submitted code against the value set at write time.
| Field | Value set | Slug | Source system |
|---|---|---|---|
specimen_type |
Specimen Type Code | system-specimen_type-code |
HL7 v2-0487 |
condition[] |
Specimen Condition | system-specimen-condition-code |
HL7 v2-0493 (v2.0.0) |
collection.method |
Collection Method | system-collection-method-code |
SNOMED CT (enumerated, see below) |
collection.body_site |
Body Site | system-body-site-observation |
(observation body-site value set) |
collection.fasting_status_codeable_concept |
Fasting Status | system-fasting-status-code |
HL7 v2-0916 (v2.0.0) |
processing[].method |
Specimen Processing Method | system-specimen-processing-method-code |
SNOMED CT (< 9265001) |
Collection Method value set
The system-collection-method-code value set enumerates these SNOMED CT codes:
| Code | Display |
|---|---|
129316008 |
Aspiration - action |
129314006 |
Biopsy - action |
129300006 |
Puncture - action |
129304002 |
Excision - action |
129323009 |
Scraping - action |
73416001 |
Urine specimen collection, clean catch |
225113003 |
Timed urine collection |
70777001 |
Urine specimen collection, catheterized |
386089008 |
Collection of coughed sputum |
278450005 |
Finger-prick sampling |
The other coded fields aren't enumerated. Fasting Status (http://terminology.hl7.org/CodeSystem/v2-0916), Specimen Condition (v2-0493), Specimen Type (v2-0487), Body Site, and Specimen Processing Method (SNOMED < 9265001) are pulled in by code-system reference or filter rather than a fixed concept list.
Resource specs (API schema)
Every spec extends EMRResource (care/emr/resources/base.py), which provides serialize (DB object → pydantic, via get_database_mapping) and de_serialize (pydantic → DB object), plus the perform_extra_serialization / perform_extra_deserialization hooks. __exclude__ = ["facility", "request"] on the base keeps those fields from being copied field-for-field.
| Spec class | Role | Key fields / behaviour |
|---|---|---|
BaseSpecimenSpec |
shared base | id, accession_identifier, status (required), specimen_type (required), received_time, collection, processing, condition, note. __model__ = Specimen, __exclude__ = ["facility", "request"]. |
SpecimenCreateSpec |
write · create | Adds subject_patient: UUID4 (required), subject_encounter: UUID4 (required), request: UUID4 | None. Wires up the FK links on creation. |
SpecimenUpdateSpec |
write · update | Relaxes status and specimen_type to optional (| None) for partial updates. |
SpecimenReadSpec |
read · list | Adds specimen_definition: dict | None. perform_extra_serialization sets id = external_id and, when present, embeds the linked specimen definition via SpecimenDefinitionReadSpec.serialize(...).to_json(). |
SpecimenRetrieveSpec |
read · detail | Extends SpecimenReadSpec, adds service_request: dict | None; serialization additionally embeds the linked service request via ServiceRequestReadSpec.serialize(...).to_json(). |
Nested specs (defined in the same module): CollectionSpec, ProcessingSpec, QuantitySpec, DurationSpec — see Nested JSON specs.
Validation & server behaviour
collection.collectorandprocessing[].performermust reference existing users byexternal_id; serialization then injects the resolvedcollector_object/performer_object(cachedUserSpec).- Coded fields are validated against their value sets on write (
ValueSetBoundCoding); an unbound or invalidcodeis rejected. - Read specs embed related resources — specimen definition on list and detail, service request on detail — as nested JSON rather than bare IDs.
- The base spec doesn't override
perform_extra_deserialization, so nostatus_history-style server-side history is appended on write, unlike some other clinical resources.
API integration notes
- Aligned with the FHIR
Specimenresource, though some MANSOQ field names differ. On create,subject_patient/subject_encounter/requestmap to thepatient/encounter/service_requestFKs. - The viewset (
SpecimenViewSet) mixes in retrieve and update only (EMRRetrieveMixin,EMRUpdateMixin) — so you get read/detail/list and update, withpydantic_model = BaseSpecimenSpec,pydantic_update_model = SpecimenUpdateSpec,pydantic_read_model = SpecimenReadSpec,pydantic_retrieve_model = SpecimenRetrieveSpec. - Filtering:
accession_identifier(icontains); ordering oncreated_date/modified_date. - The custom action
POST retrieve_by_accession_identifierlooks up a specimen byaccession_identifier(must be ≥ 5 chars), returning aSpecimenRetrieveSpec. It raises a validation error on no match or multiple matches. - Authorization keys off the linked
service_request: read viacan_read_specimen, write viacan_write_specimen. One service request may own multiple specimens.
Related models
facility → FK Facility (PROTECT, nullable) [excluded from specs]
patient → FK Patient (CASCADE) [set via subject_patient]
encounter → FK Encounter (CASCADE, nullable) [set via subject_encounter]
service_request → FK ServiceRequest (CASCADE, nullable) [set via request]
specimen_definition → FK SpecimenDefinition (CASCADE, nullable)
A Specimen is created to fulfill a ServiceRequest (a lab order) and may be shaped by a SpecimenDefinition describing the expected sample. Results derived from a specimen surface through a DiagnosticReport.
Related
- Reference: Specimen Definition
- Reference: Service Request
- Reference: Diagnostic Report
- Reference: Patient
- Reference: Base Model
- Source (model): specimen.py on GitHub
- Source (spec): specimen/spec.py on GitHub
- Source (value sets): specimen/valueset.py on GitHub
Source evidence
References are pinned to the private MANSOQ source repository. Repository access is required to open these links.