Spaces:
Sleeping
Sleeping
import requests | |
import json | |
import gradio as gr | |
import logging | |
from urllib.parse import urlparse, parse_qs | |
from gradio import Request | |
logging.basicConfig(level=logging.ERROR) | |
logger = logging.getLogger(__name__) | |
# Clés API et informations | |
OPENROUTER_API_KEY = "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" | |
AIRTABLE_API_KEY = "patUUQ6NE9zUOqooM.ec8d096169d754852305c88c7966ad1f8a151f3bf015d39f80bb895bdad0e2f5" | |
AIRTABLE_BASE_ID = "appht9RdYAQVd32Py" | |
AIRTABLE_TABLE_NAME = "DescriptionsEtudiants" | |
YOUR_SITE_URL = "votre-site.com" | |
YOUR_APP_NAME = "MonChatbot" | |
# ID de l'étudiant | |
#student_id = 34 | |
def getParams(x, request: gr.Request): | |
params = request.query_params | |
if 'name' in params: | |
return params['name'] | |
else: | |
return None | |
def get_id_by_name(student_name): | |
url = f"https://api.airtable.com/v0/{AIRTABLE_BASE_ID}/Inscription_Etudiants" | |
headers = { | |
"Authorization": f"Bearer {AIRTABLE_API_KEY}", | |
"Content-Type": "application/json" | |
} | |
# Filtrer les enregistrements par ID_Etu | |
params = { | |
"filterByFormula": f"FIND('{student_name}', {{Nom}})" | |
} | |
try: | |
response = requests.get(url, headers=headers, params=params) | |
if response.status_code == 200: | |
data = response.json() | |
if len(data['records']) > 0: | |
record = data['records'][0] # On prend le premier enregistrement correspondant | |
student_id = record['fields'].get('ID_Etu', 'Aucun id trouvé') | |
user_email = record['fields'].get('Email', 'Aucun email trouvé') | |
return student_id, user_email | |
else: | |
return None, None | |
else: | |
logger.error(f"Erreur lors de la récupération des informations : {response.status_code} - {response.text}") | |
return None, None | |
except Exception as e: | |
logger.error(f"Erreur lors de la récupération des informations : {str(e)}") | |
return None, None | |
def get_student_description(student_id): | |
url = f"https://api.airtable.com/v0/{AIRTABLE_BASE_ID}/{AIRTABLE_TABLE_NAME}" | |
headers = { | |
"Authorization": f"Bearer {AIRTABLE_API_KEY}", | |
"Content-Type": "application/json" | |
} | |
try: | |
response = requests.get(url, headers=headers) | |
if response.status_code == 200: | |
data = response.json() | |
if 'records' in data: | |
for record in data['records']: | |
if record['fields'].get('ID_Etudiant') == student_id: | |
return record['fields'].get('Description Compétences Etudiants', 'Pas de description trouvée.') | |
return "Aucun enregistrement trouvé pour cet étudiant." | |
else: | |
logger.error(f"Erreur lors de la récupération de la description : {response.status_code} - {response.text}") | |
return f"Erreur lors de la récupération : {response.status_code} - {response.text}" | |
except Exception as e: | |
logger.error(f"Erreur lors de la récupération : {str(e)}") | |
return "Erreur lors de la récupération." | |
def update_student_description(student_id, new_description): | |
# Étape 1 : Récupérer tous les enregistrements | |
url = f"https://api.airtable.com/v0/{AIRTABLE_BASE_ID}/{AIRTABLE_TABLE_NAME}" | |
headers = { | |
"Authorization": f"Bearer {AIRTABLE_API_KEY}", | |
"Content-Type": "application/json" | |
} | |
try: | |
response = requests.get(url, headers=headers) | |
if response.status_code == 200: | |
data = response.json() | |
# Étape 2 : Trouver l'enregistrement correspondant à student_id | |
record_to_update = None | |
for record in data.get('records', []): | |
if 'fields' in record and record['fields'].get('ID_Etudiant') == student_id: | |
record_to_update = record | |
break | |
if record_to_update: | |
# Étape 3 : Mettre à jour la description | |
record_id = record_to_update['id'] | |
update_url = f"https://api.airtable.com/v0/{AIRTABLE_BASE_ID}/{AIRTABLE_TABLE_NAME}/{record_id}" | |
update_data = { | |
"fields": { | |
"Description Compétences Etudiants": new_description | |
} | |
} | |
update_response = requests.patch(update_url, headers=headers, json=update_data) | |
if update_response.status_code == 200: | |
return "Description mise à jour avec succès." | |
else: | |
logger.error(f"Erreur lors de la mise à jour : {update_response.status_code} - {update_response.text}") | |
return "Erreur lors de la mise à jour." | |
else: | |
return "Aucun étudiant trouvé avec cet ID." | |
else: | |
logger.error(f"Erreur lors de la récupération des enregistrements : {response.status_code} - {response.text}") | |
return "Erreur lors de la récupération des enregistrements." | |
except Exception as e: | |
logger.error(f"Erreur lors de la mise à jour : {str(e)}") | |
return "Erreur lors de la mise à jour." | |
def call_api_for_response_analysis(current_skills, user_response): | |
messages = [ | |
{ | |
"role": "system", | |
"content": "Tu es un expert en analyse de compétences. En fonction de la réponse de l'utilisateur, " | |
"tu dois reformuler et mettre à jour les compétences actuelles. " | |
"Retourne uniquement la nouvelle description des compétences mise à jour." | |
}, | |
{ | |
"role": "user", | |
"content": f"Compétences actuelles :\n{current_skills}\n\n" | |
f"Réponse de l'utilisateur :\n{user_response}\n\n" | |
"Reformule les compétences en prenant en compte la réponse de l'utilisateur." | |
} | |
] | |
try: | |
api_response = requests.post( | |
url="https://openrouter.ai/api/v1/chat/completions", | |
headers={ | |
"Authorization": f"Bearer {OPENROUTER_API_KEY}", | |
"Content-Type": "application/json" | |
}, | |
data=json.dumps({ | |
"model": "google/gemma-2-9b-it:free", | |
"messages": messages | |
}) | |
) | |
if api_response.status_code == 200: | |
data = api_response.json() | |
return data['choices'][0]['message']['content'] | |
else: | |
logger.error(f"Erreur lors de l'analyse de la réponse : {api_response.status_code} - {api_response.text}") | |
return "Erreur lors de l'analyse de la réponse." | |
except Exception as e: | |
logger.error(f"Erreur lors de l'appel API: {str(e)}") | |
return "Erreur lors de l'appel API." | |
def get_enterprise_descriptions(): | |
url = f"https://api.airtable.com/v0/{AIRTABLE_BASE_ID}/Formulaire firm" | |
headers = { | |
"Authorization": f"Bearer {AIRTABLE_API_KEY}", | |
"Content-Type": "application/json" | |
} | |
response = requests.get(url, headers=headers) | |
if response.status_code == 200: | |
data = response.json() | |
firm_data = [ | |
( | |
record['fields'].get('Recruteur / Entreprise'), | |
record['fields'].get('Compétences requises'), | |
record['fields'].get('ID_Offre'), | |
record['fields'].get('email') | |
) | |
for record in data['records'] | |
] | |
return firm_data | |
else: | |
logger.error(f"Erreur lors de la récupération des descriptions d'entreprises : {response.status_code} - {response.text}") | |
return [] | |
def compare_skills_ai(student_skills, enterprise_skills): | |
messages = [ | |
{"role": "system", "content": "Vous êtes un expert en recrutement chargé d'évaluer la correspondance entre les compétences d'un étudiant et celles requises par une entreprise. Votre tâche est d'analyser ces compétences et de fournir une valeur de correspondance entre l'étudiant et les différentes entreprise, je veux simplement la note global pour chaque entreprise. Tenez compte des compétences similaires ou complémentaires, pas seulement des correspondances exactes."}, | |
{"role": "user", "content": f"Compétences de l'étudiant :\n{student_skills}\n\nCompétences requises par l'entreprise :\n{enterprise_skills}\n\nVeuillez analyser ces compétences et fournir un chiffre entre 0 et 100 pour la correspondance entre l'etudiant et l'entreprise afin de voir le taux de compatibilité entre l'etudiants et les différentes entreprises. ne détaille rien, juste la note global sans le détail. Ecrit que et uniquement le score, par exemple : 100 et pas : Score : 100, juste 100. Sans tiret nin rien juste le nombre"} | |
] | |
try: | |
response = requests.post( | |
url="https://openrouter.ai/api/v1/chat/completions", | |
headers={ | |
"Authorization": f"Bearer {OPENROUTER_API_KEY}", | |
"HTTP-Referer": f"{YOUR_SITE_URL}", | |
"X-Title": f"{YOUR_APP_NAME}", | |
"Content-Type": "application/json" | |
}, | |
data=json.dumps({ | |
"model": "google/gemma-2-9b-it:free", | |
"messages": messages | |
}) | |
) | |
if response.status_code == 200: | |
data = response.json() | |
ai_analysis = data['choices'][0]['message']['content'] | |
try: | |
score = int(ai_analysis.strip()) | |
return score | |
except ValueError: | |
logger.error(f"Erreur lors de la conversion du score : la réponse était '{ai_analysis}'") | |
return "Erreur lors de l'analyse des compétences : score non valide." | |
else: | |
logger.error(f"Erreur lors de l'appel à l'API : {response.status_code} - {response.text}") | |
return "Erreur lors de l'analyse des compétences." | |
except Exception as e: | |
logger.error(f"Erreur lors de l'appel API: {str(e)}") | |
return f"Erreur lors de l'analyse des compétences : {str(e)}" | |
def update_compatibility_table(student_id, skill_assessment, enterprise_skills, offer_id, compatibility_rate, student_name, student_mail, emailEntreprise, enterprise_name): | |
url = f"https://api.airtable.com/v0/{AIRTABLE_BASE_ID}/TauxCompatibilité" | |
headers = { | |
"Authorization": f"Bearer {AIRTABLE_API_KEY}", | |
"Content-Type": "application/json" | |
} | |
data = { | |
"fields": { | |
"ID_Etu": student_id, | |
"DescriptionEtu": skill_assessment, | |
"ID_Offre": offer_id, | |
"DescriptionOffre": enterprise_skills, | |
"Taux de compatibilité" : compatibility_rate, | |
"Nom": student_name, | |
"EmailEtudiant": student_mail, | |
"EmailEntreprise": emailEntreprise, | |
"Nom_Recruteur_Entreprise": enterprise_name, | |
"Postuler": "Non" | |
} | |
} | |
try: | |
response = requests.post(url, headers=headers, json=data) | |
if response.status_code == 200: | |
return "Données ajoutées avec succès !" | |
else: | |
logger.error(f"Erreur lors de l'ajout à la table de compatibilité : {response.status_code} - {response.text}") | |
return f"Erreur lors de l'ajout à la table de compatibilité : {response.status_code}" | |
except Exception as e: | |
logger.error(f"Erreur lors de l'ajout à Airtable : {str(e)}") | |
return f"Erreur lors de l'ajout à Airtable : {str(e)}" | |
def delete_compatibility_records(student_id): | |
""" | |
Supprime tous les enregistrements de la table TauxCompatibilité associés à un étudiant donné (ID_Etu). | |
""" | |
url = f"https://api.airtable.com/v0/{AIRTABLE_BASE_ID}/TauxCompatibilité" | |
headers = { | |
"Authorization": f"Bearer {AIRTABLE_API_KEY}", | |
"Content-Type": "application/json" | |
} | |
try: | |
# Récupérer les enregistrements existants pour l'ID_Etu donné | |
response = requests.get(url, headers=headers) | |
response.raise_for_status() | |
records = response.json().get('records', []) | |
# Supprimer les enregistrements existants avec l'ID_Etu correspondant | |
for record in records: | |
if record['fields'].get('ID_Etu') == student_id: | |
delete_url = f"{url}/{record['id']}" | |
delete_response = requests.delete(delete_url, headers=headers) | |
delete_response.raise_for_status() # Vérifier si la suppression a réussi | |
return "Suppression des enregistrements existants réussie." | |
except Exception as e: | |
logger.error(f"Erreur lors de la suppression des enregistrements : {str(e)}") | |
return f"Erreur lors de la suppression des enregistrements : {str(e)}" | |
def start_conversation(request: gr.Request): | |
student_name = getParams(None, request) | |
student_id, student_mail = get_id_by_name(student_name) | |
description = get_student_description(student_id) | |
initial_message = f"Bienvenue ! Voici vos compétences :\n{description}\n\nQue souhaitez-vous modifier ou améliorer dans vos compétences ?" | |
return [[None, initial_message]], description | |
def chatbot_conversation(message, history, request: gr.Request): | |
student_name = getParams(None, request) | |
student_id, student_mail = get_id_by_name(student_name) | |
current_skills = get_student_description(student_id) | |
# Analyse la réponse et obtient les compétences mises à jour | |
updated_skills = call_api_for_response_analysis(current_skills, message) | |
enterprise_descriptions = get_enterprise_descriptions() | |
results = [] | |
delete_compatibility_records(student_id) | |
for enterprise_name, enterprise_desc, offer_id, emailEntreprise in enterprise_descriptions: | |
analysis = compare_skills_ai(updated_skills, enterprise_desc) | |
add_response = update_compatibility_table(student_id, updated_skills, enterprise_desc, offer_id, analysis, student_name, student_mail, emailEntreprise, enterprise_name) | |
results.append(f"Entreprise : {enterprise_name}\n{add_response}") | |
# Prépare la réponse pour le chatbot | |
response = "Voici la version mise à jour de vos compétences :\n\n" + updated_skills | |
history.append((message, response)) | |
return history, updated_skills | |
def validate_update(updated_skills, request: gr.Request): | |
student_name = getParams(None, request) | |
student_id, student_mail = get_id_by_name(student_name) | |
response = update_student_description(student_id, updated_skills) | |
return response | |
def open_url(): | |
return f'<a href="https://albatrossstudent.softr.app" target="_blank">Cliquez ici pour retourner au site.</a>' | |
with gr.Blocks() as demo: | |
gr.HTML(""" | |
<style> | |
#scroll-container { | |
max-height: 80vh; /* Limite la hauteur du conteneur */ | |
overflow-y: auto; /* Permet le défilement vertical */ | |
padding: 20px; /* Optionnel : ajoute un peu d'espace autour */ | |
} | |
</style> | |
""") | |
with gr.Row(elem_id="scroll-container"): | |
with gr.Column(scale=1): | |
chatbot = gr.Chatbot(label="Chatbot", elem_id="chatbot") # Chatbot pour la conversation | |
msg = gr.Textbox(label="Message", placeholder="Tapez votre message ici...") # Champ de texte pour envoyer des messages | |
skill_assessment_output = gr.Textbox(label="Compétences actuelles", interactive=False) # Sortie des compétences | |
validate_button = gr.Button("Valider la mise à jour") # Bouton pour valider la mise à jour | |
validation_output = gr.Textbox(label="Résultat de la mise à jour", interactive=False) | |
button = gr.Button("Retour au site") | |
output = gr.HTML() | |
button.click(open_url, outputs=output) | |
msg.submit(fn=chatbot_conversation, | |
inputs=[msg, chatbot], | |
outputs=[chatbot, skill_assessment_output]) | |
# Action du bouton pour mettre à jour la description | |
validate_button.click(fn=validate_update, inputs=skill_assessment_output, outputs=validation_output) # Utilisation du bloc validation_output | |
# Chargement initial du message de bienvenue | |
demo.load(start_conversation, | |
inputs=None, | |
outputs=[chatbot, skill_assessment_output]) | |
if __name__ == "__main__": | |
demo.launch(share=True) |