- Documentation
- /
- Deoh
- /
- System Diagram
System Diagram
The DEOH data model, mapped — 46 models, 125 relationships
🔍 For serious exploration, open the Interactive Diagram Viewer — pan, mouse-wheel zoom, fit-to-screen, full-screen, per-module tabs, the full 125-relationship ERD, and a focus mode that draws one model and its neighbours at a chosen depth. Person (32 links), Site (15) and OperatingArea (10) are context columns that fan out to nearly everything, so the viewer hides those by default — a toggle brings them back. The diagrams below are the readable, static summary of the principal links only.
Architecture Overview
Ten areas mirror the sidebar menu. Every arrow means "one row of the left model is referenced by many rows of the right model".
flowchart LR
subgraph REF["📚 Reference"]
Authority --> SourceDocument --> ExposureStandard
end
subgraph HAZ["☣️ Hazards & sources"]
HazardousSubstance
MaterialProduct --> ProductConstituent
ExposureSource --> ExposureSourceSubstance
end
subgraph SEG["👥 Exposure groups & risk"]
SimilarExposureGroup --> SegMember
SimilarExposureGroup --> SegTask
HygieneRiskAssessment --> RiskAssessmentControl --> Control
end
subgraph RPE["😷 Respiratory protection"]
RespiratoryProtectionProgram --> RespiratorType --> FitTest
end
subgraph SMP["🧪 Sampling & field work"]
SamplingProgram --> SamplingPlan --> SamplingEvent --> Sample
Sample --> SampleTaskInterval
SamplingMethod
SamplingEquipment --> EquipmentCalibration
end
subgraph LAB["📦 Custody & laboratory"]
ChainOfCustody --> ChainOfCustodySample
LaboratoryResult
end
subgraph ASM["📈 Exposure assessment"]
StatisticalExposureAssessment --> ExposureAssessment
end
subgraph RSP["🚨 Exceedance & response"]
ExposureExceedance --> CorrectiveAction --> ControlVerification
HygieneInvestigation
ComplaintObservation
end
subgraph WRK["🧑🏭 Worker health & training"]
WorkerNotification
HealthSurveillanceReferral
TrainingRequirement --> TrainingCompletion
end
HazardousSubstance --> ExposureStandard
HazardousSubstance --> ExposureSourceSubstance
HazardousSubstance --> SamplingPlan
MaterialProduct --> ExposureSource
ExposureSource --> SegTask
SimilarExposureGroup --> SamplingPlan
SimilarExposureGroup --> HygieneRiskAssessment
SimilarExposureGroup --> StatisticalExposureAssessment
SamplingMethod --> SamplingPlan
SamplingEquipment --> Sample
RespiratorType --> Sample
Sample --> ChainOfCustodySample
Sample --> LaboratoryResult
SamplingEvent --> ChainOfCustody
LaboratoryResult --> ExposureAssessment
ExposureStandard --> ExposureAssessment
ExposureAssessment --> ExposureExceedance
ExposureAssessment --> WorkerNotification
HygieneInvestigation --> ExposureExceedance
HygieneInvestigation --> CorrectiveAction
ComplaintObservation --> HygieneInvestigation
Control --> ControlVerification
(ComplaintObservation → HygieneInvestigation is drawn in the causal
direction; the foreign key sits on ComplaintObservation pointing at the
investigation it fed.)
The Core Chain
Measurement on the left, consequence on the right. This is the diagram the application exists to make true.
erDiagram
SimilarExposureGroup ||--o{ SamplingPlan : "seg_id"
SamplingProgram ||--o{ SamplingPlan : "sampling_program_id"
SamplingMethod ||--o{ SamplingPlan : "sampling_method_id"
SamplingPlan ||--o{ SamplingEvent : "sampling_plan_id"
SamplingEvent ||--o{ Sample : "sampling_event_id"
Sample ||--o{ SampleTaskInterval : "sample_id"
SamplingEvent ||--o{ ChainOfCustody : "sampling_event_id"
ChainOfCustody ||--o{ ChainOfCustodySample : "chain_of_custody_id"
Sample ||--o{ ChainOfCustodySample : "sample_id"
Sample ||--o{ LaboratoryResult : "sample_id"
LaboratoryResult ||--o{ ExposureAssessment : "laboratory_result_id"
ExposureStandard ||--o{ ExposureAssessment : "exposure_standard_id"
StatisticalExposureAssessment ||--o{ ExposureAssessment : "statistical_group_result_id"
ExposureAssessment ||--o{ ExposureExceedance : "exposure_assessment_id"
HygieneInvestigation ||--o{ ExposureExceedance : "investigation_id"
ExposureExceedance ||--o{ CorrectiveAction : "exceedance_id"
CorrectiveAction ||--o{ ControlVerification : "corrective_action_id"
Control ||--o{ ControlVerification : "control_id"
Note that Sample reaches ExposureAssessment two ways: directly
(sample_id) and through the laboratory result. The direct link is what keeps
a direct-reading measurement — the post-blast gas peak in the demo — assessable
even though no filter was ever sent to a laboratory.
Hazards, Sources and Exposure Groups
What is emitted, from where, and who is standing in it.
erDiagram
Site ||--o{ OperatingArea : "site_id"
OperatingArea ||--o{ OperatingArea : "parent_area_id"
Site ||--o{ MaterialProduct : "site_id"
MaterialProduct ||--o{ ProductConstituent : "material_product_id"
HazardousSubstance ||--o{ ProductConstituent : "substance_id"
Site ||--o{ ExposureSource : "site_id"
OperatingArea ||--o{ ExposureSource : "operating_area_id"
MaterialProduct ||--o{ ExposureSource : "material_product_id"
ExposureSource ||--o{ ExposureSourceSubstance : "exposure_source_id"
HazardousSubstance ||--o{ ExposureSourceSubstance : "substance_id"
Site ||--o{ SimilarExposureGroup : "site_id"
WorkRole ||--o{ SimilarExposureGroup : "primary_work_role_id"
SimilarExposureGroup ||--o{ SegMember : "seg_id"
SimilarExposureGroup ||--o{ SegTask : "seg_id"
ExposureSource ||--o{ SegTask : "exposure_source_id"
SegTask is the join that matters: it links a group's tasks to the specific
sources those tasks put them near, which is what makes a sampling plan
defensible rather than arbitrary.
Standards, Risk and Controls
erDiagram
Authority ||--o{ SourceDocument : "authority_id"
SourceDocument ||--o{ ExposureStandard : "source_document_id"
HazardousSubstance ||--o{ ExposureStandard : "substance_id"
ExposureStandard ||--o{ StatisticalExposureAssessment : "exposure_standard_id"
SimilarExposureGroup ||--o{ HygieneRiskAssessment : "seg_id"
HazardousSubstance ||--o{ HygieneRiskAssessment : "substance_id"
ExposureSource ||--o{ HygieneRiskAssessment : "exposure_source_id"
HygieneRiskAssessment ||--o{ RiskAssessmentControl : "risk_assessment_id"
Control ||--o{ RiskAssessmentControl : "control_id"
HygieneRiskAssessment ||--o{ CorrectiveAction : "risk_assessment_id"
Control ||--o{ CorrectiveAction : "control_id"
An exposure standard is never a bare number: it hangs off the document that published it, which hangs off the authority that issued it, and it carries its own effective dates.
Equipment, Respiratory Protection and the Field Record
erDiagram
Organisation ||--o{ SamplingEquipment : "organisation_id"
SamplingEquipment ||--o{ EquipmentCalibration : "equipment_id"
SamplingEquipment ||--o{ Sample : "pump_equipment_id"
SamplingEquipment ||--o{ Sample : "calibrator_equipment_id"
Site ||--o{ RespiratoryProtectionProgram : "site_id"
RespiratoryProtectionProgram ||--o{ RespiratorType : "program_id"
RespiratorType ||--o{ FitTest : "respirator_type_id"
Person ||--o{ FitTest : "person_id"
RespiratorType ||--o{ Sample : "rpe_type_id"
Person ||--o{ Sample : "person_id"
SimilarExposureGroup ||--o{ Sample : "seg_id"
Two edges run from SamplingEquipment into Sample — the pump that drew the
air and the calibrator that checked its flow. Both are recorded because the
validity of the result depends on both.
Response, Notification and Health Surveillance
erDiagram
ExposureAssessment ||--o{ ExposureExceedance : "exposure_assessment_id"
HygieneInvestigation ||--o{ CorrectiveAction : "investigation_id"
HygieneInvestigation ||--o{ ComplaintObservation : "investigation_id"
ExposureSource ||--o{ ComplaintObservation : "exposure_source_id"
ExposureAssessment ||--o{ WorkerNotification : "exposure_assessment_id"
ExposureExceedance ||--o{ WorkerNotification : "exceedance_id"
Person ||--o{ WorkerNotification : "person_id"
Person ||--o{ HealthSurveillanceReferral : "person_id"
HazardousSubstance ||--o{ HealthSurveillanceReferral : "substance_id"
TrainingRequirement ||--o{ TrainingCompletion : "training_requirement_id"
Person ||--o{ TrainingCompletion : "person_id"
HealthSurveillanceReferral connects to a person and a substance and stops
there. There is no edge into any clinical record, because there is no clinical
record in this system.
Reading the Diagrams
A ||--o{ Bmeans one row of A is referenced by many rows of B (the label is the foreign-key column on B).- These static diagrams show principal links only — the context columns
present on nearly every model (
*_person_id,site_id,operating_area_id) are omitted for readability. The interactive viewer's Full ERD shows all 125, with the noise toggleable. Personhas 32 of the 125 relationships because the model records who at every step: samples, calibrates, assesses, approves, verifies, investigates, owns, delivers, is exposed. That accountability trail is the substance of hygiene evidence, not incidental metadata.Sample(4 outbound links) andExposureAssessment(2) are narrow on paper but sit at the waist of the chain — the viewer's Focus mode defaults toSample.- No foreign key was dropped in translation. The pack has no true reference
cycles; models were only reordered so every
refpoints at an already-defined table (HazardousSubstancebeforeExposureStandard,SamplingMethodbeforeSamplingPlan,StatisticalExposureAssessmentbeforeExposureAssessment,HygieneInvestigationbeforeExposureExceedance). - The pack's
AuditLogmodel was dropped — the framework provides audit history — andDocumentEvidencekeeps a deliberately polymorphicentity_type/entity_idpair rather than a foreign key, so it can attach to any record.
Diagrams are derived from the generated schema metadata
(apps/deoh/generated/json/deoh_relationship_metadata.json). If the DSL schema
changes, regenerate the metadata (python -m codegen.cli all deoh) and update
this page and the viewer's embedded data.