Beispiel #1
0
 def page_impression(self, qui):
     """ Fabrication de la page 'impression des fiches bilan' """
     cand = qui.get_cand()
     fich = qui.fichier
     entete = '<h1 align="center" class="titre">{} - {}.</h1>'.format(
         self.titre,
         fich.filiere().upper())
     txt = ''
     saut = '<div style = "page-break-after: always;"></div>'  # on saute une page entre chaque candidat
     for cand in fich:
         nb = nb_classes[fich.filiere().lower(
         )]  # récupération du contenu du fichier config.py
         # Si nb n'est pas convertible en un entier positif alors on classe tous les candidats
         try:
             nb_max = int(nb)
             if nb_max < 0: nb_max = len(fich)
         except:
             nb_max = len(fich)
         a = (Fichier.get(cand, 'traité') == 'oui')
         b = (Fichier.get(cand, 'Correction') != 'NC')
         c = not (b) or (int(Fichier.get(cand, 'Rang final')) <= nb_max)
         if a and b and c:  # seulement les classés dont le rang est inférieur à la limite fixée
             txt += entete
             txt += '<div class = encadre>Candidat classé : {}</div>'.format(
                 Fichier.get(cand, 'Rang final'))
             txt += Composeur.html['contenu_dossier'].format(
                 **self.genere_dossier(qui, cand))
             txt += Composeur.html['contenu_action'].format(
                 **self.genere_action(qui, cand))
             txt += saut
     txt = txt[:-len(
         saut
     )]  # On enlève le dernier saut de page... sinon on a une page blanche !
     return Composeur.html['page_impress'].format(**{'pages': txt})
Beispiel #2
0
 def genere_action(self, client, cand):
     """ Renvoie le dictionnaire de la partie 'action' de la page HTML"""
     ###
     # Estimation du rang final du candidat
     rg_fin = ''
     visib = 'none'  # n'est visible que pour les jurys
     if isinstance(client, Jury):  # seulement pour les jurys.
         rg_fin = client.get_rgfinal(cand)
         visib = ''  # est visible pour les jurys
     rang_final = '<td style = "display:{};">Estimation du rang final : {}</td>'.format(
         visib, rg_fin)
     ### Partie correction :
     # récupération correction
     cor = Fichier.get(cand,
                       'Correction')  # Sous forme de chaîne de caractère !
     if cor == 'NC':
         correc = min_correc  # NC correspond à la correction minimale
         rg_fin = 'NC'
     else:
         correc = float(
             cor.replace(',', '.')
         )  # valeur numérique de la correction -> placement du curseur.
     # Construction de la barre de correction qu'on alimente avec les infos courantes..
     barre = Composeur.barre.format(correc, Fichier.get(cand, 'Jury'), cor)
     ### Partie motivations :
     motifs = Composeur.motifs.format(Fichier.get(cand, 'Motifs'))
     # On met tout ça dans un dico data pour passage en argument à html['contenu_action']
     data = {
         'barre': barre,
         'scoreb': Fichier.get(cand, 'Score brut'),
         'scoref': Fichier.get(cand, 'Score final'),
         'rg_fin': rang_final,
         'motifs': motifs
     }
     return data
