System Diagram

The Emergency Preparedness and Exercises data model, mapped — 47 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 (41 links), MineSite (13) and Document (13) are role/ownership columns that fan out to nearly everything, so the viewer hides those links 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 REQ["📜 Requirements"]
        SourceAuthority --> SourceDocument --> Requirement
    end

    subgraph PLAN["📕 Plans & command"]
        EmergencyPlan --> EmergencyProcedure
        EmergencyPlan --> CommandStructure --> CommandRoleAssignment
        EmergencyControlLocation --> CommandStructure
        EmergencyPlan --> PlanReview
    end

    subgraph SCEN["⚠️ Scenarios"]
        EmergencyScenario --> ScenarioControl
    end

    subgraph RES["🧰 Resources & readiness"]
        EmergencyResource --> ResourceInspection
        ReadinessAssessment --> ReadinessAssessmentItem
    end

    subgraph COMP["🎓 Competency"]
        Competency --> EmergencyRoleCompetency
        Competency --> PersonCompetency
    end

    subgraph PROG["📅 Exercise program"]
        EmergencyExercise --> ExerciseObjective
        EmergencyExercise --> ExerciseHazardAssessment
        EmergencyExercise --> ExerciseParticipant
        EmergencyExercise --> ExerciseLocation
    end

    subgraph COND["🚨 Exercise conduct"]
        ExerciseInject --> ExerciseEventLog
        ExerciseMuster --> MusterPerson
        ExerciseCommunication
        SimulatedCasualty
        ResourceDeployment
    end

    subgraph IMPR["🔧 Improvement"]
        ExerciseObservation
        ExerciseDebrief
        ExerciseFinding --> CorrectiveAction
        ExerciseReport
    end

    Requirement --> PlanRequirementLink
    EmergencyPlan --> PlanRequirementLink
    EmergencyScenario --> ScenarioPlanLink
    EmergencyPlan --> ScenarioPlanLink
    EmergencyResource --> ResourceScenarioLink
    EmergencyScenario --> ResourceScenarioLink
    EmergencyScenario --> ReadinessAssessment
    EmergencyScenario --> EmergencyExercise
    EmergencyPlan --> EmergencyExercise
    EmergencyExercise --> ExerciseInject
    EmergencyExercise --> ExerciseMuster
    EmergencyExercise --> ExerciseCommunication
    EmergencyExercise --> SimulatedCasualty
    EmergencyExercise --> ResourceDeployment
    EmergencyResource --> ResourceDeployment
    EmergencyExercise --> ExerciseObservation
    ExerciseObjective --> ExerciseObservation
    EmergencyExercise --> ExerciseDebrief
    EmergencyExercise --> ExerciseFinding
    EmergencyExercise --> ExerciseReport
    ExerciseFinding --> PlanReview

(The ExerciseFinding → PlanReview arrow is the conceptual loop, not a foreign key — a finding triggers a review via PlanReview.review_trigger = EXERCISE and trigger_reference, which is a deliberate soft link so a review can also be triggered by an incident, a legislative change or the calendar.)


Requirements and Plan Coverage

Where the obligations come from, and how a plan demonstrates it meets them.

