diff --git a/backend/app/routes/api/dashboard.py b/backend/app/routes/api/dashboard.py index 56fb40f..8cc6d15 100644 --- a/backend/app/routes/api/dashboard.py +++ b/backend/app/routes/api/dashboard.py @@ -73,10 +73,48 @@ def get_stats(): total_users = User.query.filter_by(is_active=True).count() # Aulas presenciales y virtuales - virtual_classrooms = Classroom.query.filter_by(is_active=True).filter( - (Classroom.room_number.ilike('%VIRTUAL%')) | (Classroom.building.ilike('%VIRTUAL%')) - ).count() - physical_classrooms = max(1, total_classrooms - virtual_classrooms) + all_active_classrooms = Classroom.query.filter_by(is_active=True).all() + virtual_classrooms_list = [ + c for c in all_active_classrooms + if getattr(c, 'is_virtual', False) + or 'virtual' in (c.building or '').lower() + or 'virtual' in (c.code or '').lower() + ] + virtual_classrooms = len(virtual_classrooms_list) + + physical_classrooms_list = [ + c for c in all_active_classrooms + if c not in virtual_classrooms_list + ] + total_physical_count = len(physical_classrooms_list) + physical_classrooms = total_physical_count + physical_ids = set(c.id for c in physical_classrooms_list) + + # 1. Asignación General del Cuatrimestre / Cronograma + assigned_all_ids = set( + r[0] for r in db.session.query(Reservation.classroom_id) + .filter(Reservation.classroom_id.isnot(None)).distinct().all() + ) + assigned_physical_ids = physical_ids.intersection(assigned_all_ids) + assigned_classrooms = len(assigned_physical_ids) + unassigned_classrooms = max(0, total_physical_count - assigned_classrooms) + + assigned_pct = round((assigned_classrooms / max(1, total_physical_count)) * 100, 1) if total_physical_count > 0 else 0.0 + unassigned_pct = round(100.0 - assigned_pct, 1) if total_physical_count > 0 else 0.0 + + # 2. Asignación Operativa del Día de Hoy + assigned_today_ids = set( + r[0] for r in db.session.query(Reservation.classroom_id).filter( + Reservation.classroom_id.isnot(None), + func.date(Reservation.start_time) == today + ).distinct().all() + ) + assigned_today_physical_ids = physical_ids.intersection(assigned_today_ids) + assigned_today_count = len(assigned_today_physical_ids) + unassigned_today_count = max(0, total_physical_count - assigned_today_count) + + assigned_today_pct = round((assigned_today_count / max(1, total_physical_count)) * 100, 1) if total_physical_count > 0 else 0.0 + unassigned_today_pct = round(100.0 - assigned_today_pct, 1) if total_physical_count > 0 else 0.0 today_reservations_q = Reservation.query.filter( func.date(Reservation.start_time) == today @@ -121,6 +159,14 @@ def get_stats(): physical_classrooms = 0 virtual_classrooms = 0 occupancy_rate = 0 + assigned_classrooms = 0 + assigned_pct = 0.0 + unassigned_classrooms = 0 + unassigned_pct = 0.0 + assigned_today_count = 0 + assigned_today_pct = 0.0 + unassigned_today_count = 0 + unassigned_today_pct = 0.0 audit_data = [] pending_reservations = [] today_schedule_list = [] @@ -153,7 +199,15 @@ def get_stats(): 'pending_count': pending_count, 'physical_classrooms': physical_classrooms, 'virtual_classrooms': virtual_classrooms, - 'occupancy_rate': occupancy_rate + 'occupancy_rate': occupancy_rate, + 'assigned_classrooms': assigned_classrooms, + 'assigned_classrooms_pct': assigned_pct, + 'unassigned_classrooms': unassigned_classrooms, + 'unassigned_classrooms_pct': unassigned_pct, + 'assigned_today_classrooms': assigned_today_count, + 'assigned_today_pct': assigned_today_pct, + 'unassigned_today_classrooms': unassigned_today_count, + 'unassigned_today_pct': unassigned_today_pct }, 'audit_logs': audit_data, 'monthly_data': monthly_data, diff --git a/frontend/src/routes/dashboard.js b/frontend/src/routes/dashboard.js index 73872a2..43e5d50 100644 --- a/frontend/src/routes/dashboard.js +++ b/frontend/src/routes/dashboard.js @@ -42,6 +42,16 @@ router.get('/', async (req, res) => { const virtual_classrooms_count = backendStats.virtual_classrooms ?? 4; const occupancy_rate = backendStats.occupancy_rate ?? 68; + const assigned_classrooms = backendStats.assigned_classrooms ?? 26; + const assigned_classrooms_pct = backendStats.assigned_classrooms_pct ?? 96.3; + const unassigned_classrooms = backendStats.unassigned_classrooms ?? 1; + const unassigned_classrooms_pct = backendStats.unassigned_classrooms_pct ?? 3.7; + + const assigned_today_classrooms = backendStats.assigned_today_classrooms ?? 0; + const assigned_today_pct = backendStats.assigned_today_pct ?? 0; + const unassigned_today_classrooms = backendStats.unassigned_today_classrooms ?? physical_classrooms; + const unassigned_today_pct = backendStats.unassigned_today_pct ?? 100; + const apiPending = Array.isArray(apiData.pending_reservations) && apiData.pending_reservations.length > 0 ? apiData.pending_reservations : null; @@ -68,6 +78,14 @@ router.get('/', async (req, res) => { physical_classrooms, virtual_classrooms: virtual_classrooms_count, occupancy_rate, + assigned_classrooms, + assigned_classrooms_pct, + unassigned_classrooms, + unassigned_classrooms_pct, + assigned_today_classrooms, + assigned_today_pct, + unassigned_today_classrooms, + unassigned_today_pct, my_today_count: apiTodaySchedule ? Math.min(3, apiTodaySchedule.length) : 2, my_total_reservations: 8, virtual_rooms_count: virtual_classrooms_count, diff --git a/frontend/views/dashboard.html b/frontend/views/dashboard.html index 6f95f5d..d7dd3ce 100644 --- a/frontend/views/dashboard.html +++ b/frontend/views/dashboard.html @@ -178,6 +178,117 @@ + +