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_RESTRICTIONPERMANENT_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.