feat(safety): add drill and incident log with enforced roll-call reconciliation (fixes #299) - #304
Open
MOHITKOURAV01 wants to merge 1 commit into
Open
Conversation
…nciliation An event cannot be closed while anybody is unaccounted for. closeEvent refuses with the names and there is no force flag and no admin bypass; the only way forward is to resolve each person with a note saying what happened to them. An override would be used every time, by whoever is under the most pressure to make the number look right. Roll calls are submitted per class so counts arrive in parallel rather than through one clipboard. unaccountedCount is recomputed server-side from expected minus present minus authorised absences, and the named list has to agree with it - a roll call claiming everyone is present while naming two missing children is refused rather than filed. A recount keeps resolutions already recorded instead of discarding them. alarmRaisedAt is stamped by the server on start, and start is conditional on the event still being planned, so evacuation and reconciliation times are measurements rather than claims. Drills and real incidents share one model so the code path used in an emergency is the one that gets rehearsed. Closes Sitaram8472#299
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Related Issue
Closes #299
Description
A fire drill is currently recorded as one line in a register — "14 March, 4 min 20 s, satisfactory" — and that line omits the only number that matters: how many children were not accounted for, and for how long. This adds drills and real incidents as one module, arranged entirely around a single rule.
An event cannot be closed while anybody is unaccounted for
closeEventrefuses with a 409 and the names, and there is no force flag and no admin bypass. The only way past it is to resolve each outstanding person with a note saying what happened to them — "was at the dentist, confirmed with the office", "found in the music room, escorted out". Those notes are the record. The same check guards the all-clear.I deliberately did not add an override. An override would be used every time, by the person under the most pressure to make the number look right, and a system that can produce a clean drill report while a child is unaccounted for produces exactly the document that turns out to be worthless afterwards. If one thing in this PR gets reviewed, please make it that this rule cannot be talked out of.
The roll call has to be internally consistent
Roll calls are submitted per class by whoever is standing with that class, so counts arrive in parallel instead of through one person with a clipboard.
unaccountedCountis recomputed server-side from expected − present − authorised absences, and the named list has to agree with it: a roll call claiming everybody is present while naming two missing children is rejected rather than stored. Known absences from the register are counted separately, because the child who was off sick this morning is not missing, and counting them as missing is how a real missing child gets lost in the noise.A recount is normal under pressure, so resubmitting replaces the class's earlier roll call — but anybody already resolved keeps their resolution, since a resolution is a fact about what happened to a child and a recount must not quietly discard it.
Reconciliation is a state the event arrives at, not a button somebody presses: it flips when every named person has been accounted for, and flips back if a later roll call adds somebody.
Timings are measurements, not claims
alarmRaisedAtis stamped by the server on start, and start is conditional on the event still being planned so a second tap cannot restart the clock and shrink the evacuation time.evacuationSecondsis derived from the last roll call to arrive;reconciliationSecondsfrom the last resolution — that second number is the one the register line never contains. The statistics report the median evacuation time rather than the mean, so one drill in a thunderstorm does not move the figure everybody is judged against.One model for drills and incidents
Deliberately. The evacuation, the roll call and the reconciliation are identical whether the alarm was planned or not, and two implementations would mean the code path used in a real emergency is the one that has never been exercised. Drills exist to rehearse the response; the software should be rehearsed too. A real incident is created already in progress with the alarm stamped, because nobody is going to schedule one and then press start.
Follow-up actions carry an owner and a due date, and overdue ones are counted in the statistics — a drill that produces "the west stairwell door sticks" and nothing else is a drill that will produce it again next term.
Pages / Components Added or Modified
backend/models/SafetyEvent.js— newbackend/controllers/safetyController.js— newbackend/routes/safetyRoutes.js— newfrontend/src/pages/SafetyDrills.jsx— newbackend/server.js— mounts/api/safety(additions only)frontend/src/App.jsx— adds the/safetyroute (additions only)Screenshots
/safetygives a class teacher one thing during an event: the roll call form for their class, submittable in about four taps, because on a field at 09:12 nothing else is usable. The coordinator gets the live board — classes in, classes still to report, a running evacuation clock, and unaccounted names at the top in red with an Account for button each. The close button is disabled whenever the server says somebody is outstanding, with the reason printed underneath rather than hidden in an error toast; the point is to show what has to happen first, not just to block the click.Images omitted because the board needs a live event to show anything; happy to add them.
Checklist
npm run lint— 18 problems, identical tomain, all pre-existing and outside this PR's filesnpx vite buildsucceedsnode --checkclean on every backend file added or changedmainand each of my other open PRs to confirm no conflict