Beispiel #3
0
    def stat(self):
        """ Effectue des statistiques sur les candidats """
        # Récupère la liste des fichiers concernés
        list_fichiers = [Fichier(fich) for fich in glob.glob(os.path.join(os.curdir, "data", "admin_*.xml"))]
        # On ordonne la liste de fichiers transmise selon l'ordre spécifié dans filieres (parametres.py)
        list_fich = sorted(list_fichiers, key = lambda f: filieres.index(f.filiere().lower()))
        # L'info de candidatures est stockée dans un mot binaire où 1 bit 
        # correspond à 1 filière. Un dictionnaire 'candid' admet ces mots binaires pour clés,
        # et les valeurs sont des nombres de candidats. 
        # candid = {'001' : 609, '011' : 245, ...} indique que 609 candidats ont demandé
        # la filière 1 et 245 ont demandé à la fois la filière 1 et la filière 2

        # Initialisation du dictionnaire stockant toutes les candidatures
        candid = {i : 0 for i in range(2**len(filieres))}
        # Variables de décompte des candidats (et pas candidatures !)
        candidats = 0
        candidats_ayant_valide = 0
        # Recherche des candidatures # je suis très fier de cet algorithme !!
        # Construction des éléments de recherche
        l_dict = [ {Fichier.get(cand, 'Num ParcoursSup') : cand for cand in fich} for fich in list_fich ] # liste de dicos
        l_set = [ set(d.keys()) for d in l_dict ] # list d'ensembles (set()) d'identifiants ParcoursSup
        # Création des statistiques
        for (k,n) in enumerate(l_set): # k = index filière ; n = ensemble des identifiants des candidats dans la filière
            while len(n) > 0: # tant qu'il reste des identifiants dans n
                a = n.pop() # on en prélève 1 (et il disparait de n)
                candidats += 1
                cc, liste = 2**k, [k] # filière k : bit de poids 2**k au niveau haut.
                for i in range(k+1, len(list_fich)): # on cherche cet identifiant dans les autres filières.
                    if a in l_set[i]: # s'il y est :
                        cc |= 2**i # on met le bit 2**i au niveau haut (un ou exclusif est parfait) 
                        l_set[i].remove(a) # on supprime cet identifiant de l'ensemble des identifiants de la filière i
                        liste.append(i) # on ajoute la filière i à la liste des filières demandées par le candidat
                [Fichier.set(l_dict[j][a], 'Candidatures', cc) for j in liste] # On écrit le noeud 'Candidatures'
                flag = True # pour ne compter qu'une validation par candidat !
                for j in liste: # le test ci-dessous pourrait exclure les filières inadéquates (bien ou pas ?)..
                    if not('non validée' in Fichier.get(l_dict[j][a], 'Motifs')):
                        candid[2**j]+= 1 # ne sont comptés que les candidatures validées
                        if flag:
                            candidats_ayant_valide += 1
                            flag = False
                if len(liste) > 1: # si candidat dans plus d'une filière
                    candid[cc] += 1 # incrémentation du compteur correspondant
        # Sauvegarder
        [fich.sauvegarde() for fich in list_fich]
        # Ajouter deux éléments dans le dictionnaire candid
        candid['nb_cand'] = candidats
        candid['nb_cand_valid'] = candidats_ayant_valide
        # Écrire le fichier stat
        with open(os.path.join(os.curdir, "data", "stat"), 'wb') as stat_fich:
            pickle.dump(candid, stat_fich)
        return
Beispiel #4
0
 def get_rgfinal(self, cand):
     """ Renvoie une estimation du rg final d'un candidat """
     # On extrait du fichier les dossiers traités et non NC
     doss = [ca for ca in self.fichier if (Fichier.get(ca, 'traité') == 'oui' and Fichier.get(ca, 'Correction') != 'NC')]
     # On classes ceux-ci par ordre de score final décroissant
     doss[:] = sorted(doss, key = lambda cand: -float(Fichier.get(cand, 'Score final').replace(',','.')))
     # On calcule le rang du score_final actuel (celui de cand) dans cette liste
     rg = Fichier.rang(cand, doss, 'Score final')
     # À ce stade, rg est le rang dans la liste du jury. 
     # La suite consiste à calculer n*(rg-1) + k
     # où n est le nombre de jurys pour cette filière et k l'indice du jury courant.
     q = parse('Jury {:w}{:d}', self._droits) # n et k sont cachés dans les droits du jury !
     n = int(nb_jurys[q[0].lower()])
     k = int(q[1])
     return n*(rg-1)+k
