v_2.3
This commit is contained in:
@@ -0,0 +1,137 @@
|
||||
"""Frontoffice routes — visible to all authenticated users.
|
||||
|
||||
Sections:
|
||||
1. View Data (everyone)
|
||||
2. Add/Remove/Edit (admins only)
|
||||
"""
|
||||
import json
|
||||
from datetime import date
|
||||
from flask import Blueprint, render_template, redirect, url_for, flash, request, abort
|
||||
from flask_login import current_user
|
||||
|
||||
from .models import db, Alert, AppSetting, AuditLog
|
||||
from .forms import AlertForm
|
||||
from .auth_utils import admin_required, login_required_full, get_remote_ip
|
||||
|
||||
main_bp = Blueprint("main", __name__)
|
||||
|
||||
|
||||
def _parse_days(text: str) -> list[int]:
|
||||
return sorted({int(x.strip()) for x in text.split(",") if x.strip().isdigit() and int(x.strip()) >= 0}, reverse=True)
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Section 1 — View Data
|
||||
# --------------------------------------------------------------------------- #
|
||||
@main_bp.route("/")
|
||||
@login_required_full
|
||||
def index():
|
||||
today = date.today()
|
||||
alerts = Alert.query.filter_by(is_active=True).order_by(Alert.expiration_date.asc()).all()
|
||||
payload = []
|
||||
for a in alerts:
|
||||
days_left = (a.expiration_date - today).days
|
||||
payload.append({
|
||||
"id": a.id,
|
||||
"title": a.title,
|
||||
"category": a.category,
|
||||
"description": a.description,
|
||||
"expiration_date": a.expiration_date.isoformat(),
|
||||
"days_left": days_left,
|
||||
"reminder_days": a.reminder_days,
|
||||
})
|
||||
return render_template("index.html", alerts=payload, today_iso=today.isoformat())
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Section 2 — Add / Remove / Edit
|
||||
# --------------------------------------------------------------------------- #
|
||||
@main_bp.route("/alerts")
|
||||
@login_required_full
|
||||
def alerts_list():
|
||||
alerts = Alert.query.order_by(Alert.expiration_date.asc()).all()
|
||||
return render_template("alerts_list.html", alerts=alerts)
|
||||
|
||||
|
||||
@main_bp.route("/alerts/new", methods=["GET", "POST"])
|
||||
@admin_required
|
||||
def alert_new():
|
||||
default_reminder_days = AppSetting.get("default_reminder_days", "30,14,7,1")
|
||||
form = AlertForm(reminder_days=default_reminder_days)
|
||||
if form.validate_on_submit():
|
||||
days = _parse_days(form.reminder_days.data)
|
||||
if not days:
|
||||
flash("Reminder days must contain at least one valid number.", "error")
|
||||
else:
|
||||
alert = Alert(
|
||||
title=form.title.data.strip(),
|
||||
category=(form.category.data or "").strip() or None,
|
||||
description=(form.description.data or "").strip() or None,
|
||||
expiration_date=form.expiration_date.data,
|
||||
is_active=form.is_active.data,
|
||||
created_by=current_user.id,
|
||||
)
|
||||
alert.reminder_days = days
|
||||
db.session.add(alert)
|
||||
db.session.commit()
|
||||
AuditLog.record("alert.create", user=current_user, details=f"id={alert.id} title={alert.title}", ip=get_remote_ip())
|
||||
flash("Alert created.", "success")
|
||||
return redirect(url_for("main.alerts_list"))
|
||||
return render_template("alert_form.html", form=form, alert=None)
|
||||
|
||||
|
||||
@main_bp.route("/alerts/<int:alert_id>/edit", methods=["GET", "POST"])
|
||||
@admin_required
|
||||
def alert_edit(alert_id: int):
|
||||
alert = db.session.get(Alert, alert_id)
|
||||
if alert is None:
|
||||
abort(404)
|
||||
form = AlertForm(obj=alert)
|
||||
if request.method == "GET":
|
||||
form.reminder_days.data = ",".join(str(d) for d in alert.reminder_days)
|
||||
if form.validate_on_submit():
|
||||
days = _parse_days(form.reminder_days.data)
|
||||
if not days:
|
||||
flash("Reminder days must contain at least one valid number.", "error")
|
||||
else:
|
||||
alert.title = form.title.data.strip()
|
||||
alert.category = (form.category.data or "").strip() or None
|
||||
alert.description = (form.description.data or "").strip() or None
|
||||
alert.expiration_date = form.expiration_date.data
|
||||
alert.is_active = form.is_active.data
|
||||
# If reminder_days changed OR expiration_date changed, reset sent log
|
||||
if days != alert.reminder_days:
|
||||
alert.reminders_sent = []
|
||||
alert.reminder_days = days
|
||||
db.session.commit()
|
||||
AuditLog.record("alert.update", user=current_user, details=f"id={alert.id}", ip=get_remote_ip())
|
||||
flash("Alert updated.", "success")
|
||||
return redirect(url_for("main.alerts_list"))
|
||||
return render_template("alert_form.html", form=form, alert=alert)
|
||||
|
||||
|
||||
@main_bp.route("/alerts/<int:alert_id>/delete", methods=["POST"])
|
||||
@admin_required
|
||||
def alert_delete(alert_id: int):
|
||||
alert = db.session.get(Alert, alert_id)
|
||||
if alert is None:
|
||||
abort(404)
|
||||
title = alert.title
|
||||
db.session.delete(alert)
|
||||
db.session.commit()
|
||||
AuditLog.record("alert.delete", user=current_user, details=f"id={alert_id} title={title}", ip=get_remote_ip())
|
||||
flash(f"Alert '{title}' deleted.", "success")
|
||||
return redirect(url_for("main.alerts_list"))
|
||||
|
||||
|
||||
@main_bp.route("/alerts/<int:alert_id>/reset-reminders", methods=["POST"])
|
||||
@admin_required
|
||||
def alert_reset_reminders(alert_id: int):
|
||||
alert = db.session.get(Alert, alert_id)
|
||||
if alert is None:
|
||||
abort(404)
|
||||
alert.reminders_sent = []
|
||||
db.session.commit()
|
||||
AuditLog.record("alert.reset_reminders", user=current_user, details=f"id={alert_id}", ip=get_remote_ip())
|
||||
flash("Reminder send-history cleared. Pending reminders will be re-sent on next scheduler run.", "info")
|
||||
return redirect(url_for("main.alerts_list"))
|
||||
Reference in New Issue
Block a user