erDiagram
    SourceAuthority ||--o{ SourceDocument : "source_authority_id"
    SourceDocument ||--o{ SourceDocument : "superseded_by_id"
    SourceDocument ||--o{ Requirement : "source_document_id"
    Requirement ||--o{ PlanRequirementLink : "requirement_id"
    EmergencyPlan ||--o{ PlanRequirementLink : "emergency_plan_id"
    Document ||--o{ SourceDocument : "local_document_id"
    Document ||--o{ PlanRequirementLink : "evidence_document_id"

Plans, Procedures and Command

The plan spine. A plan revision supersedes the previous one rather than overwriting it, and a command structure names both a primary and an alternate control location, and both a primary and an alternate position per role.

erDiagram
    EmergencyPlan ||--o{ EmergencyPlan : "supersedes_plan_id"
    EmergencyPlan ||--o{ EmergencyProcedure : "emergency_plan_id"
    EmergencyPlan ||--o{ CommandStructure : "emergency_plan_id"
    EmergencyPlan ||--o{ PlanReview : "emergency_plan_id"
    EmergencyControlLocation ||--o{ CommandStructure : "primary_control_location_id"
    EmergencyControlLocation ||--o{ CommandStructure : "alternate_control_location_id"
    CommandStructure ||--o{ CommandRoleAssignment : "command_structure_id"
    Position ||--o{ CommandRoleAssignment : "primary_position_id"
    Position ||--o{ CommandRoleAssignment : "alternate_position_id"
    Position ||--o{ EmergencyProcedure : "responsible_position_id"
    OperatingArea ||--o{ EmergencyControlLocation : "operating_area_id"

Scenarios, Controls and Resources

Credible scenarios drive everything downstream: the controls that must hold, the plans that address them, the resources that must be available, and the readiness assessments that rate the whole picture.

erDiagram
    EmergencyScenario ||--o{ ScenarioControl : "emergency_scenario_id"
    EmergencyScenario ||--o{ ScenarioPlanLink : "emergency_scenario_id"
    EmergencyPlan ||--o{ ScenarioPlanLink : "emergency_plan_id"
    EmergencyScenario ||--o{ ResourceScenarioLink : "emergency_scenario_id"
    EmergencyResource ||--o{ ResourceScenarioLink : "emergency_resource_id"
    EmergencyResource ||--o{ ResourceInspection : "emergency_resource_id"
    EmergencyScenario ||--o{ ReadinessAssessment : "emergency_scenario_id"
    ReadinessAssessment ||--o{ ReadinessAssessmentItem : "readiness_assessment_id"
    Position ||--o{ ScenarioControl : "responsible_position_id"
    Position ||--o{ EmergencyResource : "responsible_position_id"
    OperatingArea ||--o{ EmergencyScenario : "operating_area_id"
    OperatingArea ||--o{ EmergencyResource : "operating_area_id"

Competency Coverage

EmergencyRoleCompetency is the requirement side (what a command role needs, and how many people must hold it); PersonCompetency is the evidence side. Note that neither joins to CommandRoleAssignment directly — the link is the command_role enum value, so coverage is computed rather than navigated.

erDiagram
    Competency ||--o{ EmergencyRoleCompetency : "competency_id"
    Competency ||--o{ PersonCompetency : "competency_id"
    Person ||--o{ PersonCompetency : "person_id"
    Person ||--o{ PersonCompetency : "verified_by_person_id"
    Person ||--o{ PersonPosition : "person_id"
    Position ||--o{ PersonPosition : "position_id"

The Exercise Spine

Everything about an exercise hangs off EmergencyExercise — twenty-one relationships, the most of any model except Person.

erDiagram
    EmergencyScenario ||--o{ EmergencyExercise : "emergency_scenario_id"
    EmergencyPlan ||--o{ EmergencyExercise : "emergency_plan_id"
    EmergencyExercise ||--o{ ExerciseObjective : "emergency_exercise_id"
    EmergencyExercise ||--o{ ExerciseHazardAssessment : "emergency_exercise_id"
    EmergencyExercise ||--o{ ExerciseParticipant : "emergency_exercise_id"
    EmergencyExercise ||--o{ ExerciseLocation : "emergency_exercise_id"
    EmergencyControlLocation ||--o{ ExerciseLocation : "emergency_control_location_id"
    OperatingArea ||--o{ ExerciseLocation : "operating_area_id"

Exercise Conduct

The timed record of what actually happened. ExerciseEventLog.related_inject_id is what ties a controller's inject to the response it provoked.

erDiagram
    EmergencyExercise ||--o{ ExerciseInject : "emergency_exercise_id"
    EmergencyExercise ||--o{ ExerciseEventLog : "emergency_exercise_id"
    ExerciseInject ||--o{ ExerciseEventLog : "related_inject_id"
    EmergencyExercise ||--o{ ExerciseCommunication : "emergency_exercise_id"
    EmergencyContact ||--o{ ExerciseCommunication : "recipient_contact_id"
    EmergencyExercise ||--o{ ExerciseMuster : "emergency_exercise_id"
    ExerciseMuster ||--o{ MusterPerson : "exercise_muster_id"
    EmergencyExercise ||--o{ SimulatedCasualty : "emergency_exercise_id"
    EmergencyExercise ||--o{ ResourceDeployment : "emergency_exercise_id"
    EmergencyResource ||--o{ ResourceDeployment : "emergency_resource_id"

Evaluation and Improvement

The closing loop. A finding points at whatever it implicates — the objective, the plan, the procedure, the resource — so improvement is traceable to the thing that failed.

erDiagram
    EmergencyExercise ||--o{ ExerciseObservation : "emergency_exercise_id"
    ExerciseObjective ||--o{ ExerciseObservation : "exercise_objective_id"
    EmergencyExercise ||--o{ ExerciseDebrief : "emergency_exercise_id"
    EmergencyExercise ||--o{ ExerciseFinding : "emergency_exercise_id"
    ExerciseObjective ||--o{ ExerciseFinding : "related_objective_id"
    EmergencyPlan ||--o{ ExerciseFinding : "related_plan_id"
    EmergencyProcedure ||--o{ ExerciseFinding : "related_procedure_id"
    EmergencyResource ||--o{ ExerciseFinding : "related_resource_id"
    ExerciseFinding ||--o{ CorrectiveAction : "exercise_finding_id"
    Position ||--o{ CorrectiveAction : "assigned_position_id"
    EmergencyExercise ||--o{ ExerciseReport : "emergency_exercise_id"

Application Flow — Preparedness to Improvement

The order in which the system is actually used, and what gates each step.

flowchart TD
    A["📜 Register source documents<br/>and extract requirements"] --> B["📕 Write / revise the emergency plan<br/><i>PlanRequirementLink shows coverage</i>"]
    B --> C["⚠️ Register credible scenarios<br/>and their controls"]
    C --> D["🔗 Link scenarios to plans<br/>and to required resources"]
    D --> E["🧰 Inspect resources<br/><i>available · functional · in date · accessible</i>"]
    D --> F["🎓 Verify role competency coverage<br/><i>minimum people current per role</i>"]
    E --> G{"📊 Readiness assessment<br/>item by item"}
    F --> G
    G -->|"gap rated NOT_TESTED<br/>or NOT_READY"| H["📅 Schedule an exercise<br/>against the untested control"]
    G -->|"defect or shortfall"| P["🔧 Corrective action"]
    H --> I["🎯 Write weighted objectives<br/>with success measures"]
    I --> J{"⛔ Hazard assessment<br/>APPROVED?<br/><i>stop criteria + real-emergency protocol</i>"}
    J -->|no| I
    J -->|yes| K["▶️ Run the exercise<br/><i>injects · event log · comms · muster ·<br/>casualties · deployments</i>"]
    K --> L["👁️ Evaluator observations<br/>rated per objective"]
    L --> M["🗣️ Hot debrief, then formal debrief"]
    M --> N["📋 Classify findings<br/><i>OBSERVATION → NONCONFORMANCE_CRITICAL</i>"]
    N --> O["📄 Exercise report<br/>prepared → reviewed → approved"]
    N --> P
    P --> Q{"✅ Verified by someone<br/>other than the owner?"}
    Q -->|no| P
    Q -->|yes| R{"All major / critical<br/>actions verified?"}
    O --> R
    R -->|no| S["Exercise stays<br/>ACTIONS_OPEN"]
    S --> P
    R -->|yes| T["🔁 Plan review<br/><i>trigger = EXERCISE</i>"]
    T --> U["📕 New plan revision<br/><i>old revision → SUPERSEDED</i>"]
    U --> B
    T --> V["🔒 Exercise CLOSED"]

Application Flow — Running an Exercise

The conduct phase in detail, including the two things that make an exercise safe: the real-emergency override and the simulation markers.

flowchart TD
    S(["Exercise SCHEDULED"]) --> B["Safety brief<br/><i>or no-notice controls</i>"]
    B --> ST["▶️ IN_PROGRESS<br/>start authorised"]
    ST --> IN{"Controller releases<br/>the next inject"}
    IN --> AK["Inject acknowledged<br/>→ ACTED_ON / SKIPPED"]
    AK --> EV["📝 Event log entry<br/><i>real_event = false</i>"]
    EV --> RESP{"What did it provoke?"}
    RESP --> C1["📞 Communication<br/><i>simulated = true, prefixed EXERCISE</i>"]
    RESP --> C2["👥 Muster + MusterPerson<br/><i>accounted · missing · offsite · exempt</i>"]
    RESP --> C3["🩹 Simulated casualty<br/><i>LOCATED → TRIAGED → TREATED →<br/>EXTRACTED → TRANSFERRED</i>"]
    RESP --> C4["🚚 Resource deployment<br/><i>requested → mobilised → arrived →<br/>deployed → stood down</i>"]
    C1 --> OBS["👁️ Evaluator observation<br/>rated against an objective"]
    C2 --> OBS
    C3 --> OBS
    C4 --> OBS
    OBS --> MORE{"More injects?"}
    MORE -->|yes| IN
    MORE -->|no| END["⏹️ Stand down<br/>participants + resources accounted"]
    END --> DONE(["COMPLETED → DEBRIEFING"])

    ST -.->|"any stop criterion met"| STOP["⛔ PAUSED / ABORTED"]
    ST -.->|"REAL EMERGENCY ×3"| REAL["🚨 Exercise abandoned<br/>site emergency plan takes over<br/><i>logged with real_event = true</i>"]
    STOP -.-> ST

Reading the Diagrams

  • A ||--o{ B means 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 role columns that appear on nearly every model (*_person_id, mine_site_id, *_document_id) are omitted for readability. The interactive viewer's Full ERD shows all 125, with the person/site/document noise toggleable.
  • EmergencyExercise is the hub of the domain — the viewer's Focus mode defaults to it, with 21 links. Person has the most (41) because every assurance act records who did it: owned, accountable, approved, prepared, reviewed, verified, inspected, assessed, observed, facilitated, directed, controlled, evaluated, completed.
  • No FK was dropped in translation — the proforma graph was acyclic, so every relationship in the source model is present. EmergencyControlLocation was hoisted above CommandStructure (the pack declares it after first use). OperatingArea.parent_area_id, EmergencyPlan.supersedes_plan_id and SourceDocument.superseded_by_id are self-references.
  • One pointer is deliberately not a foreign key: ReadinessAssessmentItem.item_reference_key is a polymorphic reference — an item can address a plan, a procedure, a resource, a control, a contact or a competency — so it is a string, not an edge.
  • Competency coverage is likewise computed, not navigated: EmergencyRoleCompetency and CommandRoleAssignment both carry the command_role enum rather than joining, so "does this role have enough current holders?" is a query across two enum matches.

Diagrams are derived from the generated schema metadata (apps/emergprep/generated/json/emergprep_relationship_metadata.json). If the DSL schema changes, regenerate the metadata (python -m codegen.cli all emergprep) and update this page and the viewer's embedded data.