meedv/NextStepAI
0
1from flask import Flask, render_template, request, jsonify2import joblib, json, numpy as np3from pymongo import MongoClient4import os5 6# --- Connexion sécurisée à MongoDB ---7MONGO_URI = os.environ.get("MONGO_URI")8if MONGO_URI:9 client = MongoClient(MONGO_URI)10 db = client['orientation_db']11 collection = db['student_responses']12 13app = Flask(__name__)14 15# --- Chargement du modèle + encoders ---16model = joblib.load('model_xgb.pkl')17le_target = joblib.load('le_target.pkl')18encoders = joblib.load('encoders.pkl')19with open('model_meta.json', encoding='utf-8') as f:20 meta = json.load(f)21 22FEATURES = meta['features']23CAT_COLS = meta['cat_cols']24NUM_COLS = meta['num_cols']25 26FILIERES_INFO = {27 "3G": {"nom":"Géosciences, Géodynamique et Géomatique","couleur":"#92400e","icone":"🌍","debouches":["Géologue","Ingénieur mines","Cartographe","Environnement"],"modules":["Géophysique","Cartographie","Géologie","Géostatistiques"]},28 "BPV": {"nom":"Biotechnologie et Production Végétale","couleur":"#15803d","icone":"🌿","debouches":["Ingénieur agronome","Biotechnologiste","Agriculture","Industrie pharma"],"modules":["Biotechnologie végétale","Physiologie végétale","Génétique","Agroalimentaire"]},29 "COA": {"nom":"Chimie Organique Appliquée","couleur":"#dc2626","icone":"🧪","debouches":["Chimiste","Agroalimentaire","Pharmacie","Cosmétique"],"modules":["Chimie organique","Polymères","Réactions organiques","Agroalimentaire"]},30 "CPA": {"nom":"Chimie Physique et Applications","couleur":"#ea580c","icone":"🔬","debouches":["Ingénieur procédés","Chimiste analytique","Environnement"],"modules":["Electrochimie","Cinétique","Catalyse","Environnement"]},31 "GEER": {"nom":"Génie Electrique et Energies Renouvelables","couleur":"#ca8a04","icone":"⚡","debouches":["Ingénieur électrique","Energies renouvelables","Industrie électrique"],"modules":["Electronique","Energies renouvelables","Automatique","Réseaux électriques"]},32 "IMEI": {"nom":"Ingénierie Mécanique et Energétiques Industrielles","couleur":"#0369a1","icone":"⚙️","debouches":["Ingénieur mécanique","Industrie","Bureau d'études"],"modules":["Mécanique","Thermodynamique","Matériaux","CAO"]},33 "MA": {"nom":"Mathématiques et Applications","couleur":"#059669","icone":"📐","debouches":["Chercheur","Enseignant","Actuaire","Grandes écoles"],"modules":["Analyse","Algèbre","Topologie","Calcul différentiel"]},34 "MI": {"nom":"Développement et Bases de données","couleur":"#2563eb","icone":"💻","debouches":["Développeur web","Admin BDD","Ingénieur logiciel"],"modules":["Programmation web","Bases de données","Réseaux","POO Java"]},35 "MIA": {"nom":"Matériaux Inorganiques et Applications","couleur":"#d97706","icone":"⚗️","debouches":["Ingénieur matériaux","Chercheur chimiste","Industrie"],"modules":["Chimie des matériaux","Cristallographie","Electrochimie","Spectroscopie"]},36 "PM": {"nom":"Physique Moderne","couleur":"#7c3aed","icone":"🔭","debouches":["Chercheur physicien","Enseignant","Ingénieur R&D"],"modules":["Physique quantique","Optique","Mécanique","Physique des matériaux"]},37 "SD": {"nom":"Sciences des données","couleur":"#6d28d9","icone":"📊","debouches":["Data Scientist","Analyste données","Ingénieur ML"],"modules":["Machine learning","Big data","Python","Statistiques"]},38 "SESBM":{"nom":"Santé, Environnement et Sciences Bio Médicales","couleur":"#0891b2","icone":"🧬","debouches":["Biologiste","Chercheur santé","Pharmacie","Médecine"],"modules":["Biochimie","Microbiologie","Génétique","Immunologie"]},39 "TR": {"nom":"Télécommunications et Réseaux","couleur":"#0f766e","icone":"📡","debouches":["Ingénieur télécom","Réseaux","Opérateur télécoms"],"modules":["Réseaux","Télécoms","Protocoles","Systèmes embarqués"]},40}41 42RENFORCER = {43 "MI": [("note_math",13,"Mathématiques"),("note_biologie",0,"—")],44 "SD": [("note_math",13,"Mathématiques & Statistiques"),("note_biologie",0,"—")],45 "MA": [("note_math",16,"Mathématiques (niveau élevé requis)")],46 "MIA": [("note_chimie",13,"Chimie"),("note_physique",13,"Physique")],47 "COA": [("note_chimie",13,"Chimie organique")],48 "CPA": [("note_chimie",13,"Chimie physique"),("note_physique",13,"Physique")],49 "PM": [("note_physique",14,"Physique")],50 "GEER": [("note_physique",13,"Physique"),("note_math",12,"Mathématiques")],51 "IMEI": [("note_physique",13,"Physique"),("note_math",12,"Mathématiques")],52 "TR": [("note_physique",12,"Physique"),("note_math",12,"Mathématiques")],53 "SESBM":[("note_biologie",13,"Biologie / SVT")],54 "BPV": [("note_biologie",13,"Biologie végétale")],55 "3G": [("note_biologie",11,"SVT & Géologie")],56}57 58def safe_encode(col, val):59 le = encoders.get(col)60 if le is None: return 061 try: return int(le.transform([str(val)])[0])62 except: return 063 64def build_X(d):65 row = {66 "moyenne_generale_bac": float(d.get("moyenne_generale_bac",12)),67 "note_math": float(d.get("note_math",12)),68 "note_physique": float(d.get("note_physique",12)),69 "note_chimie": float(d.get("note_chimie",12)),70 "note_biologie": float(d.get("note_biologie",12)),71 "q6_interet_biologie": float(d.get("q6",3)),72 "q7_interet_chimie": float(d.get("q7",3)),73 "q8_interet_math": float(d.get("q8",3)),74 "q9_interet_physique": float(d.get("q9",3)),75 "q10_interet_informatique": float(d.get("q10",3)),76 "q11_interet_terrain": float(d.get("q11",3)),77 "q12_interet_sante": float(d.get("q12",3)),78 "q13_interet_environnement": float(d.get("q13",3)),79 "q14_interet_industrie": float(d.get("q14",3)),80 "q15_interet_developpement_logiciel": float(d.get("q15",3)),81 "q16_interet_data_ia": float(d.get("q16",3)),82 "q17_niveau_memorisation": float(d.get("q17",3)),83 "q18_niveau_analyse": float(d.get("q18",3)),84 "q19_niveau_abstraction": float(d.get("q19",3)),85 "q20_niveau_programmation": float(d.get("q20",3)),86 "q21_aisance_laboratoire_bio": float(d.get("q21",3)),87 "q22_aisance_laboratoire_phys_chim": float(d.get("q22",3)),88 "q23_aisance_projets_numeriques": float(d.get("q23",3)),89 "q24_aisance_sorties_terrain": float(d.get("q24",3)),90 "sexe_enc": safe_encode("sexe", d.get("sexe","M")),91 "bac_type_enc": safe_encode("bac_type", d.get("bac_type","SM")),92 "bac_mention_enc": safe_encode("bac_mention", d.get("bac_mention","Bien")),93 "q1_matiere_preferee_enc": safe_encode("q1_matiere_preferee", d.get("q1","")),94 "q2_activite_preferee_enc": safe_encode("q2_activite_preferee", d.get("q2","")),95 "q3_type_problemes_prefere_enc": safe_encode("q3_type_problemes_prefere", d.get("q3","")),96 "q4_preference_travail_enc": safe_encode("q4_preference_travail", d.get("q4","Individuel")),97 "q5_preference_environnement_enc":safe_encode("q5_preference_environnement", d.get("q5","Mixte")),98 "q25_objectif_carriere_enc": safe_encode("q25_objectif_carriere", d.get("q25","")),99 }100 return np.array([[row[f] for f in FEATURES]])101 102@app.route("/")103def index():104 return render_template("index.html")105 106@app.route("/recommander", methods=["POST"])107def recommander():108 data = request.json109 X = build_X(data)110 proba = model.predict_proba(X)[0]111 top3 = np.argsort(proba)[::-1][:3]112 113 results = []114 for rank, idx in enumerate(top3):115 code = le_target.classes_[idx]116 score = float(proba[idx])117 info = FILIERES_INFO.get(code, {"nom":code,"couleur":"#64748b","icone":"🎓","debouches":[],"modules":[]})118 119 renforcer = []120 for nkey, seuil, label in RENFORCER.get(code, []):121 if seuil > 0 and float(data.get(nkey, 12)) < seuil:122 renforcer.append(label)123 if not renforcer:124 renforcer = ["✅ Bon profil — continuez!"]125 126 results.append({127 "rang": rank+1, "code": code,128 "nom": info["nom"], "couleur": info["couleur"], "icone": info["icone"],129 "taux": round(score*100, 1),130 "modules": info["modules"], "debouches": info["debouches"],131 "renforcer": renforcer132 })133 134 # --- ENREGISTREMENT AUTOMATIQUE DANS MONGODB ---135 if MONGO_URI:136 try:137 # On fait une copie propre des données reçues pour ne pas altérer le flux138 donnees_a_sauvegarder = dict(data)139 # On enregistre le Top 1 recommandé (le choix principal du modèle)140 donnees_a_sauvegarder['prediction_proposee'] = results[0]['nom']141 donnees_a_sauvegarder['code_filiere'] = results[0]['code']142 143 # Insertion dans la base Cloud144 collection.insert_one(donnees_a_sauvegarder)145 print("Données enregistrées avec succès dans MongoDB Atlas !")146 except Exception as e:147 print(f"Erreur MongoDB lors de la sauvegarde : {e}")148 149 return jsonify(results)150 151if __name__ == "__main__":152 app.run(host="0.0.0.0", port=7860)