Beispiel #5
0
 def clore_commission(self):
     """ Cloture la commission """
     # Objectif : récolter les fichiers comm en fin de commission, calculer tous les scores finals, classés les 
     # candidats et reconstituer un fichier unique par filière (class_XXX.xml). Enfin, construire des tableaux *.csv 
     # nécessaires à la suite du traitement administratif du recrutement (ces tableaux sont définis dans config.py)
     for fil in filieres: # pour chaque filière
         path = os.path.join(os.curdir, "data", "comm_{}*.xml".format(fil.upper()))
         list_fich = [Fichier(fich) for fich in glob.glob(path)] # récupération des fichiers comm de la filière
         list_fich = sorted(list_fich, key = lambda fich: fich.nom) # l'ordre est important pour la suite
         list_doss = [] # contiendra les dossiers de chaque sous-comm
         # Pour chaque sous-commission
         for fich in list_fich:
             # Les fichiers non vus se voient devenir NC, score final = 0, avec
             # motifs = "Dossier moins bon que le dernier classé" (sauf s'il y a déjà un motif - Admin)
             for c in fich:
                 if Fichier.get(c, 'traité') != 'oui':
                     Fichier.set(c, 'Correction', 'NC')
                     Fichier.set(c, 'Score final', '0')
                     if Fichier.get(c, 'Jury') == 'Auto': # 'Auto' est la valeur par défaut du champ-xml 'Jury' 
                         Fichier.set(c, 'Motifs', 'Dossier moins bon que le dernier classé.')
             # list_doss récupère la liste des dossiers classée selon score_final + age
             list_doss.append(fich.ordonne('score_f'))
         # Ensuite, on entremêle les dossiers de chaque sous-comm
         doss_fin = [] # contiendra les dossiers intercalés comme il se doit..
         if list_doss: # Y a-t-il des dossiers dans cette liste ?
             nb = len(list_doss[0]) # (taille du fichier du 1er jury de cette filière)
             num = 0
             for i in range(nb): # list_doss[0] est le plus grand !!
                 doss_fin.append(list_doss[0][i])
                 for k in range(1, len(list_doss)): # reste-t-il des candidats classés dans les listes suivantes ?
                     if i < len(list_doss[k]): doss_fin.append(list_doss[k][i])
             res = etree.Element('candidats') # Création d'une arborescence xml 'candidats'
             [res.append(c) for c in doss_fin] # qu'on remplit avec les candidats classés.
             # Calcul et renseignement du rang final (index dans res)
             rg = 1
             for cand in res:
                 nu = 'NC'
                 if Fichier.get(cand, 'Correction') != 'NC': # si le candidat est classé
                     nu = str(rg)
                     rg += 1
                 Fichier.set(cand, 'Rang final', nu) # rang final = NC si non classé
             # Sauvegarde du fichier class...
             nom = os.path.join(os.curdir, "data", "class_{}.xml".format(fil.upper()))
             with open(nom, 'wb') as fichier:
                 fichier.write(etree.tostring(res, pretty_print=True, encoding='utf-8'))
     # On lance la génération des tableaux bilan de commission
     list_fich = [Fichier(fich) for fich in glob.glob(os.path.join(os.curdir, "data", "class_*.xml"))]
     self.tableaux_bilan(list_fich)
Beispiel #6
0
 def traiter(self, **kwargs):
     """ Traiter un dossier """
     # Fonction lancée par la fonction "traiter" du Serveur, elle même lancée par un clic sur 'Classé' ou 'NC'
     cand  = self.get_cand() # on récupère le candidat
     # On récupère la correction apportée par le jury
     cor = kwargs['correc']
     cor_prec = Fichier.get(cand, 'Correction') # précédente correction si le jury revient sur un candidat
     # Mise à jour du fichier décomptes 
     # ce fichier a été créé par admin, dans la méthode generation_comm
     a = (Fichier.get(cand, 'traité') == 'oui')
     b = (cor_prec == 'NC')
     c = (float(cor) == float(min_correc))
     if (not(a ^ b ^ c) and not(b and c)): # doit-on changer le nb de candidats classés
         change_decompte = 1
         if c: change_decompte = -1 # -1 si candidat classé qui devient non classé
         with open(os.path.join(os.curdir,"data","decomptes"), 'br') as fich:
             decompt = pickle.load(fich)
         qui = self._droits
         for key in decompt.keys():
             if key in qui:
                 decompt[key] += change_decompte
         with open(os.path.join(os.curdir, "data", "decomptes"), 'wb') as stat_fich:
             pickle.dump(decompt, stat_fich)
     # On met à jour le contenu de ce dossier :
     # tout d'abord, calcul du score final
     if float(cor) == float(min_correc): # cas d'un choix 'NC'
         cor, scoref = 'NC', '0'
     else:
         note = float(Fichier.get(cand, 'Score brut').replace(',','.')) + float(cor)
         scoref = '{:.2f}'.format(note).replace('.',',') # mise en forme pour que les tableaux bilan
         cor = '{:.2f}'.format(float(cor)).replace('.',',') # de commission soient jolis !
     # Écriture des différents champs
     # 1/ correction et score final
     Fichier.set(cand, 'Correction', cor) # écriture de la correction dans le noeud xml du candidat
     Fichier.set(cand, 'Score final', scoref) # écriture du score final dans le noeud xml du candidat
     # 2/ Qui a traité le dossier : écriture du noeud xml adéquat
     Fichier.set(cand, 'Jury', self._droits)
     # 3/ noeud 'traité' : le dossier a été traité (classé ou non)
     Fichier.set(cand, 'traité', 'oui')
     # 4/ motivation du jury
     Fichier.set(cand, 'Motifs', kwargs['motif'])
     ## Fin mise à jour dossier
     # On sélectionne le dossier suivant
     if self.num_doss < len(self.fichier)-1:
         self.num_doss += 1
     # Et on sauvegarde le tout
     self.fichier.sauvegarde() # sauvegarde physique du fichier.
Beispiel #7
0
 def tableaux_bilan(self, list_fich):
     """ Cette fonction créé les tableaux dont a besoin l'admin pour la suite du recrutement """
     # Un peu de ménage...
     dest = os.path.join(os.curdir, "tableaux")
     restaure_virginite(dest)
     # Pour chaque filière :
     for fich in list_fich:
         # Tableaux candidats classés
         for name in tableaux_candidats_classes.keys():
             # Création du fichier csv
             nom = os.path.join(os.curdir, "tableaux", "{}_{}.csv".format(fich.filiere(), name))
             cw = csv.writer(open(nom, 'w'))
             entetes = tableaux_candidats_classes[name]
             cw.writerow(entetes)
             for cand in fich:
                 # On ne met dans ces tableaux que les candidats traités, classés et dont le rang est inférieur à la 
                 # limite prévue dans config.py.
                 nb = nb_classes[fich.filiere().lower()] # récupération du contenu du fichier config.py
                 # Si nb n'est pas convertible en un entier positif alors on classe tous les candidats
                 try:
                     nb_max = int(nb)
                     if nb_max < 0: nb_max = len(fich)
                 except:
                     nb_max = len(fich)
                 a = (Fichier.get(cand, 'traité') == 'oui')
                 b = (Fichier.get(cand, 'Correction') != 'NC')
                 c = not(b) or (int(Fichier.get(cand, 'Rang final')) <= nb_max)
                 if a and b and c:
                     data = [Fichier.get(cand, champ) for champ in entetes]
                     cw.writerow(data)
         # Tableaux tous candidats : ces tableaux-là contiennent tous les candidats.
         for name in tableaux_tous_candidats:
             # Création du fichier csv
             nom = os.path.join(os.curdir, "tableaux", "{}_{}.csv".format(fich.filiere(), name))
             cw = csv.writer(open(nom, 'w'))
             entetes = tableaux_tous_candidats[name]
             cw.writerow(entetes)
             for cand in fich.ordonne('alpha'):
                 data = [Fichier.get(cand, champ) for champ in entetes]
                 cw.writerow(data)
Beispiel #8
0
 def genere_liste(self, client, mem_scroll):
     """ Génère la partie liste de la page HTML"""
     # Construction de la chaine lis : code html de la liste des dossiers.
     lis = '<form id = "form_liste" action = "click_list" method=POST>'
     lis += '<input type="hidden" name = "scroll_mem" value = "{}"/>'.format(mem_scroll)
     for index, cand in enumerate(client.fichier):
         # Les candidats rejetés par admin n'apparaissent pas aux jurys
         a = isinstance(client, Jury)
         b = Fichier.get(cand, 'Correction') == 'NC'
         c = Fichier.get(cand, 'Jury') == 'Admin'
         if not(a and b and c):
             lis += '<input type = "submit" name="num" '
             clas = 'doss'
             if cand == client.get_cand(): # affecte la class css "doss_courant" au dossier courant
                     clas += ' doss_courant' # candidat sélectionné entouré d'un cadre coloré
             if Fichier.get(cand, 'traité'): # affecte la classe css "doss_traite" aux dossiers qui le sont
                     clas += ' doss_traite' # candidat traité = background vert.
             if Fichier.get(cand, 'Correction') == 'NC': # affecte la classe css "doss_rejete" aux dossiers NC
                 clas += ' doss_rejete' # candidat NC = background gris.
             ### dossiers à mettre en évidence (fond rouge) :
             # client Admin ET alerte déposée par nettoie.py
             if isinstance(client, Admin) and '- Alerte' in Fichier.get(cand, 'Motifs'):
                 clas += ' doss_incomplet' # LE TERME INCOMPLET N'EST PLUS ADÉQUAT
             # Si Admin a écrit un commentaire :
             if '- Admin' in Fichier.get(cand, 'Motifs'):
                 if isinstance(client, Admin): # si admin
                     clas += ' doss_commente'
                 if isinstance(client, Jury): # si jury
                     clas += ' doss_incomplet' # LE TERME INCOMPLET N'EST PLUS ADÉQUAT
             ### fin dossiers à mettre en évidence
             lis += 'class = "{}"'.format(clas)
             nom = '{}, {}'.format(Fichier.get(cand, 'Nom'), Fichier.get(cand, 'Prénom'))
             # La variable txt qui suit est le texte du bouton. Attention, ses 3 premiers
             # caractères doivent être le numéro du dossier dans la liste des
             # dossiers (client_get_dossiers())... Cela sert dans click_list(), pour identifier sur qui on a clické..
             # une police monospace est utilisée pour l'esthétique et la largeur affectée au nom est fixée. Ainsi, 
             # les informations de candidatures multiples sont alignées.
             txt = '{:3d}) {: <30}{}'.format(index+1, nom[:29], Fichier.get(cand, 'Candidatures'))
             lis += ' value="{}"></input><br>'.format(txt)
     lis += '-'*7 + ' fin de liste ' + '-'*7
     lis = lis + '</form>'
     return lis
Beispiel #9
0
 def genere_dossier(self, qui, cand, format_admin = False):
     """ Renvoie le dictionnaire contenant les infos du dossier en cours"""
     #### Début
     data = {}
     # Pédigré
     liste_attr = ['Nom', 'Prénom', 'Date de naissance', 'Établissement', 'Département',
             'Pays', 'Num ParcoursSup', 'INE']
     for attr in liste_attr:
         data[attr] = Fichier.get(cand, attr)
     # récup filiere pour connaître le chemin vers le dossier pdf (dans répertoire docs_candidats)
     fil = qui.fichier.filiere()
     data['ref_fich'] = os.path.join('docs_candidats', '{}'.format(fil.lower()),
             'docs_{}'.format(Fichier.get(cand, 'Num ParcoursSup')))
     # Formatage des champs de notes et de classe actuelle en fonction du client (ou de format_comm)
     # En effet, Admin a la possibilité d'écrire dans ces champs alors que Jury est en lecture seule.
     formateur_clas_actu = '{}'
     formateur_note = '{note}'
     activ = 'disabled'
     if format_admin:
         formateur_clas_actu = '<input type="text" id="Classe actuelle" name="Classe actuelle" size="10" value="{}"/>'
         formateur_note = '<input type="text" class="notes grossi" id="{}" name="{}" value="{note}"\
                 onfocusout="verif_saisie()"/>'
         activ = ''
     ### Suite de la création du dictionnaire
     # classe actuelle
     data['Classe actuelle'] = formateur_clas_actu.format(Fichier.get(cand, 'Classe actuelle'))
     # Notes
     matiere = ['Mathématiques', 'Physique/Chimie']
     date = ['trimestre 1', 'trimestre 2', 'trimestre 3']
     classe = ['Première', 'Terminale']
     for cl in classe:
         for mat in matiere:
             for da in date:
                 key = '{} {} {}'.format(mat, cl, da)
                 data[key] = formateur_note.format(key, key, note=Fichier.get(cand, key))
     # Autres notes
     liste = ['Mathématiques CPES', 'Physique/Chimie CPES', 'Écrit EAF', 'Oral EAF']
     for li in liste:
         if not('cpes' in Fichier.get(cand, 'Classe actuelle').lower()) and 'cpes' in li.lower():
             data[li] = formateur_note.format(li, li, note='-')
         else:
             data[li] = formateur_note.format(li, li, note=Fichier.get(cand, li))
     # Suite
     data['cand'] = Fichier.get(cand, 'Candidatures impr')
     return data
