From 159db50add0a32b2cb33013aab03240e5dbf9f27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9o=20Baras?= Date: Tue, 30 Mar 2021 07:56:41 +0200 Subject: [PATCH] =?UTF-8?q?am=C3=A9lioratios=20diverses?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- python/export.py | 75 ++++------ python/modeles.py | 17 +++ python/pn/modele_ressource.tex | 37 +++++ python/pn/saes.yml | 4 + python/ressource.py | 245 ++++++++++++++++++++++++++++++--- 5 files changed, 309 insertions(+), 69 deletions(-) create mode 100644 python/modeles.py create mode 100644 python/pn/modele_ressource.tex diff --git a/python/export.py b/python/export.py index 8e93157..80f329f 100644 --- a/python/export.py +++ b/python/export.py @@ -130,70 +130,42 @@ print(f"{nbre_ressources} ressources") ressources = {"S1" : [], "S2": []} for r in liste_ressources: + # Nettoie titre + nettoie_titre(r) + # Nettoie le champ heures_encadrees - if r.heures_encadrees: - volumes = nettoie_heure(r.heures_encadrees) - if r.tp: - r.tp = nettoie_heure(r.tp) - if isinstance(volumes, int): - r.heures_encadrees = volumes - elif isinstance(volumes, tuple): - r.heures_encadrees = volumes[0] - if not r.tp: - r.tp = volumes[1] - elif r.tp != volumes[1]: - __LOGGER.warning(r"Dans {r.nom}, pb dans les heures tp/td") - else: - r.heures_encadrees = None + nettoie_heure(r) # Nettoie les codes - if r.code: - r.code = nettoie_code(r.code) - if not r.code: # Recherche le code dans les ressources - code_devine = get_code_from_nom(r) - if code_devine: - __LOGGER.warning(f"Dans \"{r.nom}\", remplace le code par {code_devine}") - r.code = code_devine + nettoie_code(r) # Nettoie les semestres - if r.semestre: - if "1" in r.semestre: - r.semestre = "S1" - elif "2" in r.semestre: - r.semestre = "S2" - else: - __LOGGER.warning(f"Dans \"{r.nom}, PAS de semestre => rattaché au S2") - r.semestre = "S2" - else: - __LOGGER.warning(f"Dans \"{r.nom}, PAS de semestre => rattaché au S2") - r.semestre = "S2" - # Remet en forme le titre - if r.code: - if supprime_accent_espace(r.nom) != supprime_accent_espace(DATA_RESSOURCES[r.semestre][r.code]): - __LOGGER.warning(f"Dans \"{r.nom}\", pb dans le nom de la ressource : devient " + DATA_RESSOURCES[r.semestre][r.code]) - r.nom = DATA_RESSOURCES[r.semestre][r.code] - - + nettoie_semestre(r) # Remet en forme les ACs - acs = r.apprentissages - if len(acs) != 3: - __LOGGER.warning(f"Problème dans le nombre de compétences de {r.nom}") - for comp in range(3): - donnees = acs[comp] # chaine de caractères listant les ACS - # donnees = donnees.replace("\t", "").replace("-", "") # supprime les tabulations - acs_avec_code = devine_acs_by_code(donnees) - acs_avec_nom = devine_acs_by_nom(donnees) - acs_finaux = sorted(list(set(acs_avec_code + acs_avec_nom))) - r.apprentissages[comp] = acs_finaux + nettoie_acs(r) + + # Remet en forme les pré-requis + nettoie_prerequis(r) + + # Remet en forme le descriptif + nettoie_description(r) # Tri dans le bon semestre ressources[r.semestre] += [r] +# complète les codes d'après les numéros +for sem in ressources: + for (i, r) in enumerate(ressources[sem]): + if not r.code: + if i == 0: + r.code = "R" + sem[1] + "01" + elif ressources[sem][i-1].code: + r.code = "R" + sem[1] + "{:02d}".format(int(ressources[sem][i-1].code[-2:])+1) + # ************************************************************************ # Affichages divers - # Bilan des heures & Calcul somme des heures par semestre ligne = "{:20s} | {:75s} | {:10s} | {:10s} |" trait = "-"*len(ligne.format("", "", "", "")) @@ -247,3 +219,6 @@ for sem in ressources: valeurs = tuple(valeurs) print(ligne.format(*valeurs)) +for sem in ressources: + for r in ressources[sem]: + print(r.to_latex()) \ No newline at end of file diff --git a/python/modeles.py b/python/modeles.py new file mode 100644 index 0000000..70ca6d4 --- /dev/null +++ b/python/modeles.py @@ -0,0 +1,17 @@ +""" +Gestion des modeles de question/reponses latex +""" +import string, os + +def get_modele(fichier): + # print(os.path.abspath(os.curdir)) + with open(fichier, "r", encoding="utf8") as fid: + contenu = fid.read() + + return contenu + +def effify(non_f_str: str): + return eval(f'f"""{non_f_str}"""') + +class TemplateLatex(string.Template): + delimiter = '#' diff --git a/python/pn/modele_ressource.tex b/python/pn/modele_ressource.tex new file mode 100644 index 0000000..abf7ef9 --- /dev/null +++ b/python/pn/modele_ressource.tex @@ -0,0 +1,37 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Ressources +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\nouvelleressource{#code}{#nom} + +\ajoutheures{#heures_formation}{#heures_tp} + +%% Les compétences et les ACs +\ajoutcompetence{RT1-Administrer}{\niveauA} + +#compRT1 + +\ajoutcompetence{RT2-Connecter}{\niveauA} + +#compRT2 + +\ajoutcompetence{RT3-Programmer}{\niveauA} + +% Les SAE +#saes + +% Les pre-requis +#prerequis + +% Le descriptif +\ajoutancrage{ +#contexte +} + +% Contenus +\ajoutcontenudetaille{ +#contenu +} + +% Mots-clés +\ajoutmotscles{#motscles} diff --git a/python/pn/saes.yml b/python/pn/saes.yml index a2910aa..8ed99f4 100644 --- a/python/pn/saes.yml +++ b/python/pn/saes.yml @@ -6,3 +6,7 @@ S1: SAE15: "Traiter des données" S2: SAE21: "" + SAE22: "" + SAE23: "" + SAE24: "" + diff --git a/python/ressource.py b/python/ressource.py index 073341b..8493d6b 100644 --- a/python/ressource.py +++ b/python/ressource.py @@ -3,6 +3,7 @@ import logging import re import yaml import unicodedata +from modeles import * __LOGGER = logging.getLogger(__name__) @@ -12,6 +13,8 @@ with open("pn/ressources.yml", 'r', encoding="utf8") as fid: # Récupère les données officielles des ACs with open("pn/acs.yml", 'r', encoding="utf8") as fid: DATA_ACS = yaml.load(fid.read(), Loader=yaml.Loader) +with open("pn/saes.yml", 'r', encoding="utf8") as fid: + DATA_SAES = yaml.load(fid.read(), Loader=yaml.Loader) class Ressource(): """Classe modélisant les ressources""" @@ -27,6 +30,8 @@ class Ressource(): self.sae = sae self.prerequis = prerequis self.description = description + self.contexte = None + self.contenu = None self.mots = mots def charge_ac(self, apprentissages): @@ -35,36 +40,154 @@ class Ressource(): def __str__(self): print(self.nom + " " + self.code) + def to_latex(self): + contenu = get_modele("pn/modele_ressource.tex") -def nettoie_heure(champ): + ajoutac = "\\ajoutac{%s}{%s}" + compRT = [] + for i in range(len(self.apprentissages)): + comps = [] + for ac in self.apprentissages[i]: + comps.append( ajoutac % (ac, DATA_ACS["RT"+str(i+1)][ac]) ) + compRT.append("\n".join(comps)) + + # ajoutsaes = "\\ajoutsae{%s}{%s}" + # compRT = [] + # for i in range(len(self.apprentissages)): + # comps = [] + # for ac in self.apprentissages[i]: + # code = self.apprentissages[i] + # comps.append(ajoutac % (code, DATA_ACS["RT" + str(i + 1)][code])) + # compRT.append("\n".join(comps)) + + ajoutprerequis = "\\ajoutprerequis{%s}{%s}" + prerequis = "" + + chaine = TemplateLatex(contenu).substitute(code=self.code, + nom=self.nom, + heures_formation=str(self.heures_encadrees) if self.heures_encadrees else "???", + heures_tp=str(self.tp) if self.tp else "???", + compRT1=compRT[0], + compRT2=compRT[1], + compRT3=compRT[2], + saes="", + prerequis=prerequis, + contexte=self.contexte, + contenu=self.contenu, + motscles=self.mots + ) + return chaine + + +def nettoie_heure(r): """Nettoie le champ (horaire) (de la forme 46h ou 33...) pour en extraire la valeur numérique : le champ peut contenir 2 volumes (heures formation puis heures tp), auquel cas les 2 valeurs sont renvoyées dans un tuple""" - try: # champ contenant uniquement un nbre d'heure - volumes = int(champ) - return volumes - except: - volumes = re.findall("(\d{2}\D|\d{1}\D)", champ) - if len(volumes) == 1: - return int(volumes[0][:-1]) - elif len(volumes) == 2: - volumes = sorted(volumes, reverse=True) - return (int(volumes[0][:-1]), int(volumes[1][:-1])) + def nettoie_champ_heure(champ): + try: # champ contenant uniquement un nbre d'heure + volumes = int(champ) + return volumes + except: + volumes = re.findall("(\d{2}\D|\d{1}\D)", champ) + if len(volumes) == 1: + return int(volumes[0][:-1]) + elif len(volumes) == 2: + volumes = sorted(volumes, reverse=True) + return (int(volumes[0][:-1]), int(volumes[1][:-1])) + + if r.heures_encadrees: # si les heures encadrées sont renseignées + volumes = nettoie_champ_heure(r.heures_encadrees) + if r.tp: + r.tp = nettoie_champ_heure(r.tp) + if isinstance(volumes, int): + r.heures_encadrees = volumes + elif isinstance(volumes, tuple): + r.heures_encadrees = volumes[0] + if not r.tp: + r.tp = volumes[1] + elif r.tp != volumes[1]: + __LOGGER.warning(r"nettoie_heure: ans {r.nom}, pb dans les heures tp/td") + else: + r.heures_encadrees = None #else: #__LOGGER.warning("Heures non détectées") -def nettoie_code(champ): +def nettoie_code(r): """Recherche les codes ressources de la forme RXXX dans champ""" - codes = re.findall(r"(R[0-9][0-9][0-9])", champ) - # if len(codes) > 1: - # __LOGGER.warning("plusieurs codes trouvés :(") - #elif len(codes) == 0: - # __LOGGER.warning("code manquant") - if len(codes) == 1: - return codes[0] + champ = r.code + if r.code: + codes = re.findall(r"(R[0-9][0-9][0-9])", champ) + # if len(codes) > 1: + # __LOGGER.warning("plusieurs codes trouvés :(") + #elif len(codes) == 0: + # __LOGGER.warning("code manquant") + if len(codes) == 1: + r.code = codes[0] + else: + code_devine = get_code_from_nom(r) + if code_devine: + __LOGGER.warning(f"nettoie_code : \"{r.nom}\" => code {code_devine}") + r.code = code_devine + else: + r.code = None + __LOGGER.warning(f"nettoie_code : \"{r.nom}\" => code manquant") +def nettoie_semestre(r): + """Nettoie les semestres : semestre 1 => "S1", semestre 2 => "S2" """ + if r.semestre: + if "1" in r.semestre: + r.semestre = "S1" + elif "2" in r.semestre: + r.semestre = "S2" + else: + __LOGGER.warning(f"nettoie_semestre : dans \"{r.nom}, PAS de semestre => rattaché au S2") + r.semestre = "S2" + else: + __LOGGER.warning(f"nettoie_semestre : dans \"{r.nom}, PAS de semestre => rattaché au S2") + r.semestre = "S2" + +def nettoie_titre(r): + """Nettoie le titre en utilisant les titres officiels""" + def devine_nom_from_ressources(champ): + champ_purge = supprime_accent_espace(champ) + for sem in DATA_RESSOURCES: + for code in DATA_RESSOURCES[sem]: + nom_purge = supprime_accent_espace(DATA_RESSOURCES[sem][code]) + if champ_purge.startswith(nom_purge): + return DATA_RESSOURCES[sem][code] # le bon nom + + old = r.nom + titre = devine_nom_from_ressources(r.nom) + if titre and titre != old: + __LOGGER.warning(f"nettoie_titre : {old} => titre \"{titre}\"") + r.nom = titre + +def nettoie_acs(r): + """Nettoie les acs d'une ressource en les remplaçant par leur code pour les 3 compétences""" + if len(r.apprentissages) != 3: + __LOGGER.warning(f"nettoie_acs : Problème dans le nombre de compétences de {r.nom}") + for comp in range(3): + donnees = r.apprentissages[comp] # chaine de caractères listant les ACS + # donnees = donnees.replace("\t", "").replace("-", "") # supprime les tabulations + acs_avec_code = devine_acs_by_code(donnees) + acs_avec_nom = devine_acs_by_nom(donnees) + acs_finaux = acs_avec_code + acs_avec_nom + acs_finaux = [ac.replace(" ", "") for ac in acs_finaux] + acs_finaux = sorted(list(set(acs_finaux))) + r.apprentissages[comp] = acs_finaux + +def nettoie_prerequis(r): + """Nettoie les prérequis (ressource) en les remplaçant par leur code de ressource""" + R_avec_code = devine_ressources_by_code(r.prerequis) + R_avec_nom = devine_ressources_by_code(r.prerequis) + R_finaux = sorted(list(set(R_avec_code + R_avec_nom))) + if R_finaux: + r.prerequis = R_finaux + else: + r.prerequis = "Aucun" def supprime_accent_espace(chaine): + """Met en minuscule, supprime les accents, les ponctuations et les espaces""" purge = chaine.lower().replace("'", "").replace("’", "") purge = unicodedata.normalize('NFD', purge).encode('ascii', 'ignore').decode('ascii') purge = purge.replace(" ", "") @@ -72,6 +195,8 @@ def supprime_accent_espace(chaine): def get_code_from_nom(ressource): + """Récupère le code d'une ressource d'après son nom en utilisant les noms officiels + des ressources du yaml""" nom = supprime_accent_espace(ressource.nom) for sem in DATA_RESSOURCES: for code in DATA_RESSOURCES[sem]: @@ -103,6 +228,88 @@ def devine_acs_by_nom(donnees): acs += [code] return sorted(list(set(acs))) +def devine_ressources_by_code(champ): + """Recherche les codes ressources de la forme RXXX dans champ ; + """ + codes = re.findall(r"(R\d{3}\D)", champ) # de code à 3 chiffres + return sorted(list(set(codes))) + +def devine_ressources_by_nom(donnees): + """Partant d'une chaine de caractères, détermine les ressources + présentes dans la donnée, en utilisant les infos officielles de + ressources.yml""" + donnees_purge = supprime_accent_espace(donnees) + codes = [] + for sem in DATA_RESSOURCES: + for code in DATA_RESSOURCES[sem]: + nom_purge = supprime_accent_espace(DATA_RESSOURCES[sem][code]) + if nom_purge in donnees_purge: + codes += [code] + return sorted(list(set(codes))) + +def nettoie_description(r): + champs = r.description.split("\n") + champs = [c for c in champs if c] # supprime les lignes vides + + indicea = 0 # la ligne mentionnant le contexte + if True in [ligne.startswith("Contexte et ") for ligne in champs]: # la ligne commençant par Contenus + indicea = [ligne.startswith("Contexte et ") for ligne in champs].index(True) + + indicec = 0 + if True in [ligne.startswith("Contenus") for ligne in champs]: # la ligne commençant par Contenus + indicec = [ligne.startswith("Contenus") for ligne in champs].index(True) + if indicea>0: + contexte = "\n".join(champs[indicea:indicec]) + else: + contexte = "\n".join(champs[:indicec]) + + contenus = champs[indicec+1:] + # suppression des \t + contenus = [ligne.rstrip().replace("--", "-") for ligne in contenus] + contenus = [c for c in contenus if c] # supprime les lignes vides + marqueurs = [] + for ligne in contenus: + m = re.search(r"(\t)*", ligne) # dès \t ? + if m.group() != "": + ajout = m.group() + else: + ajout = "" + ligne = ligne.replace("\t","")[0] + if ligne[0] not in string.ascii_letters and ligne[0] != "É": + marqueurs += [ajout + ligne[0]] + + def has_digits(liste): + return sum([m in string.digits for m in liste])>0 + + a_marqueur_numerique = has_digits(marqueurs) # des marqueurs numériques ? + marqueurs_finaux = [] # tri les marqueurs + for m in marqueurs: + if m not in string.digits and m not in marqueurs_finaux: + marqueurs_finaux.append(m) + elif m not in marqueurs_finaux: + if a_marqueur_numerique: + if not has_digits(marqueurs_finaux): + marqueurs_finaux.append(m) + + contenus_fin = contenus[:] # copie des ligne + for (i, ligne) in enumerate(contenus): + if ligne[0] in string.digits: + pos = marqueurs_finaux.index("1") + if ligne[1:].startswith("/"): + contenus_fin[i] = "\t" * pos + "* " + ligne[2:].replace("\t", "").rstrip() + else: + contenus_fin[i] = "\t" * pos + "* " + ligne[1:].replace("\t", "").rstrip() # la ligne avec marqueur supprimé et / supprimé + else: + if ligne[0] in marqueurs_finaux: + pos = marqueurs_finaux.index(ligne[0]) + contenus_fin[i] = "\t" * pos + "* " + ligne[1:].replace("\t", "").rstrip() + + contenu = "\n".join(contenus_fin) + + r.contexte = contexte + r.contenu = contenu + print(r.nom, contexte, contenu, sep="\n") + if __name__=="__main__": # Eléments de test for sem in DATA_RESSOURCES: