demo additionnal content for the future
This commit is contained in:
204
algos-demo/chu-stats-analyzer/main.go
Normal file
204
algos-demo/chu-stats-analyzer/main.go
Normal file
@@ -0,0 +1,204 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"math"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const dataFile = "/data/chu_stats.json"
|
||||
|
||||
type CHUStats struct {
|
||||
GeneratedAt time.Time `json:"generated_at"`
|
||||
HospitalName string `json:"hospital_name"`
|
||||
Date string `json:"date"`
|
||||
BedsTotal int `json:"beds_total"`
|
||||
BedsOccupied int `json:"beds_occupied"`
|
||||
OccupancyRate float64 `json:"occupancy_rate"`
|
||||
PatientsAdmitted int `json:"patients_admitted"`
|
||||
PatientsDischarge int `json:"patients_discharged"`
|
||||
EmergencyVisits int `json:"emergency_visits"`
|
||||
AvgERWaitMinutes int `json:"avg_er_wait_minutes"`
|
||||
SurgeriesPerformed int `json:"surgeries_performed"`
|
||||
ICUBeds int `json:"icu_beds"`
|
||||
ICUOccupied int `json:"icu_occupied"`
|
||||
MortalityRate float64 `json:"mortality_rate"`
|
||||
InfectionRate float64 `json:"infection_rate"`
|
||||
StaffPresent int `json:"staff_present"`
|
||||
StaffRequired int `json:"staff_required"`
|
||||
PatientSatisfaction float64 `json:"patient_satisfaction"`
|
||||
}
|
||||
|
||||
type Check struct {
|
||||
Indicator string `json:"indicator"`
|
||||
Value float64 `json:"value"`
|
||||
Unit string `json:"unit"`
|
||||
Threshold string `json:"threshold"`
|
||||
Conformant bool `json:"conformant"`
|
||||
Severity string `json:"severity"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
type Analysis struct {
|
||||
AnalyzedAt time.Time `json:"analyzed_at"`
|
||||
HospitalName string `json:"hospital_name"`
|
||||
StatsDate string `json:"stats_date"`
|
||||
Conformant bool `json:"conformant"`
|
||||
Score int `json:"score"`
|
||||
PassingChecks int `json:"passing_checks"`
|
||||
TotalChecks int `json:"total_checks"`
|
||||
CriticalAlerts int `json:"critical_alerts"`
|
||||
WarningAlerts int `json:"warning_alerts"`
|
||||
Checks []Check `json:"checks"`
|
||||
Summary string `json:"summary"`
|
||||
}
|
||||
|
||||
func round2(v float64) float64 {
|
||||
return math.Round(v*100) / 100
|
||||
}
|
||||
|
||||
func analyze(s CHUStats) Analysis {
|
||||
var checks []Check
|
||||
|
||||
occ := Check{Indicator: "Taux d'occupation des lits", Value: s.OccupancyRate, Unit: "%", Threshold: "< 85%"}
|
||||
switch {
|
||||
case s.OccupancyRate < 85:
|
||||
occ.Conformant, occ.Severity, occ.Message = true, "ok", "Taux d'occupation normal"
|
||||
case s.OccupancyRate < 95:
|
||||
occ.Conformant, occ.Severity, occ.Message = false, "warning", "Taux d'occupation élevé, risque de saturation"
|
||||
default:
|
||||
occ.Conformant, occ.Severity, occ.Message = false, "critical", "Saturation critique des lits hospitaliers"
|
||||
}
|
||||
checks = append(checks, occ)
|
||||
|
||||
er := Check{Indicator: "Temps d'attente aux urgences", Value: float64(s.AvgERWaitMinutes), Unit: "min", Threshold: "< 60 min"}
|
||||
switch {
|
||||
case s.AvgERWaitMinutes < 60:
|
||||
er.Conformant, er.Severity, er.Message = true, "ok", "Temps d'attente aux urgences acceptable"
|
||||
case s.AvgERWaitMinutes < 120:
|
||||
er.Conformant, er.Severity, er.Message = false, "warning", "Temps d'attente aux urgences trop long"
|
||||
default:
|
||||
er.Conformant, er.Severity, er.Message = false, "critical", "Saturation critique des urgences"
|
||||
}
|
||||
checks = append(checks, er)
|
||||
|
||||
icuRate := round2(float64(s.ICUOccupied) / float64(s.ICUBeds) * 100)
|
||||
icu := Check{Indicator: "Taux d'occupation ICU", Value: icuRate, Unit: "%", Threshold: "< 85%"}
|
||||
switch {
|
||||
case icuRate < 85:
|
||||
icu.Conformant, icu.Severity, icu.Message = true, "ok", "Capacité de réanimation suffisante"
|
||||
case icuRate < 95:
|
||||
icu.Conformant, icu.Severity, icu.Message = false, "warning", "Taux d'occupation ICU élevé"
|
||||
default:
|
||||
icu.Conformant, icu.Severity, icu.Message = false, "critical", "Réanimation en situation critique"
|
||||
}
|
||||
checks = append(checks, icu)
|
||||
|
||||
mort := Check{Indicator: "Taux de mortalité hospitalière", Value: s.MortalityRate, Unit: "%", Threshold: "< 2%"}
|
||||
switch {
|
||||
case s.MortalityRate < 2.0:
|
||||
mort.Conformant, mort.Severity, mort.Message = true, "ok", "Taux de mortalité dans les normes"
|
||||
case s.MortalityRate < 4.0:
|
||||
mort.Conformant, mort.Severity, mort.Message = false, "warning", "Taux de mortalité supérieur au seuil recommandé"
|
||||
default:
|
||||
mort.Conformant, mort.Severity, mort.Message = false, "critical", "Taux de mortalité critique — investigation requise"
|
||||
}
|
||||
checks = append(checks, mort)
|
||||
|
||||
infect := Check{Indicator: "Taux d'infection nosocomiale", Value: s.InfectionRate, Unit: "%", Threshold: "< 5%"}
|
||||
switch {
|
||||
case s.InfectionRate < 5.0:
|
||||
infect.Conformant, infect.Severity, infect.Message = true, "ok", "Taux d'infection nosocomiale acceptable"
|
||||
case s.InfectionRate < 8.0:
|
||||
infect.Conformant, infect.Severity, infect.Message = false, "warning", "Taux d'infection nosocomiale préoccupant"
|
||||
default:
|
||||
infect.Conformant, infect.Severity, infect.Message = false, "critical", "Risque infectieux critique — mesures d'urgence requises"
|
||||
}
|
||||
checks = append(checks, infect)
|
||||
|
||||
staffRate := round2(float64(s.StaffPresent) / float64(s.StaffRequired) * 100)
|
||||
staff := Check{Indicator: "Taux de couverture du personnel", Value: staffRate, Unit: "%", Threshold: ">= 80%"}
|
||||
switch {
|
||||
case staffRate >= 80:
|
||||
staff.Conformant, staff.Severity, staff.Message = true, "ok", "Personnel suffisant"
|
||||
case staffRate >= 70:
|
||||
staff.Conformant, staff.Severity, staff.Message = false, "warning", "Manque de personnel, vigilance requise"
|
||||
default:
|
||||
staff.Conformant, staff.Severity, staff.Message = false, "critical", "Sous-effectif critique"
|
||||
}
|
||||
checks = append(checks, staff)
|
||||
|
||||
sat := Check{Indicator: "Satisfaction des patients", Value: s.PatientSatisfaction, Unit: "/ 5", Threshold: ">= 3.5 / 5"}
|
||||
switch {
|
||||
case s.PatientSatisfaction >= 3.5:
|
||||
sat.Conformant, sat.Severity, sat.Message = true, "ok", "Satisfaction des patients satisfaisante"
|
||||
case s.PatientSatisfaction >= 3.0:
|
||||
sat.Conformant, sat.Severity, sat.Message = false, "warning", "Satisfaction des patients insuffisante"
|
||||
default:
|
||||
sat.Conformant, sat.Severity, sat.Message = false, "critical", "Satisfaction des patients très mauvaise"
|
||||
}
|
||||
checks = append(checks, sat)
|
||||
|
||||
passing, critical, warning := 0, 0, 0
|
||||
for _, c := range checks {
|
||||
if c.Conformant {
|
||||
passing++
|
||||
}
|
||||
switch c.Severity {
|
||||
case "critical":
|
||||
critical++
|
||||
case "warning":
|
||||
warning++
|
||||
}
|
||||
}
|
||||
total := len(checks)
|
||||
score := passing * 100 / total
|
||||
|
||||
return Analysis{
|
||||
AnalyzedAt: time.Now().UTC(),
|
||||
HospitalName: s.HospitalName,
|
||||
StatsDate: s.Date,
|
||||
Conformant: passing == total,
|
||||
Score: score,
|
||||
PassingChecks: passing,
|
||||
TotalChecks: total,
|
||||
CriticalAlerts: critical,
|
||||
WarningAlerts: warning,
|
||||
Checks: checks,
|
||||
Summary: fmt.Sprintf(
|
||||
"%d/%d indicateurs conformes (score: %d%%) — %d alerte(s) critique(s), %d avertissement(s)",
|
||||
passing, total, score, critical, warning,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
for i := range []int{0, 1} {
|
||||
data, err := os.ReadFile(strings.ReplaceAll(dataFile, "stats", "stats"+fmt.Sprintf("%v", i)))
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
log.Fatalf("stats file not found at %s — run chu-stats-generator first", dataFile)
|
||||
}
|
||||
log.Fatalf("failed to read stats file: %v", err)
|
||||
}
|
||||
|
||||
var stats CHUStats
|
||||
if err := json.Unmarshal(data, &stats); err != nil {
|
||||
log.Fatalf("failed to parse stats file: %v", err)
|
||||
}
|
||||
|
||||
analysis := analyze(stats)
|
||||
|
||||
result, err := json.MarshalIndent(analysis, "", " ")
|
||||
if err != nil {
|
||||
log.Fatalf("failed to marshal analysis: %v", err)
|
||||
}
|
||||
|
||||
log.Printf("Analysis complete: %s", analysis.Summary)
|
||||
fmt.Println(string(result))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user