from flask import Blueprint, render_template from flask_login import login_required from app_new.core.models.college import Site, Building, Zone, Room, RoomProfile, RoomSchedule from app_new.core.models.equipment import Equipment from app_new.core.models.maintenance import LotTask from app_new.core.models.user import User from app_new.core.models.planning import WorkSchedule from app_new.contracts.models import Contract configuration_bp = Blueprint("configuration", __name__, template_folder="templates") def _status(ok, label, url, detail=None, optional=False, attention=False): if optional and not ok: state = "Facultatif" elif attention: state = "Attention" elif ok: state = "Configuré" else: state = "À configurer" return {"label": label, "state": state, "ok": ok, "url": url, "detail": detail or ""} @configuration_bp.route("/") @login_required def index(): from flask import url_for sites = Site.query.count() buildings = Building.query.count() zones = Zone.query.count() rooms = Room.query.count() profiles = RoomProfile.query.count() unprofiled = Room.query.filter(Room.room_type_id.is_(None)).count() equipment_without_lot = Equipment.query.filter(Equipment.is_deleted.is_(False), Equipment.lot_id.is_(None)).count() technicians = User.query.filter_by(is_active=True).all() scheduled_users = {row[0] for row in WorkSchedule.query.filter_by(is_active=True).with_entities(WorkSchedule.user_id).all()} tasks_without_duration = LotTask.query.filter(LotTask.is_active.is_(True), (LotTask.duree_minutes.is_(None) | (LotTask.duree_minutes <= 0))).count() contracts_without_due = Contract.query.filter(Contract.is_active.is_(True), Contract.end_date.is_(None)).count() if hasattr(Contract, "end_date") else 0 room_schedules = RoomSchedule.query.count() sections = [ ("ESSENTIEL", [ _status(User.query.filter_by(is_active=True).count() > 0, "Administrateur / utilisateurs", url_for("admin.users")), _status(sites > 0, "Établissement / site", url_for("buildings.index"), f"{sites} site(s)"), _status(buildings > 0, "Bâtiments", url_for("buildings.index"), f"{buildings} bâtiment(s)"), _status(zones > 0, "Zones", url_for("zones.index"), f"{zones} zone(s)"), _status(rooms > 0, "Locaux / emplacements", url_for("rooms.index"), f"{rooms} local(aux)"), _status(len(scheduled_users) >= len(technicians) and bool(technicians), "Horaires techniciens", url_for("planning.work_schedules"), f"{len(technicians) - len(scheduled_users)} sans horaires" if technicians else "Aucun technicien actif", attention=bool(technicians) and len(scheduled_users) < len(technicians)), ]), ("MAINTENANCE", [ _status(profiles > 0, "Profils de locaux", url_for("room_profiles.index"), f"{profiles} profil(s)"), _status(equipment_without_lot == 0, "Équipements avec lot", url_for("equipments.index"), f"{equipment_without_lot} sans lot", attention=equipment_without_lot > 0), _status(tasks_without_duration == 0, "Durées préventives", url_for("lots.index"), f"{tasks_without_duration} à renseigner", attention=tasks_without_duration > 0), _status(room_schedules > 0, "Planning des salles", url_for("planning.room_schedules"), f"{room_schedules} créneau(x)"), ]), ("PATRIMOINE", [ _status(unprofiled == 0, "Locaux avec profil structurel", url_for("rooms.index"), f"{unprofiled} sans type", attention=unprofiled > 0), _status(True, "Logements de fonction", url_for("housing.index"), "Facultatif", optional=True), ]), ("ENTREPRISES", [_status(contracts_without_due == 0, "Contrats avec échéance", url_for("companies.index"), f"{contracts_without_due} sans échéance", attention=contracts_without_due > 0)]), ("COMPTEURS", [_status(False, "Compteurs", url_for("meters.index"), "Non configuré", optional=True)]), ("CARTOGRAPHIE TECHNIQUE", [_status(False, "Relations techniques", url_for("equipments.index"), "Facultatif", optional=True)]), ("INTÉGRATIONS", [ _status(False, "Pronote", url_for("pronote.index"), "Non configuré", optional=True), _status(False, "ENT / Outlook / Yeastar / IA", url_for("admin.settings"), "Facultatif", optional=True), ]), ] return render_template("configuration/index.html", sections=sections)