Beispiel #10
0
    def genere_dossier(self, qui, cand, format_admin=False):
        """ Renvoie le dictionnaire contenant les infos du dossier en cours"""
        # format_admin = False lorsque la demande est destinée à concevoir les fiches bilan de commission.
        # En effet, cette demande est lancée par Admin, mais l'impression se fait avec un dossier formaté
        # comme pour Jury : les notes de sont pas des <input type="text" .../>. C'est ensuite le code css
        # qui se charge d'adapter l'aspect de la page pour l'impression (fond blanc, barre de correction
        # invisible, remplacée par une indication du jury qui a traité le dossier et la correction faite,
        # liste des dossiers également invisible; vive le css !)
        #### Début
        data = {}
        # Pédigré
        liste_attr = [
            'Nom', 'Prénom', 'Date de naissance', 'Établissement',
            'Département', 'Pays', 'Num ParcoursSup', 'INE'
        ]
        for attr in liste_attr:
            data[attr] = Fichier.get(cand, attr)
        # récup filiere pour connaître le chemin vers le dossier pdf (dans répertoire docs_candidats)
        fil = qui.fichier.filiere()
        data['ref_fich'] = os.path.join(
            'docs_candidats', '{}'.format(fil.lower()),
            'docs_{}'.format(Fichier.get(cand, 'Num ParcoursSup')))
        # Formatage des champs de notes et de classe actuelle en fonction de format_admin
        # En effet, Admin a la possibilité d'écrire dans ces champs alors que Jury est en lecture seule.
        formateur_clas_actu = '{}'
        formateur_note = '{note}'
        activ = 'disabled'
        if format_admin:
            formateur_clas_actu = '<input type="text" id="Classe actuelle" name="Classe actuelle" size="10" value="{}"/>'
            formateur_note = '<input type="text" class="notes grossi" id="{}" name="{}" value="{note}"\
                    onfocusout="verif_saisie()"/>'

            activ = ''
        ### Suite de la création du dictionnaire
        # classe actuelle
        data['Classe actuelle'] = formateur_clas_actu.format(
            Fichier.get(cand, 'Classe actuelle'))
        # Notes
        matiere = ['Mathématiques', 'Physique/Chimie']
        date = ['trimestre 1', 'trimestre 2', 'trimestre 3']
        classe = ['Première', 'Terminale']
        for cl in classe:
            for mat in matiere:
                for da in date:
                    key = '{} {} {}'.format(mat, cl, da)
                    data[key] = formateur_note.format(key,
                                                      key,
                                                      note=Fichier.get(
                                                          cand, key))
        # Autres notes
        liste = [
            'Mathématiques CPES', 'Physique/Chimie CPES', 'Écrit EAF',
            'Oral EAF'
        ]
        for li in liste:
            if not ('cpes' in Fichier.get(
                    cand, 'Classe actuelle').lower()) and 'cpes' in li.lower():
                data[li] = formateur_note.format(li, li, note='-')
            else:
                data[li] = formateur_note.format(li,
                                                 li,
                                                 note=Fichier.get(cand, li))
        # Suite
        data['cand'] = Fichier.get(cand, 'Candidatures impr')
        return data
