Ajouter les alertes de lots au tableau de bord
Some checks are pending
CI - Tests et Syntax / lint-and-test (push) Waiting to run

This commit is contained in:
root 2026-08-19 23:46:52 +00:00
parent f0c3dafd25
commit a18e0f2713
2 changed files with 100 additions and 1 deletions

View file

@ -12,6 +12,7 @@ from ..models.company import Company
from ..models.college import Building, Room
from ..models.company import Part
from app_new.constants import INTERVENTION_STATUSES, EQUIPMENT_STATUSES
from sqlalchemy import func
dashboard_bp = Blueprint('dashboard', __name__)
@ -23,6 +24,7 @@ def index():
from datetime import date
from ..services.planning_service import PlanningService
from ..models.planning import ScheduledTask, AdminTask
from ..models.maintenance import Lot
# Statistiques
stats = {
@ -38,6 +40,41 @@ def index():
'companies_total': Company.query.count(),
'parts_low_stock': Part.query.filter(Part.quantity <= Part.min_quantity).count(),
}
# Contrôle des lots : un lot présent doit être rattaché à au moins un
# équipement, sauf s'il a été explicitement marqué « non présent ».
equipment_counts = dict(
db.session.query(Equipment.lot_id, func.count(Equipment.id))
.filter(
Equipment.lot_id.isnot(None),
Equipment.is_deleted.is_(False),
db.or_(Equipment.status.is_(None), Equipment.status != 'jete'),
)
.group_by(Equipment.lot_id)
.all()
)
lots = Lot.query.order_by(Lot.name).all()
lots_without_equipment = [
lot for lot in lots
if lot.is_present and equipment_counts.get(lot.id, 0) == 0
]
lots_missing_duration = []
for lot in lots:
if not lot.is_present or equipment_counts.get(lot.id, 0) == 0:
continue
active_tasks = [task for task in lot.tasks if task.is_active]
missing_tasks = [
task for task in lot.tasks
if task.is_active and not task.duree_minutes
]
if missing_tasks or not active_tasks:
lots_missing_duration.append({
'lot': lot,
'tasks': missing_tasks,
'no_task': not active_tasks,
})
stats['lots_without_equipment'] = len(lots_without_equipment)
stats['lots_missing_duration'] = len(lots_missing_duration)
# Dernières interventions
recent_interventions = Intervention.query.order_by(Intervention.created_at.desc()).limit(10).all()
@ -108,6 +145,8 @@ def index():
urgent_interventions=urgent_interventions,
pending_interpretations=pending_interpretations,
day_schedule=day_schedule,
lots_without_equipment=lots_without_equipment,
lots_missing_duration=lots_missing_duration,
today=today,
statuses=INTERVENTION_STATUSES)

View file

@ -119,6 +119,66 @@
</div>
</div>
<!-- Contrôles des référentiels de maintenance -->
<div class="row g-2 g-md-3 mb-3">
<div class="col-lg-6">
<div class="card h-100 {% if stats.lots_without_equipment %}border-warning{% else %}border-success{% endif %}">
<div class="card-header {% if stats.lots_without_equipment %}bg-warning text-dark{% else %}bg-success text-white{% endif %} py-2">
<i class="bi bi-box-seam me-1"></i>Lots présents sans équipement
<span class="badge bg-light text-dark ms-1">{{ stats.lots_without_equipment }}</span>
</div>
<div class="card-body py-2">
{% if lots_without_equipment %}
<div class="list-group list-group-flush">
{% for lot in lots_without_equipment[:8] %}
<a class="list-group-item list-group-item-action px-0 py-1 d-flex justify-content-between" href="{{ url_for('lots.detail', id=lot.id) }}">
<span>{{ lot.name }}</span><i class="bi bi-chevron-right"></i>
</a>
{% endfor %}
</div>
{% if lots_without_equipment|length > 8 %}<small class="text-muted">{{ lots_without_equipment|length - 8 }} autre(s)</small>{% endif %}
{% else %}
<span class="text-success"><i class="bi bi-check-circle me-1"></i>Tous les lots présents ont un équipement.</span>
{% endif %}
</div>
</div>
</div>
<div class="col-lg-6">
<div class="card h-100 {% if stats.lots_missing_duration %}border-danger{% else %}border-success{% endif %}">
<div class="card-header {% if stats.lots_missing_duration %}bg-danger text-white{% else %}bg-success text-white{% endif %} py-2">
<i class="bi bi-stopwatch me-1"></i>Lots équipés avec durée manquante
<span class="badge bg-light text-dark ms-1">{{ stats.lots_missing_duration }}</span>
</div>
<div class="card-body py-2">
{% if lots_missing_duration %}
<div class="list-group list-group-flush">
{% for item in lots_missing_duration[:8] %}
<div class="list-group-item px-0 py-1">
<div class="d-flex justify-content-between align-items-center">
<a href="{{ url_for('lots.detail', id=item.lot.id) }}"><strong>{{ item.lot.name }}</strong></a>
{% if item.no_task %}<span class="badge bg-danger">Aucune tâche</span>{% else %}<span class="badge bg-danger">{{ item.tasks|length }} tâche(s)</span>{% endif %}
</div>
{% if item.no_task %}
<a class="small text-decoration-none d-block ms-2" href="{{ url_for('lots.detail', id=item.lot.id) }}">
<i class="bi bi-plus-circle me-1"></i>Ajouter une tâche et définir sa durée
</a>
{% endif %}
{% for task in item.tasks[:3] %}
<a class="small text-decoration-none d-block ms-2" href="{{ url_for('lots.task_edit', lot_id=item.lot.id, task_id=task.id) }}">
<i class="bi bi-pencil me-1"></i>{{ task.tache or 'Tâche sans nom' }} — définir la durée
</a>
{% endfor %}
</div>
{% endfor %}
</div>
{% else %}
<span class="text-success"><i class="bi bi-check-circle me-1"></i>Toutes les tâches actives ont une durée définie.</span>
{% endif %}
</div>
</div>
</div>
</div>
<!-- Alertes -->
{% if alerts %}
<div class="card border-warning mb-3">
@ -652,4 +712,4 @@ fetch('/scheduler/api/overdue-count').then(r => r.json()).then(d => {
}
}).catch(() => {});
</script>
{% endblock %}
{% endblock %}