- Documentation
- /
- Deoh
- /
Application Overview
What DEOH models, and the ideas behind it
The Core Chain
The reason the application exists — a measurement that stays traceable in both directions:
SimilarExposureGroup
→ SamplingPlan (who gets sampled, for what, how often)
→ SamplingEvent (a shift in the field)
→ Sample (a pump, a filter, a wearer, a duration)
→ ChainOfCustody (custody of the filter, sealed and reconciled)
→ LaboratoryResult (a number on an accredited certificate)
→ ExposureAssessment (that number against a versioned limit)
→ ExposureExceedance
→ HygieneInvestigation
→ CorrectiveAction
→ ControlVerification (proof the change worked)
Read left to right it is a monitoring program. Read right to left it is evidence: every closed exceedance can be walked back to the calibration certificate of the pump that produced it. The point of putting both on one chain is that a control credited in a risk assessment can be compared against what the field actually measured — a control nobody verified, and a result nobody acted on, are both visible as gaps.
Supporting Chains
Organisation → Site → OperatingArea (self-nesting)
Organisation → Person → PersonRoleAssignment (WorkRole, Site)
Authority → SourceDocument → ExposureStandard (per HazardousSubstance)
MaterialProduct → ProductConstituent → HazardousSubstance
ExposureSource → ExposureSourceSubstance
SimilarExposureGroup → SegMember / SegTask
HygieneRiskAssessment → RiskAssessmentControl → Control
RespiratoryProtectionProgram → RespiratorType → FitTest
SamplingEquipment → EquipmentCalibration
Sample → SampleTaskInterval
SimilarExposureGroup → StatisticalExposureAssessment
ExposureAssessment / ExposureExceedance → WorkerNotification
Person → HealthSurveillanceReferral (restricted)
ComplaintObservation → HygieneInvestigation
TrainingRequirement → TrainingCompletion
DocumentEvidence (polymorphic attachment)
Key Concepts
The similar exposure group is the unit of decision, the sample is the unit of
evidence. You cannot sample everyone every day, so people doing comparable
work are grouped and the group is sampled representatively. That is why there
are two kinds of assessment: an ExposureAssessment is one person on one shift,
a StatisticalExposureAssessment is a decision about the whole group over a
period. The demo carries both for the crusher crew, and they disagree in a
useful way — individual results look mixed, the group decision is
POORLY_CONTROLLED.
Exposure standards are versioned and sourced, not constants. An
ExposureStandard points at the SourceDocument that published it and the
Authority behind that, and carries effective_from / effective_to. The demo
holds the current 0.05 mg/m³ silica limit and the superseded 2019 value of
0.1 mg/m³, plus an internal action level at half the standard. An assessment
records which limit was applied, so re-reading an old decision does not silently
apply today's rule to yesterday's data.
Raw measurement and calculated exposure are separate columns. A Sample
keeps pre-flow, post-flow, average flow, duration and volume. A
LaboratoryResult keeps the reported value, detection limit, reporting limit
and uncertainty. An ExposureAssessment keeps the raw value, the adjustment
factor, the shift duration, the limit actually applied and the resulting
percentage. Nothing is overwritten with its own derivative, so the arithmetic
stays auditable.
Validity is a first-class state. validity_status on a sample is
PENDING → VALID / QUALIFIED / INVALID, driven by flow drift: over 5% qualifies
the result, over 10% invalidates it. Invalid samples are kept — excluded
from compliance decisions and from the statistics, retained for audit and
quality review. The demo has one of each: a supervisor's sample qualified at
6.2% drift, and a March sample invalidated at 12.4%.
Task intervals are what make a number explainable. A shift-long result says
someone was overexposed. SampleTaskInterval slices the same shift into what
they were actually doing — control room, liner support, chute clearing — so the
investigation can point at the interval carrying the mass rather than at the
person. In the demo, three hours inside the crusher shell with the spray bars
isolated is the answer, and it is visible in the data before anyone forms an
opinion.
Controls carry a required performance, and verification is a separate
record. RiskAssessmentControl states what "working" means for a control in
the context of a specific assessment; ControlVerification records someone
going and checking, with an observed performance and a result of EFFECTIVE,
PARTIALLY_EFFECTIVE, INEFFECTIVE or NOT_VERIFIED. NOT_VERIFIED is a
recorded outcome, not a blank — the demo uses it for a ventilation survey that
was deferred, so the gap is visible instead of absent.
An exceedance cannot close on a completed action. It closes on a verified
control, approved by a named person, with a closure timestamp. The demo's
crusher exceedance runs OPEN → CONTAINED → INVESTIGATING → ACTIONING →
VERIFICATION → CLOSED over seven weeks, and the closing evidence is a
measurement showing static dust at the discharge falling from 2.9 to
0.7 mg/m³ — not a signature on the action.
The respirator is credited, not assumed. RespiratorType carries an
assigned protection factor, FitTest records whether a specific person achieved
a seal on a specific model, and the Sample records which respirator was
actually worn. The demo's whole finding turns on that: two operators on the same
shift, in the same area, one in a P2 disposable and one in the PAPR the risk
assessment credits, with results a factor of five apart.
Health surveillance is referral status only. HealthSurveillanceReferral
holds the reason, the appointment dates, the fitness outcome
(NO_RESTRICTION … PERMANENT_RESTRICTION) and any restriction summary needed
to roster someone safely — all at HIGHLY_RESTRICTED privacy. There is no field
anywhere in the schema for a diagnosis, a clinical note or a test result,
because the boundary is a design decision rather than an access-control setting.
Notification is an obligation with a state. WorkerNotification runs
PREPARED → DELIVERED → ACKNOWLEDGED, with UNABLE_TO_CONTACT as a real
outcome. The demo includes a group-result letter to a former operator that came
back undelivered — the obligation does not disappear when the person leaves.