# # version update # import os # import pickle # import threading # from flask import Flask, request, jsonify # from flask_cors import CORS # from supabase import create_client, Client # from sentence_transformers import SentenceTransformer # from sklearn.metrics.pairwise import cosine_similarity # import resend # app = Flask(__name__) # CORS(app) # # ========================================== # # ⚠️ CONFIGURATION (À VÉRIFIER) # # ========================================== # SUPABASE_URL = "https://dvddftdtrkidsulcxaqp.supabase.co" # SUPABASE_KEY = "sb_secret_CoFpwT9q6IrR-lfzjXynKg_DCoyB8F0" # # resend.api_key = "re_CYUPs5Nt_A3L3t2EDX1UT5JbBLLycqTHM" # # supabase: Client = create_client(SUPABASE_URL, SUPABASE_KEY) # print("⏳ Chargement du modèle IA...") # model_ia = SentenceTransformer('paraphrase-multilingual-MiniLM-L12-v2') # # def load_vectors(): # with open("opportunities_vectors.pkl", "rb") as f: # # return pickle.load(f) # vector_data = load_vectors() # # 📧 Notification Email en Arrière-plan # def send_email_background(nom, email, domaine, opportunites): # # C'est cette ligne qui définit le nom vu par l'utilisateur # # sender_email = "EduConnect Afrika " # sender_email = "EduConnect Afrika " # bourses_html = "".join([ # f"
  • [{b.get('type', 'Opportunité')}] {b.get('titre', '')} - 📍 {b.get('pays', 'En ligne')}
  • " # for b in opportunites # ]) # try: # resend.Emails.send({ # "from": sender_email, # "to": email, # "subject": f"🎯 Vos opportunités en {domaine} sont prêtes !", # "html": f""" #
    #

    Félicitations {nom} !

    #

    Notre IA a analysé votre profil. Voici les meilleures opportunités pour vous :

    #
      # {bourses_html} #
    #

    Accédez à votre espace pour postuler :

    #
    # Accéder au Dashboard #
    #
    #

    # EduConnect Afrika
    # L'avenir de l'orientation académique en Afrique.
    # Responsable : Lauryane #

    #
    # """ # }) # print(f"✅ Email envoyé avec succès via EduConnect à {email}") # except Exception as e: # print(f"❌ Erreur d'envoi : {e}") # @app.route('/api/recommend', methods=['POST']) # def get_recommendations(): # data = request.json # user_id = data.get('user_id') # try: # # 1. Profil utilisateur # res_profile = supabase.table('profiles').select('*').eq('user_id', user_id).execute() # if not res_profile.data: return jsonify({"error": "Profil introuvable"}), 404 # user = res_profile.data[0] # filiere = user.get('filiere') or "votre domaine" # nom_etudiant = user.get('name') or "Étudiant" # email_etudiant = user.get('email') # # 2. Vectorisation du profil # profil_text = f"Niveau: {user.get('niveau')}. Domaine: {filiere}. Intérêts: {user.get('interets')}." # user_vector = model_ia.encode([profil_text]) # # 3. Similarité et Scoring # similarities = cosine_similarity(user_vector, vector_data["vectors"])[0] # top_indices = similarities.argsort()[-15:][::-1] # On prend un peu plus pour mixer # scores_dict = {int(vector_data["ids"][idx]): min(0.99, float(similarities[idx]) + 0.35) for idx in top_indices} # # 4. Récupération unifiée des opportunités # top_ids = list(scores_dict.keys()) # res_opps = supabase.table('opportunities').select('*').in_('id', top_ids).execute() # recommandations = [] # for opp in res_opps.data: # opp['score_ia'] = scores_dict[opp['id']] # recommandations.append(opp) # # Tri final par score # recommandations = sorted(recommandations, key=lambda x: x['score_ia'], reverse=True) # # 🚀 5. Email en arrière-plan # if email_etudiant: # thread = threading.Thread(target=send_email_background, args=(nom_etudiant, email_etudiant, filiere, recommandations[:3])) # thread.start() # return jsonify({"status": "success", "recommandations": recommandations}) # except Exception as e: # print(f"❌ Erreur: {e}") # return jsonify({"error": str(e)}), 500 # if __name__ == '__main__': # app.run(port=5000, debug=True) # code pour la prod # version update - Sécurisée pour Hugging Face Spaces import os import pickle import threading from flask import Flask, request, jsonify from flask_cors import CORS from supabase import create_client, Client from sentence_transformers import SentenceTransformer from sklearn.metrics.pairwise import cosine_similarity import resend app = Flask(__name__) @app.route('/') def home(): return jsonify({ "status": "online", "message": "EduConnect Afrika API is running successfully!", "version": "1.0.0" }) # Autorise ton frontend Vercel à appeler cette API CORS(app) # ========================================== # 🔐 CONFIGURATION SÉCURISÉE (VIA SECRETS HF) # ========================================== # On récupère les clés depuis l'environnement du serveur SUPABASE_URL = os.getenv("SUPABASE_URL") SUPABASE_KEY = os.getenv("SUPABASE_KEY") resend.api_key = os.getenv("RESEND_API_KEY") # Vérification au démarrage pour éviter les crashs silencieux if not SUPABASE_URL or not SUPABASE_KEY: print("❌ ERREUR : Les variables d'environnement Supabase sont manquantes !") supabase: Client = create_client(SUPABASE_URL, SUPABASE_KEY) print("⏳ Chargement du modèle IA (paraphrase-multilingual)...") model_ia = SentenceTransformer('paraphrase-multilingual-MiniLM-L12-v2') def load_vectors(): try: with open("opportunities_vectors.pkl", "rb") as f: return pickle.load(f) except FileNotFoundError: print("❌ ERREUR : Le fichier opportunities_vectors.pkl est introuvable !") return None vector_data = load_vectors() # 📧 Notification Email en Arrière-plan def send_email_background(nom, email, domaine, opportunites): # Expéditeur utilisant ton domaine afriaisolutions.com validé sur Resend sender_email = "EduConnect Afrika " bourses_html = "".join([ f"
  • [{b.get('type', 'Opportunité')}] {b.get('titre', '')} - 📍 {b.get('pays', 'En ligne')}
  • " for b in opportunites ]) try: resend.Emails.send({ "from": sender_email, "to": email, "subject": f"🎯 Vos opportunités en {domaine} sont prêtes !", "html": f"""

    Félicitations {nom} !

    Notre IA a analysé votre profil. Voici les meilleures opportunités pour vous :

      {bourses_html}

    Accédez à votre espace pour postuler :

    Accéder au Dashboard

    EduConnect Afrika
    L'avenir de l'orientation académique en Afrique.
    Responsable : Lauryane

    """ }) print(f"✅ Email envoyé avec succès à {email}") except Exception as e: print(f"❌ Erreur d'envoi Resend : {e}") @app.route('/api/recommend', methods=['POST']) def get_recommendations(): data = request.json user_id = data.get('user_id') if not user_id: return jsonify({"error": "user_id manquant"}), 400 try: # 1. Récupération du profil res_profile = supabase.table('profiles').select('*').eq('user_id', user_id).execute() if not res_profile.data: return jsonify({"error": "Profil introuvable"}), 404 user = res_profile.data[0] filiere = user.get('filiere') or "votre domaine" nom_etudiant = user.get('name') or "Étudiant" email_etudiant = user.get('email') # 2. Vectorisation du profil utilisateur profil_text = f"Niveau: {user.get('niveau')}. Domaine: {filiere}. Intérêts: {user.get('interets')}." user_vector = model_ia.encode([profil_text]) # 3. Calcul de similarité (Cosine Similarity) similarities = cosine_similarity(user_vector, vector_data["vectors"])[0] top_indices = similarities.argsort()[-15:][::-1] # Scoring IA ajusté scores_dict = {int(vector_data["ids"][idx]): min(0.99, float(similarities[idx]) + 0.35) for idx in top_indices} # 4. Récupération des données depuis Supabase top_ids = list(scores_dict.keys()) res_opps = supabase.table('opportunities').select('*').in_('id', top_ids).execute() recommandations = [] for opp in res_opps.data: opp['score_ia'] = scores_dict[opp['id']] recommandations.append(opp) # Tri final par score décroissant recommandations = sorted(recommandations, key=lambda x: x['score_ia'], reverse=True) # 🚀 5. Notification Email (Asynchrone via Threading) if email_etudiant: thread = threading.Thread(target=send_email_background, args=(nom_etudiant, email_etudiant, filiere, recommandations[:3])) thread.start() return jsonify({"status": "success", "recommandations": recommandations}) except Exception as e: print(f"❌ Erreur API : {e}") return jsonify({"error": str(e)}), 500 if __name__ == '__main__': # Configuration obligatoire pour Hugging Face Spaces (Port 7860) app.run(host='0.0.0.0', port=7860, debug=False)