feat: implement Bedelía dashboard panels and Phase 1 Automated Security Audit
This commit is contained in:
@@ -149,6 +149,92 @@ def get_stats():
|
||||
upcoming_reservations = [serialize_reservation(r) for r in upcoming_reservations_q]
|
||||
milestones = [serialize_milestone(m) for m in milestones_q]
|
||||
|
||||
# ---- Bedelía: Comisiones sin aula asignada (semana y mes) ----
|
||||
import calendar
|
||||
# Calcular rango de semana actual (lunes a domingo)
|
||||
today_dt = datetime.utcnow()
|
||||
week_start = today_dt - timedelta(days=today_dt.weekday()) # lunes
|
||||
week_start = week_start.replace(hour=0, minute=0, second=0, microsecond=0)
|
||||
week_end = week_start + timedelta(days=6, hours=23, minutes=59, seconds=59)
|
||||
|
||||
# Calcular rango del mes actual
|
||||
month_start = today_dt.replace(day=1, hour=0, minute=0, second=0, microsecond=0)
|
||||
last_day = calendar.monthrange(today_dt.year, today_dt.month)[1]
|
||||
month_end = today_dt.replace(day=last_day, hour=23, minute=59, second=59, microsecond=0)
|
||||
|
||||
# Comisiones activas totales
|
||||
all_active_commissions = Commission.query.filter_by(active=True).all()
|
||||
total_active_commissions = len(all_active_commissions)
|
||||
|
||||
# IDs de comisiones que SÍ tienen reserva en la semana
|
||||
comm_ids_with_res_week = set(
|
||||
r[0] for r in db.session.query(Reservation.commission_id).filter(
|
||||
Reservation.commission_id.isnot(None),
|
||||
Reservation.start_time >= week_start,
|
||||
Reservation.start_time <= week_end
|
||||
).distinct().all()
|
||||
)
|
||||
# IDs de comisiones que SÍ tienen reserva en el mes
|
||||
comm_ids_with_res_month = set(
|
||||
r[0] for r in db.session.query(Reservation.commission_id).filter(
|
||||
Reservation.commission_id.isnot(None),
|
||||
Reservation.start_time >= month_start,
|
||||
Reservation.start_time <= month_end
|
||||
).distinct().all()
|
||||
)
|
||||
|
||||
unassigned_commissions_week = []
|
||||
unassigned_commissions_month = []
|
||||
for comm in all_active_commissions:
|
||||
comm_data = {
|
||||
'id': comm.id,
|
||||
'code': comm.code,
|
||||
'full_code': comm.get_full_code(),
|
||||
'subject_name': comm.subject.name if comm.subject else 'Sin materia',
|
||||
'subject_code': comm.subject.code if comm.subject else '',
|
||||
'teacher_name': comm.teacher_name,
|
||||
'schedule': comm.schedule or 'Sin horario',
|
||||
'shift': comm.shift or 'Sin turno',
|
||||
'max_students': comm.max_students,
|
||||
'current_students': comm.current_students
|
||||
}
|
||||
if comm.id not in comm_ids_with_res_week:
|
||||
unassigned_commissions_week.append(comm_data)
|
||||
if comm.id not in comm_ids_with_res_month:
|
||||
unassigned_commissions_month.append(comm_data)
|
||||
|
||||
# ---- Bedelía: Aulas disponibles (sin reservas) en la semana y mes ----
|
||||
classroom_ids_used_week = set(
|
||||
r[0] for r in db.session.query(Reservation.classroom_id).filter(
|
||||
Reservation.classroom_id.isnot(None),
|
||||
Reservation.start_time >= week_start,
|
||||
Reservation.start_time <= week_end
|
||||
).distinct().all()
|
||||
)
|
||||
classroom_ids_used_month = set(
|
||||
r[0] for r in db.session.query(Reservation.classroom_id).filter(
|
||||
Reservation.classroom_id.isnot(None),
|
||||
Reservation.start_time >= month_start,
|
||||
Reservation.start_time <= month_end
|
||||
).distinct().all()
|
||||
)
|
||||
|
||||
available_classrooms_week = []
|
||||
available_classrooms_month = []
|
||||
for c in all_active_classrooms:
|
||||
c_data = {
|
||||
'id': c.id,
|
||||
'code': c.code,
|
||||
'building': c.building or 'Sin edificio',
|
||||
'floor': c.floor or '',
|
||||
'capacity': c.capacity,
|
||||
'is_virtual': getattr(c, 'is_virtual', False) or 'virtual' in (c.building or '').lower() or 'virtual' in (c.code or '').lower()
|
||||
}
|
||||
if c.id not in classroom_ids_used_week:
|
||||
available_classrooms_week.append(c_data)
|
||||
if c.id not in classroom_ids_used_month:
|
||||
available_classrooms_month.append(c_data)
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error fetching stats: {e}")
|
||||
total_classrooms = 0
|
||||
@@ -171,6 +257,11 @@ def get_stats():
|
||||
pending_reservations = []
|
||||
today_schedule_list = []
|
||||
upcoming_reservations = []
|
||||
unassigned_commissions_week = []
|
||||
unassigned_commissions_month = []
|
||||
available_classrooms_week = []
|
||||
available_classrooms_month = []
|
||||
total_active_commissions = 0
|
||||
milestones = []
|
||||
|
||||
# Mocked monthly data for chart since complex SQL group by month might differ based on dialect (SQLite vs Postgres)
|
||||
@@ -207,14 +298,21 @@ def get_stats():
|
||||
'assigned_today_classrooms': assigned_today_count,
|
||||
'assigned_today_pct': assigned_today_pct,
|
||||
'unassigned_today_classrooms': unassigned_today_count,
|
||||
'unassigned_today_pct': unassigned_today_pct
|
||||
'unassigned_today_pct': unassigned_today_pct,
|
||||
'total_active_commissions': total_active_commissions
|
||||
},
|
||||
'audit_logs': audit_data,
|
||||
'monthly_data': monthly_data,
|
||||
'pending_reservations': pending_reservations,
|
||||
'today_schedule_list': today_schedule_list,
|
||||
'upcoming_reservations': upcoming_reservations,
|
||||
'milestones': milestones
|
||||
'milestones': milestones,
|
||||
'bedelia': {
|
||||
'unassigned_commissions_week': unassigned_commissions_week,
|
||||
'unassigned_commissions_month': unassigned_commissions_month,
|
||||
'available_classrooms_week': available_classrooms_week,
|
||||
'available_classrooms_month': available_classrooms_month
|
||||
}
|
||||
}), 200
|
||||
|
||||
@api_dashboard_bp.route('/sync-sheets', methods=['POST'])
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
bandit>=1.7.5
|
||||
pip-audit>=2.7.2
|
||||
schemathesis>=3.28.6
|
||||
njsscan>=0.3.5
|
||||
Reference in New Issue
Block a user