""" Ce fichier permet de gerer les unités d'enseignement (UE) Une unité d'enseignement est definit par : - Code (valeur unique & clé) - Titre - Description - prerequis - Objectif - Programme - Méthode pédagogique - Méthode d'évaluation - Support - Durée (heures, jours, semaines, mois) Rattachement Unité d’enseignement (UE) & formation : Lors du rattachement d’une UE à une formation, il faudra définir : - Le crédit associé (numérique) - Présentiel/ distantiel/ hybride, - Est_note (oui / non) pour savoir cette unité doit être notée - Groupe_evaluation : Dans une formation, on peut rassembler plusieurs UE dans un groupe d’UE """ import bson import pymongo from pymongo import MongoClient import json from bson import ObjectId import re from datetime import datetime import prj_common as mycommon import secrets import inspect import sys, os import csv import pandas as pd from pymongo import ReturnDocument import GlobalVariable as MYSY_GV from math import isnan import GlobalVariable as MYSY_GV import ela_index_bdd_classes as eibdd import email_mgt as email import jinja2 from flask import send_file from xhtml2pdf import pisa from email.message import EmailMessage from email.mime.text import MIMEText from email import encoders import smtplib from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText from email.mime.base import MIMEBase from email import encoders """ Ajout d'une unité d'enseignement (UE) """ def Add_Unite_Enseignement(diction): try: diction = mycommon.strip_dictionary(diction) """ Verification des input acceptés """ field_list = ['token', 'code', 'titre', 'description', 'objectif', 'prerequis', 'programme', 'methode_pedagogique', 'methode_evaluation', 'support', 'duration', 'duration_unite' ] incom_keys = diction.keys() for val in incom_keys: if val not in field_list and val.startswith('my_') is False: mycommon.myprint(str( inspect.stack()[0][3]) + " Le champ '" + val + "' n'est pas autorisé") return False, " Les informations fournies sont incorrectes" """ Verification des champs obligatoires """ field_list_obligatoire = ['token', 'code', 'titre',] for val in field_list_obligatoire: if val not in diction: mycommon.myprint( str(inspect.stack()[0][3]) + " - La valeur '" + val + "' n'est pas presente dans liste ") return False, " Les informations fournies sont incorrectes" """ Verification de l'identité et autorisation de l'entité qui appelle cette API """ token = "" if ("token" in diction.keys()): if diction['token']: token = diction['token'] local_status, my_partner = mycommon.Check_Connexion_And_Return_Partner_Data(diction) if (local_status is not True): return local_status, my_partner mydata = {} mydata = diction del mydata['token'] # Initialisation des champs non envoyés à vide for val in field_list: if val not in diction.keys(): mydata[str(val)] = "" # Verifier qu'une UE avec le meme code n'existe pas déjà pour ce partenaire is_ue_exist_count = MYSY_GV.dbname['unite_enseignement'].count_documents({'partner_owner_recid':my_partner['recid'], 'valide':'1', 'code':diction['code']}) if( is_ue_exist_count > 0 ): mycommon.myprint( str(inspect.stack()[0][3]) + " Il existe déjà une UE avec le même code ") return False, " Il existe déjà une UE avec le même code " """ Verifier que l'unite de durée est valide """ if( "duration_unite" in diction.keys() and diction['duration_unite']): if( diction['duration_unite'] not in MYSY_GV.CLASS_DURATION_UNIT): mycommon.myprint( str(inspect.stack()[0][3]) + " L'unité de durée est invalide ") return False, " L'unité de durée est invalide " """ Verifier que la durée est bien un float """ if ("duration" in diction.keys() and diction['duration']): local_status, local_retval = mycommon.IsFloat(str(diction['duration'])) if( local_status is False ): mycommon.myprint( str(inspect.stack()[0][3]) + " La durée est invalide ") return False, " La durée est invalide " mydata['date_update'] = str(datetime.now()) mydata['update_by'] = str(my_partner['_id']) mydata['partner_owner_recid'] = str(my_partner['recid']) mydata['valide'] = "1" mydata['locked'] = "0" del mydata['token'] inserted_id = MYSY_GV.dbname['unite_enseignement'].insert_one(mydata).inserted_id if (not inserted_id): mycommon.myprint(" Impossible de créer l'unité d'enseignement (2) ") return False, " Impossible de créer l'unité d'enseignement (2) " return True, " L'unité d'enseignement a été correctement ajoutée " except Exception as e: exc_type, exc_obj, exc_tb = sys.exc_info() mycommon.myprint(str(inspect.stack()[0][3]) + " -" + str(e) + " - Line : " + str(exc_tb.tb_lineno)) return False, " Impossible de créer l'UE " """ Mise à jour d'une unité d'enseignement """ def Update_Unite_Enseignement(diction): try: diction = mycommon.strip_dictionary(diction) """ Verification des input acceptés """ field_list = ['token', '_id', 'code', 'titre', 'description', 'objectif', 'prerequis', 'programme', 'methode_pedagogique', 'methode_evaluation', 'support', 'duration', 'duration_unite'] incom_keys = diction.keys() for val in incom_keys: if val not in field_list and val.startswith('my_') is False: mycommon.myprint(str( inspect.stack()[0][3]) + " Le champ '" + val + "' n'est pas autorisé") return False, " Les informations fournies sont incorrectes" """ Verification des champs obligatoires """ field_list_obligatoire = ['token', '_id' ] for val in field_list_obligatoire: if val not in diction: mycommon.myprint( str(inspect.stack()[0][3]) + " - La valeur '" + val + "' n'est pas presente dans liste ") return False, " Les informations fournies sont incorrectes" """ Verification de l'identité et autorisation de l'entité qui appelle cette API """ token = "" if ("token" in diction.keys()): if diction['token']: token = diction['token'] ue_id = "" if ("_id" in diction.keys()): if diction['_id']: ue_id = diction['_id'] local_status, my_partner = mycommon.Check_Connexion_And_Return_Partner_Data(diction) if (local_status is not True): return local_status, my_partner # Verifier que l'UE existe et est valide is_valide_opport = MYSY_GV.dbname['unite_enseignement'].count_documents({'_id':ObjectId(str(ue_id)), 'valide':'1', 'locked':'0', 'partner_owner_recid':str(my_partner['recid'])}) if( is_valide_opport != 1): mycommon.myprint( str(inspect.stack()[0][3]) + " L'identifiant de l'UE est invalide ") return False, " L'identifiant de l'UE est invalide " """ Si on veut modidier le code, verifier que le nouveau code n'est pas deja prix """ if( "code" in diction.keys() ): is_new_code_exist_with_orther_id = MYSY_GV.dbname['unite_enseignement'].find_one({'code':str(diction['code']), 'valide':'1', 'locked':'0', 'partner_owner_recid':str(my_partner['recid'])}) if( is_new_code_exist_with_orther_id and str(is_new_code_exist_with_orther_id['_id']) != ue_id ): mycommon.myprint( str(inspect.stack()[0][3]) + " Le code de l'UE '"+str(diction['code'])+" est déjà utilisé ") return False, " Le code de l'UE '"+str(diction['code'])+" est déjà utilisé " mydata = {} mydata = diction del mydata['token'] del mydata['_id'] mydata['date_update'] = str(datetime.now()) mydata['update_by'] = str(my_partner['_id']) result = MYSY_GV.dbname['unite_enseignement'].find_one_and_update( {'_id':ObjectId(str(ue_id)), 'partner_owner_recid':str(my_partner['recid'])}, {"$set": mydata}, upsert=False, return_document=ReturnDocument.AFTER ) if ("_id" not in result.keys()): mycommon.myprint( " Impossible de mettre à jour l'UE (2) ") return False, " Impossible de mettre à jour l'UE (2) " return True, " L'UE a été correctement mise à jour " except Exception as e: exc_type, exc_obj, exc_tb = sys.exc_info() mycommon.myprint(str(inspect.stack()[0][3]) + " -" + str(e) + " - Line : " + str(exc_tb.tb_lineno)) return False, " Impossible de mettre à jour l'UE " """ Recuperer la liste des UE, sans filtres """ def Get_List_Unite_Enseignement_no_filter(diction): try: diction = mycommon.strip_dictionary(diction) """ Verification des input acceptés """ field_list = ['token' ] incom_keys = diction.keys() for val in incom_keys: if val not in field_list and val.startswith('my_') is False: mycommon.myprint(str( inspect.stack()[0][3]) + " Le champ '" + val + "' n'est pas autorisé") return False, " Les informations fournies sont incorrectes" """ Verification des champs obligatoires """ field_list_obligatoire = ['token',] for val in field_list_obligatoire: if val not in diction: mycommon.myprint( str(inspect.stack()[0][3]) + " - La valeur '" + val + "' n'est pas presente dans liste ") return False, " Les informations fournies sont incorrectes" """ Verification de l'identité et autorisation de l'entité qui appelle cette API """ token = "" if ("token" in diction.keys()): if diction['token']: token = diction['token'] local_status, my_partner = mycommon.Check_Connexion_And_Return_Partner_Data(diction) if (local_status is not True): return local_status, my_partner RetObject = [] val_tmp = 0 qry = {"partner_owner_recid":str(my_partner['recid']), 'valide':'1', 'locked':'0'} for New_retVal in MYSY_GV.dbname['unite_enseignement'].find(qry).sort([("_id", pymongo.DESCENDING), ]): user = New_retVal user['id'] = str(val_tmp) val_tmp = val_tmp + 1 if (str(New_retVal['duration_unite']) == "heure"): user['duration_concat'] = str(New_retVal['duration']) + " h" elif (str(New_retVal['duration_unite']) == "jour"): user['duration_concat'] = str(New_retVal['duration']) + " j" elif (str(New_retVal['duration_unite']) == "semaine"): user['duration_concat'] = str(New_retVal['duration']) + " s" elif (str(New_retVal['duration_unite']) == "mois"): user['duration_concat'] = str(New_retVal['duration']) + " m" elif (str(New_retVal['duration_unite']) == "annee"): user['duration_concat'] = str(New_retVal['duration']) + " a" else: user['duration_concat'] = str(New_retVal['duration']) + " ?" RetObject.append(mycommon.JSONEncoder().encode(user)) #print(" #### RetObject = ", RetObject) return True, RetObject except Exception as e: exc_type, exc_obj, exc_tb = sys.exc_info() mycommon.myprint(str(inspect.stack()[0][3]) + " -" + str(e) + " - Line : " + str(exc_tb.tb_lineno)) return False, " Impossible de récupérer la liste des unites d'enseignement " """ Recuperer la liste des UE, avec filtres """ def Get_List_Unite_Enseignement_with_filter(diction): try: diction = mycommon.strip_dictionary(diction) """ Verification des input acceptés """ field_list = ['token', 'code', 'titre'] incom_keys = diction.keys() for val in incom_keys: if val not in field_list and val.startswith('my_') is False: mycommon.myprint(str(inspect.stack()[0][ 3]) + " Le champ '" + val + "' n'est pas autorisé") return False, " Toutes les informations fournies ne sont pas valables" """ Verification des champs obligatoires """ field_list_obligatoire = ['token',] for val in field_list_obligatoire: if val not in diction: mycommon.myprint( str(inspect.stack()[0][3]) + " - La valeur '" + val + "' n'est pas presente dans liste ") return False, " Les informations fournies sont incorrectes" """ Verification de l'identité et autorisation de l'entité qui appelle cette API """ token = "" if ("token" in diction.keys()): if diction['token']: token = diction['token'] local_status, my_partner = mycommon.Check_Connexion_And_Return_Partner_Data(diction) if (local_status is not True): return local_status, my_partner filt_code = {} if ("code" in diction.keys()): filt_code = {'code': {'$regex': str(diction['code']), "$options": "i"}} filt_titre = {} if ("titre" in diction.keys()): filt_titre = {'titre': {'$regex': str(diction['titre']), "$options": "i"}} query = [{'$match': { '$and': [filt_code, filt_titre, {"partner_owner_recid":str(my_partner['recid']), 'valide':'1', 'locked':'0'} ]}}, {'$sort': {'_id': -1}}, ] RetObject = [] val_tmp = 0 for New_retVal in MYSY_GV.dbname['unite_enseignement'].aggregate(query): user = New_retVal user['id'] = str(val_tmp) val_tmp = val_tmp + 1 if (str(New_retVal['duration_unite']) == "heure"): user['duration_concat'] = str(New_retVal['duration']) + " h" elif (str(New_retVal['duration_unite']) == "jour"): user['duration_concat'] = str(New_retVal['duration']) + " j" elif (str(New_retVal['duration_unite']) == "semaine"): user['duration_concat'] = str(New_retVal['duration']) + " s" elif (str(New_retVal['duration_unite']) == "mois"): user['duration_concat'] = str(New_retVal['duration']) + " m" elif (str(New_retVal['duration_unite']) == "annee"): user['duration_concat'] = str(New_retVal['duration']) + " a" else: user['duration_concat'] = str(New_retVal['duration']) + " ?" RetObject.append(mycommon.JSONEncoder().encode(user)) #print(" #### RetObject = ", RetObject) return True, RetObject except Exception as e: exc_type, exc_obj, exc_tb = sys.exc_info() mycommon.myprint(str(inspect.stack()[0][3]) + " -" + str(e) + " - Line : " + str(exc_tb.tb_lineno)) return False, " Impossible de récupérer la liste des unites d'enseignement " """ Suppression d'une UE Si elle n'est pas utilisée regles : on ne peut supprimer une UE que si elle n'est pas utilisée dans une formation """ def Delete_Given_Unite_Enseignement(diction): try: diction = mycommon.strip_dictionary(diction) """ Verification des input acceptés """ field_list = ['token', '_id', ] incom_keys = diction.keys() for val in incom_keys: if val not in field_list and val.startswith('my_') is False: mycommon.myprint(str( inspect.stack()[0][3]) + " Le champ '" + val + "' n'est pas autorisé") return False, " Les informations fournies sont incorrectes" """ Verification des champs obligatoires """ field_list_obligatoire = ['token', '_id', ] for val in field_list_obligatoire: if val not in diction: mycommon.myprint( str(inspect.stack()[0][3]) + " - La valeur '" + val + "' n'est pas presente dans liste ") return False, " Les informations fournies sont incorrectes" """ Verification de l'identité et autorisation de l'entité qui appelle cette API """ token = "" if ("token" in diction.keys()): if diction['token']: token = diction['token'] local_status, my_partner = mycommon.Check_Connexion_And_Return_Partner_Data(diction) if (local_status is not True): return local_status, my_partner # Verifier l'existance de l'ue is_ue_valide = MYSY_GV.dbname['unite_enseignement'].count_documents({'_id':ObjectId(str(diction['_id'])), 'partner_owner_recid':str(my_partner['recid'])}) if (is_ue_valide != 1): mycommon.myprint( str(inspect.stack()[0][3]) + " L'identifiant de l'UE est invalide ") return False, " L'identifiant de l'UE est invalide " """ Verifier si cette formation n'est pas utilisée dans une formation """ is_ue_used_in_class_count = MYSY_GV.dbname['myclass'].count_documents({'partner_owner_recid':str(my_partner['recid']), 'valide':'1', 'list_unite_enseignement._id':str(diction['_id'])}) if( is_ue_used_in_class_count > 0 ): mycommon.myprint( str(inspect.stack()[0][3]) + " Cette UE est actuellement utilisée dans "+str(is_ue_used_in_class_count)+" formation. Impossible de la supprimer ") return False, " Cette UE est actuellement utilisée dans "+str(is_ue_used_in_class_count)+" formation. Impossible de la supprimer " qry = {'_id':ObjectId(str(diction['_id'])), 'partner_owner_recid':str(my_partner['recid'])} ret_del_competence = MYSY_GV.dbname['unite_enseignement'].delete_many(qry) return True, "L'UE a été correctement supprimée" except Exception as e: exc_type, exc_obj, exc_tb = sys.exc_info() mycommon.myprint(str(inspect.stack()[0][3]) + " -" + str(e) + " - Line : " + str(exc_tb.tb_lineno)) return False, " Impossible de supprimer l'UE " """ Recuperer une UE données """ def Get_Given_Unite_Enseignement(diction): try: diction = mycommon.strip_dictionary(diction) """ Verification des input acceptés """ field_list = ['token', '_id' ] incom_keys = diction.keys() for val in incom_keys: if val not in field_list and val.startswith('my_') is False: mycommon.myprint(str( inspect.stack()[0][3]) + " Le champ '" + val + "' n'est pas autorisé") return False, " Les informations fournies sont incorrectes" """ Verification des champs obligatoires """ field_list_obligatoire = ['token','_id'] for val in field_list_obligatoire: if val not in diction: mycommon.myprint( str(inspect.stack()[0][3]) + " - La valeur '" + val + "' n'est pas presente dans liste ") return False, " Les informations fournies sont incorrectes" """ Verification de l'identité et autorisation de l'entité qui appelle cette API """ token = "" if ("token" in diction.keys()): if diction['token']: token = diction['token'] local_status, my_partner = mycommon.Check_Connexion_And_Return_Partner_Data(diction) if (local_status is not True): return local_status, my_partner RetObject = [] val_tmp = 0 qry = {"partner_owner_recid":str(my_partner['recid']), 'valide':'1', 'locked':'0', '_id':ObjectId(str(diction['_id']))} for New_retVal in MYSY_GV.dbname['unite_enseignement'].find(qry).sort([("_id", pymongo.DESCENDING), ]): user = New_retVal user['id'] = str(val_tmp) val_tmp = val_tmp + 1 RetObject.append(mycommon.JSONEncoder().encode(user)) #print(" #### RetObject = ", RetObject) return True, RetObject except Exception as e: exc_type, exc_obj, exc_tb = sys.exc_info() mycommon.myprint(str(inspect.stack()[0][3]) + " -" + str(e) + " - Line : " + str(exc_tb.tb_lineno)) return False, " Impossible de récupérer les données de l'unite d'enseignement " """ Recuperer la liste des UE d'une formation donnée """ def Get_List_Unite_Enseignement_Of_Given_Class(diction): try: diction = mycommon.strip_dictionary(diction) """ Verification des input acceptés """ field_list = ['token', 'class_id', 'class_internal_url'] incom_keys = diction.keys() for val in incom_keys: if val not in field_list and val.startswith('my_') is False: mycommon.myprint(str( inspect.stack()[0][3]) + " Le champ '" + val + "' n'est pas autorisé") return False, " Les informations fournies sont incorrectes" """ Verification des champs obligatoires """ field_list_obligatoire = ['token',] for val in field_list_obligatoire: if val not in diction: mycommon.myprint( str(inspect.stack()[0][3]) + " - La valeur '" + val + "' n'est pas presente dans liste ") return False, " Les informations fournies sont incorrectes" """ Verification de l'identité et autorisation de l'entité qui appelle cette API """ token = "" if ("token" in diction.keys()): if diction['token']: token = diction['token'] local_status, my_partner = mycommon.Check_Connexion_And_Return_Partner_Data(diction) if (local_status is not True): return local_status, my_partner RetObject = [] val_tmp = 0 is_qry_data = "0" if( "class_id" in diction.keys() and diction['class_id']) : is_qry_data = "1" if ("class_internal_url" in diction.keys() and diction['class_internal_url']): is_qry_data = "1" if( is_qry_data == "0" ): # L'utilisateur n'a fournir aucun element pour faire la requete. on retourne du vide dans ce cas. user = [] RetObject.append(mycommon.JSONEncoder().encode(user)) # print(" #### RetObject = ", RetObject) return True, RetObject if( "class_id" in diction.keys() and diction['class_id']): qry = {"partner_owner_recid":str(my_partner['recid']), 'valide':'1', 'locked':'0', '_id':ObjectId(str(diction['class_id']))} elif ( "class_internal_url" in diction.keys() and diction['class_internal_url'] ): qry = {"partner_owner_recid": str(my_partner['recid']), 'valide': '1', 'locked': '0', 'internal_url': str(diction['class_internal_url'])} for New_retVal in MYSY_GV.dbname['myclass'].find(qry).sort([("_id", pymongo.DESCENDING), ]): if( "list_unite_enseignement" in New_retVal.keys() ): for local_val in New_retVal['list_unite_enseignement'] : user = local_val user['id'] = str(val_tmp) val_tmp = val_tmp + 1 user['class_id'] = str(New_retVal['_id']) user['internal_url'] = str(New_retVal['internal_url']) """ Recuperation des données associées à cette UE """ ue_data = MYSY_GV.dbname['unite_enseignement'].find_one({'partner_owner_recid':str(my_partner['recid']), 'valide':'1', 'locked':'0', '_id':ObjectId(str(local_val['_id']))}) if( ue_data ): for val in ue_data.keys() : user[str(val)] = ue_data[str(val)] if (str(ue_data['duration_unite']) == "heure"): user['duration_concat'] = str(ue_data['duration']) + " h" elif (str(ue_data['duration_unite']) == "jour"): user['duration_concat'] = str(ue_data['duration']) + " j" elif (str(ue_data['duration_unite']) == "semaine"): user['duration_concat'] = str(ue_data['duration']) + " s" elif (str(ue_data['duration_unite']) == "mois"): user['duration_concat'] = str(ue_data['duration']) + " m" elif (str(ue_data['duration_unite']) == "annee"): user['duration_concat'] = str(ue_data['duration']) + " a" else: user['duration_concat'] = str(ue_data['duration']) + " ?" RetObject.append(mycommon.JSONEncoder().encode(user)) #print(" #### RetObject = ", RetObject) return True, RetObject except Exception as e: exc_type, exc_obj, exc_tb = sys.exc_info() mycommon.myprint(str(inspect.stack()[0][3]) + " -" + str(e) + " - Line : " + str(exc_tb.tb_lineno)) return False, " Impossible de récupérer la liste des unites d'enseignement de la formation " """ Cette fonction permet de recupérer les données d'une UE dans une formation données c'est a dire les detail : credit, is_noted, etc """ def Get_Givent_Unite_Enseignement_Data_Of_Given_Class(diction): try: diction = mycommon.strip_dictionary(diction) """ Verification des input acceptés """ field_list = ['token', 'class_id', 'ue_id'] incom_keys = diction.keys() for val in incom_keys: if val not in field_list and val.startswith('my_') is False: mycommon.myprint(str( inspect.stack()[0][3]) + " Le champ '" + val + "' n'est pas autorisé") return False, " Les informations fournies sont incorrectes" """ Verification des champs obligatoires """ field_list_obligatoire = ['token', 'class_id', 'ue_id'] for val in field_list_obligatoire: if val not in diction: mycommon.myprint( str(inspect.stack()[0][3]) + " - La valeur '" + val + "' n'est pas presente dans liste ") return False, " Les informations fournies sont incorrectes" """ Verification de l'identité et autorisation de l'entité qui appelle cette API """ token = "" if ("token" in diction.keys()): if diction['token']: token = diction['token'] local_status, my_partner = mycommon.Check_Connexion_And_Return_Partner_Data(diction) if (local_status is not True): return local_status, my_partner RetObject = [] val_tmp = 0 qry = {"partner_owner_recid":str(my_partner['recid']), 'valide':'1', 'locked':'0', '_id':ObjectId(str(diction['class_id'])), 'list_unite_enseignement._id':str(my_partner['ue_id'])} print(" ### qry 0147 = ", qry) for New_retVal in MYSY_GV.dbname['myclass'].find(qry).sort([("_id", pymongo.DESCENDING), ]): if( "list_unite_enseignement" in New_retVal.keys() ): for local_val in New_retVal['list_unite_enseignement'] : if( "_id" in local_val.keys() and local_val['_id'] == str(my_partner['ue_id']) ): user = local_val user['id'] = str(val_tmp) val_tmp = val_tmp + 1 """ Recuperation des données associées à cette UE """ ue_data = MYSY_GV.dbname['unite_enseignement'].find_one({'partner_owner_recid':str(my_partner['recid']), 'valide':'1', 'locked':'0', '_id':ObjectId(str(local_val['_id']))}) if( ue_data ): for val in ue_data.keys() : user[str(val)] = ue_data[str(val)] if (str(ue_data['duration_unite']) == "heure"): user['duration_concat'] = str(ue_data['duration']) + " h" elif (str(ue_data['duration_unite']) == "jour"): user['duration_concat'] = str(ue_data['duration']) + " j" elif (str(ue_data['duration_unite']) == "semaine"): user['duration_concat'] = str(ue_data['duration']) + " s" elif (str(ue_data['duration_unite']) == "mois"): user['duration_concat'] = str(ue_data['duration']) + " m" elif (str(ue_data['duration_unite']) == "annee"): user['duration_concat'] = str(ue_data['duration']) + " a" else: user['duration_concat'] = str(ue_data['duration']) + " ?" RetObject.append(mycommon.JSONEncoder().encode(user)) #print(" #### RetObject = ", RetObject) return True, RetObject except Exception as e: exc_type, exc_obj, exc_tb = sys.exc_info() mycommon.myprint(str(inspect.stack()[0][3]) + " -" + str(e) + " - Line : " + str(exc_tb.tb_lineno)) return False, " Impossible de récupérer les données de unites d'enseignement de la formation "