from datetime import date, datetime, timedelta from flask import Blueprint, flash, make_response, redirect, render_template, request, url_for from flask_login import current_user, login_required from sqlalchemy import func from app_new.extensions import db from app_new.core.models.user import Staff from app_new.core.models.prevention import PreventionWorkLog, StaffAuthorization, RiskAssessment, PreventionAction, SafetyRegisterEntry from app_new.core.services.planning_service import PlanningService prevention_bp = Blueprint("prevention", __name__) def _date(value): return datetime.strptime(value, "%Y-%m-%d").date() if value else None @prevention_bp.route("/") @login_required def index(): monday = date.today() - timedelta(days=date.today().weekday()) minutes = db.session.query(func.coalesce(func.sum(PreventionWorkLog.duration_minutes), 0)).filter(PreventionWorkLog.user_id == current_user.id, PreventionWorkLog.work_date >= monday).scalar() available_minutes = 0 for offset in range(7): hours = PlanningService.get_working_hours(monday + timedelta(days=offset), current_user.id) if hours: available_minutes += int((datetime.combine(date.min, hours[1]) - datetime.combine(date.min, hours[0])).total_seconds() / 60) if hours[2] and hours[3]: available_minutes -= int((datetime.combine(date.min, hours[3]) - datetime.combine(date.min, hours[2])).total_seconds() / 60) target = round(available_minutes * 0.10) logs = PreventionWorkLog.query.filter_by(user_id=current_user.id).order_by(PreventionWorkLog.work_date.desc()).limit(20).all() return render_template("prevention/index.html", staff=Staff.query.filter_by(is_active=True).order_by(Staff.last_name).all(), logs=logs, risks=RiskAssessment.query.filter_by(is_active=True).all(), actions=PreventionAction.query.order_by(PreventionAction.due_date).all(), entries=SafetyRegisterEntry.query.order_by(SafetyRegisterEntry.entry_date.desc()).limit(20).all(), minutes=minutes, target=target, available_minutes=available_minutes) @prevention_bp.post("/staff") @login_required def add_staff(): db.session.add(Staff(first_name=request.form.get("first_name", "").strip(), last_name=request.form.get("last_name", "").strip(), function=request.form.get("function"))) db.session.commit(); flash("Agent ajouté.", "success"); return redirect(url_for("prevention.index")) @prevention_bp.post("/time") @login_required def add_time(): db.session.add(PreventionWorkLog(user_id=current_user.id, work_date=_date(request.form.get("work_date")) or date.today(), duration_minutes=max(1, request.form.get("duration_minutes", type=int) or 1), activity=request.form.get("activity", "").strip(), notes=request.form.get("notes"))) db.session.commit(); flash("Temps de prévention enregistré.", "success"); return redirect(url_for("prevention.index")) @prevention_bp.post("/authorization") @login_required def add_authorization(): db.session.add(StaffAuthorization(staff_id=request.form.get("staff_id", type=int), name=request.form.get("name", "").strip(), issued_on=_date(request.form.get("issued_on")), expires_on=_date(request.form.get("expires_on")), issuer=request.form.get("issuer"))) db.session.commit(); flash("Habilitation enregistrée.", "success"); return redirect(url_for("prevention.index")) @prevention_bp.post("/risk") @login_required def add_risk(): severity = request.form.get("severity", type=int) or 1 probability = request.form.get("probability", type=int) or 1 work_unit = request.form.get("work_unit", "").strip() hazard = request.form.get("hazard", "").strip() if not work_unit or not hazard or severity not in range(1, 5) or probability not in range(1, 5): flash("Unité, danger et cotations de 1 à 4 sont obligatoires.", "danger") return redirect(url_for("prevention.index")) db.session.add(RiskAssessment(work_unit=work_unit, hazard=hazard, exposed_people=request.form.get("exposed_people"), severity=severity, probability=probability, control_measures=request.form.get("control_measures"), reviewed_on=date.today())) db.session.commit(); flash("Risque ajouté au DUERP.", "success"); return redirect(url_for("prevention.index")) @prevention_bp.post("/action") @login_required def add_action(): db.session.add(PreventionAction(title=request.form.get("title", "").strip(), source_type=request.form.get("source_type", "observation"), risk_id=request.form.get("risk_id", type=int), priority=request.form.get("priority", "normale"), owner=request.form.get("owner"), due_date=_date(request.form.get("due_date")))) db.session.commit(); flash("Action créée.", "success"); return redirect(url_for("prevention.index")) @prevention_bp.post("/action//status") @login_required def update_action_status(id): action = PreventionAction.query.get_or_404(id) allowed = {"a_faire", "validee", "planifiee", "en_cours", "verifiee", "cloturee", "refusee"} status = request.form.get("status") if status not in allowed: flash("Statut de prévention invalide.", "danger") else: action.status = status db.session.commit() flash("Action de prévention mise à jour.", "success") return redirect(url_for("prevention.index")) @prevention_bp.post("/register") @login_required def add_register(): db.session.add(SafetyRegisterEntry(entry_date=_date(request.form.get("entry_date")) or date.today(), entry_type=request.form.get("entry_type", "observation"), title=request.form.get("title", "").strip(), details=request.form.get("details"), location=request.form.get("location"), created_by_id=current_user.id)) db.session.commit(); flash("Entrée ajoutée au registre.", "success"); return redirect(url_for("prevention.index")) @prevention_bp.get('/duerp.csv') @login_required def export_duerp(): """Exporte le registre DUERP exploitable dans un tableur.""" import csv import io output = io.StringIO() writer = csv.writer(output, delimiter=';') writer.writerow(['Unité de travail', 'Danger', 'Personnes exposées', 'Gravité', 'Probabilité', 'Criticité', 'Mesures existantes', 'Dernière revue']) for risk in RiskAssessment.query.order_by(RiskAssessment.work_unit, RiskAssessment.hazard).all(): writer.writerow([risk.work_unit, risk.hazard, risk.exposed_people or '', risk.severity, risk.probability, (risk.severity or 0) * (risk.probability or 0), risk.control_measures or '', risk.reviewed_on.isoformat() if risk.reviewed_on else '']) response = make_response('\ufeff' + output.getvalue()) response.headers['Content-Type'] = 'text/csv; charset=utf-8' response.headers['Content-Disposition'] = 'attachment; filename=duerp.csv' return response