Beispiel #11
0
    def menu_admin(self, qui, fichiers_utilises, comm_en_cours):
        """ Compose le menu administrateur
        contenu : selon l'état (phase 1, 2 ou 3) du traitement
        phase 1 : avant la commission, l'admin gère ce qui provient de ParcoursSup,
                  commente et/ou complète les dossiers
        phase 2 : l'admin a généré les fichiers *_comm_* destinés à la commission. Les
                  différents jurys doivent se prononcer sur les dossiers. C'est le coeur
                  de l'opération de sélection.
        phase 3 : commission terminée. L'admin doit gérer "l'après sélection" : recomposer
                  un fichier ordonné par filière, générer tous les tableaux récapitulatifs. """
        data = {}
        ## entête
        page = self.genere_entete('{} - Accès {}.'.format(
            self.titre, qui.get_droits()))
        list_fich_comm = glob.glob(
            os.path.join(os.curdir, "data", "comm_*.xml"))
        patron = 'menu_admin_'
        if len(list_fich_comm) > 0:  # phase 2 ou 3
            data['decompt'] = self.genere_liste_decompte()
            data['liste_stat'] = self.genere_liste_stat(qui)
            if comm_en_cours:  # phase 2
                patron += 'pendant'
                txt = ''
                for fich in fichiers_utilises.values():
                    txt += '<input type = "submit" class ="fichier" name = "fichier" value = "{}"/><br>'.format(
                        fich)
                data['liste_jurys'] = txt
            else:  # phase 3
                patron += 'apres'
                # Etape 4 bouton
                data['bout_etap4'] = '<input type = "button" class ="fichier"'
                data[
                    'bout_etap4'] += ' value = "Récolter les fichiers" onclick = "recolt_wait();"/>'
                # Etape 5 bouton et Etape 6
                list_fich_class = glob.glob(
                    os.path.join(os.curdir, "data", "class_*.xml"))
                data['liste_impression'] = ''
                if len(list_fich_class) > 0:
                    data['liste_impression'] = self.genere_liste_impression()

        else:  # avant commission
            patron += 'avant'
            # liste csv
            data['liste_csv'] = self.genere_liste_csv()
            # liste pdf
            data['liste_pdf'] = self.genere_liste_pdf()
            # liste admin
            data['liste_admin'] = self.genere_liste_admin()
            # liste_stat
            data['liste_stat'] = self.genere_liste_stat(qui)
            # Etape 3 bouton : ce bouton n'est actif que si admin a levé toutes les alertes.
            ### Testons s'il reste encore des alertes dans les fichiers admin
            # Récupération des fichiers admin
            list_fich = {
                Fichier(fich)
                for fich in glob.glob(
                    os.path.join(os.curdir, "data", "admin_*.xml"))
            }
            alertes = False
            while not (alertes) and len(
                    list_fich
            ) > 0:  # à la première alerte détectée alertes = True
                fich = list_fich.pop()
                alertes = (True in {
                    '- Alerte :' in Fichier.get(cand, 'Motifs')
                    for cand in fich if Fichier.get(cand, 'Correction') != 'NC'
                })
            ### Suite
            txt = ''
            if len(data['liste_admin']
                   ) > 0:  # si les fichiers admin existent :
                txt = '<input type = "button" class ="fichier" value = "Générer les fichiers commission"'
                affich = ''
                if (alertes):
                    affich = 'disabled'
                txt += 'onclick = "genere_wait();" {}/>'.format(affich)
            data['bout_etap3'] = txt
        # Envoyez le menu
        contenu = Composeur.html[patron].format(**data)
        # Composition de la page
        page += Composeur.html["MEP_MENU"].format(**{
            'contenu': contenu,
            'script': qui.script_menu
        })
        page += '</html>'
        return page
