Spaces:
Sleeping
Sleeping
| # -*- coding: utf-8 -*- | |
| """optimisation_examen_PMNE.ipynb | |
| Automatically generated by Colab. | |
| Original file is located at | |
| https://colab.research.google.com/drive/1Uewn7e8zZiGZdAAYHqXJtl-3TA7sj5eL | |
| """ | |
| #!pip install pulp | |
| #!pip install gradio | |
| import gradio as gr | |
| import pandas as pd | |
| from pulp import LpMinimize, LpProblem, LpVariable, lpSum | |
| def planifier_examens(examens_text, salles_text, jours_text, creneaux_text, disponibilite_salle_text, conflits_text): | |
| # --- 1. Parsing des données --- | |
| # Examens | |
| examens = [] | |
| for line in examens_text.strip().splitlines(): | |
| parts = line.split(',') | |
| if len(parts) == 3: | |
| exam_id, nb_students, duration = parts | |
| examens.append((exam_id.strip(), int(nb_students.strip()), int(duration.strip()))) | |
| # Salles et capacités | |
| salles = {} | |
| for line in salles_text.strip().splitlines(): | |
| parts = line.split(',') | |
| if len(parts) == 2: | |
| room_id, capacity = parts | |
| salles[room_id.strip()] = int(capacity.strip()) | |
| # Jours et créneaux | |
| jours = [int(j.strip()) for j in jours_text.split(',')] | |
| creneaux = [int(c.strip()) for c in creneaux_text.split(',')] | |
| # Disponibilité des salles | |
| disponibilite_salle = {} | |
| for line in disponibilite_salle_text.strip().splitlines(): | |
| parts = line.split(',') | |
| room_id = parts[0].strip() | |
| disponibilite_salle[room_id] = [int(s) for s in parts[1:]] | |
| # Conflits entre examens | |
| conflits = [] | |
| for line in conflits_text.strip().splitlines(): | |
| parts = line.split(',') | |
| conflits.append((parts[0].strip(), parts[1].strip())) | |
| # --- 2. Modélisation avec PuLP (PMNE) --- | |
| model = LpProblem("Planification_Examens", LpMinimize) | |
| # Variables de décision | |
| X = {(e, d, c, s): LpVariable(f"X_{e}_{d}_{c}_{s}", cat="Binary") | |
| for e, _, _ in examens for d in jours for c in creneaux for s in salles} | |
| Y = {d: LpVariable(f"Y_{d}", cat="Binary") for d in jours} | |
| # Contrainte 1 : Chaque examen doit être programmé une seule fois | |
| for e, _, _ in examens: | |
| model += lpSum(X[e, d, c, s] for d in jours for c in creneaux for s in salles) == 1 | |
| # Contrainte 2 : Capacité des salles respectée | |
| for d in jours: | |
| for c in creneaux: | |
| for s in salles: | |
| model += lpSum(nb_students * X[e, d, c, s] for e, nb_students, _ in examens) <= salles[s] | |
| # Contrainte 3 : Une salle ne peut accueillir qu’un seul examen par créneau | |
| for d in jours: | |
| for c in creneaux: | |
| for s in salles: | |
| model += lpSum(X[e, d, c, s] for e, _, _ in examens) <= 1 | |
| # Contrainte 4 : Disponibilité des salles | |
| for e, _, _ in examens: | |
| for d in jours: | |
| for c in creneaux: | |
| for s in salles: | |
| if disponibilite_salle[s][c - 1] == 0: | |
| model += X[e, d, c, s] == 0 | |
| # Contrainte 5 : Conflits entre examens (ne pas être en même temps) | |
| for e1, e2 in conflits: | |
| for d in jours: | |
| for c in creneaux: | |
| model += lpSum(X[e1, d, c, s] for s in salles) + lpSum(X[e2, d, c, s] for s in salles) <= 1 | |
| # Contrainte 6 : Activation des jours | |
| for d in jours: | |
| for e, _, _ in examens: | |
| for c in creneaux: | |
| for s in salles: | |
| model += Y[d] >= X[e, d, c, s] | |
| # Fonction Objectif : Minimiser le nombre de jours utilisés | |
| model += lpSum(Y[d] for d in jours) | |
| # --- 3. Résolution --- | |
| model.solve() | |
| # --- 4. Extraction des résultats --- | |
| planning = [] | |
| for e, _, _ in examens: | |
| for d in jours: | |
| for c in creneaux: | |
| for s in salles: | |
| if X[e, d, c, s].varValue == 1: | |
| planning.append({"Examen": e, "Jour": d, "Créneau": c, "Salle": s}) | |
| df = pd.DataFrame(planning) | |
| df.to_csv("planning_examens.csv", index=False) | |
| return df, "planning_examens.csv" | |
| # Interface avec Gradio | |
| with gr.Blocks() as demo: | |
| gr.Markdown("# 📅 Planification des Examens avec PMNE et Gradio") | |
| exams_input = gr.Textbox(label="Examens (exam_id, nb_étudiants, durée)", lines=5, | |
| value="E1, 30, 2\nE2, 25, 1\nE3, 40, 2\nE4, 20, 1") | |
| rooms_input = gr.Textbox(label="Salles (room_id, capacité)", lines=3, | |
| value="S1, 50\nS2, 30\nS3, 40") | |
| jours_input = gr.Textbox(label="Jours disponibles (séparés par des virgules)", value="1, 2, 3") | |
| creneaux_input = gr.Textbox(label="Créneaux disponibles (séparés par des virgules)", value="1, 2, 3, 4") | |
| disponibilite_input = gr.Textbox(label="Disponibilité des salles (room_id, slot1, slot2, ...)", lines=4, | |
| value="S1, 1, 1, 1, 1\nS2, 1, 1, 0, 1\nS3, 1, 1, 1, 0") | |
| conflits_input = gr.Textbox(label="Conflits entre examens (exam1, exam2)", lines=3, | |
| value="E1, E3") | |
| output_table = gr.Dataframe(headers=["Examen", "Jour", "Créneau", "Salle"], label="Planning des examens") | |
| output_file = gr.File(label="Télécharger le planning") | |
| solve_btn = gr.Button("Planifier les examens") | |
| solve_btn.click( | |
| fn=planifier_examens, | |
| inputs=[exams_input, rooms_input, jours_input, creneaux_input, disponibilite_input, conflits_input], | |
| outputs=[output_table, output_file] | |
| ) | |
| demo.launch() | |