Beispiel #12
0
    def traiter(self, **kwargs):
        """ Traiter un dossier """
        # Fonction lancée par la fonction "traiter" du Serveur, elle même lancée par un clic sur 'Classé' ou 'NC'
        # On récupère le candidat courant
        cand = self.get_cand()
        # Ici, on va répercuter les complétions de l'administrateur dans tous les dossiers que le candidat a déposé.
        # Attention ! le traitement du fichier en cours est fait à part car deux objets 'Fichier' qui
        # auraient le même nom sont malgré tout différents !! On rajoute la bonne instance Fichier juste après.
        # Recherche de tous les fichiers existants (sauf fichier en cours) :
        list_fich_admin = [Fichier(fich) for fich in glob.glob(os.path.join(os.curdir, "data", "admin_*.xml"))\
                if fich != self.fichier.nom]
        # On restreint la liste aux fichiers contenant le candidat en cours
        list_fich_cand = [fich for fich in list_fich_admin if cand in fich]
        # On rajoute le fichier suivi actuellement
        list_fich_cand.append(self.fichier)
        # list_fich_cand contient tous les fichiers dans lesquels le candidat courant se trouve.
        #
        ############### Admin a-t-il changé qqc ? Si oui, mise à jour. 
        # Classe actuelle ?
        if Fichier.get(cand, 'Classe actuelle') != kwargs['Classe actuelle']:
            for fich in list_fich_cand: Fichier.set(fich.get_cand(cand), 'Classe actuelle', kwargs['Classe actuelle'])
        # Cas des notes
        matiere = ['Mathématiques', 'Physique/Chimie']
        date = ['trimestre 1', 'trimestre 2', 'trimestre 3']
        classe = ['Première', 'Terminale']
        for cl in classe:
            for mat in matiere:
                for da in date:
                    key = '{} {} {}'.format(mat, cl, da)
                    if Fichier.get(cand, key) != kwargs[key]: # la note a-t-elle été modifiée ?
                        for fich in list_fich_cand: Fichier.set(fich.get_cand(cand), key, kwargs[key])
        # CPES et EAF
        #liste = ['Mathématiques CPES', 'Physique/Chimie CPES', 'Écrit EAF', 'Oral EAF']
        # Seulement EAF depuis 2020
        liste = ['Écrit EAF', 'Oral EAF']
        for li in liste:
            if 'cpes' in li.lower():
                if ('cpes' in Fichier.get(cand, 'Classe actuelle').lower()) and Fichier.get(cand, li) != kwargs[li]:
                    for fich in list_fich_cand: Fichier.set(fich.get_cand(cand), li, kwargs[li])
            else:
                if Fichier.get(cand, li) != kwargs[li]:
                    for fich in list_fich_cand: Fichier.set(fich.get_cand(cand), li, kwargs[li])
        # Commentaire éventuel admin + gestion des 'NC'
        # Les commentaires admin sont précédés de '- Admin :' c'est à cela qu'on les reconnaît. Et le jury les
        # verra sur fond rouge dans la liste de ses dossiers.
        # Par ailleurs, dossiers_jury.js exclut qu'un tel commentaire soit considéré comme une motivation de jury.
        motif = kwargs['motif']
        if not('- Admin :' in motif or motif == '' or '- Alerte :' in motif):
            motif = '- Admin : {}'.format(motif)
        # Récupération de la correction. On en fait qqc seulement si elle est minimale (NC)
        cor = kwargs['correc'] # récupération de la correction et calcul du score final
        if float(cor) == float(min_correc):
            # L'admin a validé le formulaire avec la correction NC (le candidat ne passera pas en commission)
            # Pour ce cas là, on ne recopie pas dans toutes les filières. Admin peut exclure une candidature
            # dans une filière sans l'exclure des autres. Sécurité !
            Fichier.set(cand, 'Correction', 'NC') # la fonction calcul_scoreb renverra 0 !
            Fichier.set(cand, 'Jury', 'Admin') # Cette exclusion est un choix de l'admin (apparaît dans les tableaux)
            Fichier.set(cand, 'Motifs', motif)
        else:
            Fichier.set(cand, 'Correction', '0') # 2 lignes nécessaires si l'admin a NC un candidat, puis a changé d'avis.
            Fichier.set(cand, 'Jury', '')
            for fich in list_fich_cand:
                Fichier.set(fich.get_cand(cand), 'Motifs', motif)

        # On (re)calcule le score brut !
        Fichier.calcul_scoreb(cand)
        # On sauvegarde tous les fichiers retouchés
        for fich in list_fich_cand:
            fich.sauvegarde()