# -*- mode: python -*- # -*- coding: utf-8 -*- ############################################################################## # # Gestion scolarite IUT # # Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Emmanuel Viennet emmanuel.viennet@viennet.net # ############################################################################## """Form. pour inscription rapide des etudiants d'un semestre dans un autre Utilise les autorisations d'inscription délivrées en jury. """ import datetime from operator import itemgetter from flask import url_for, g, request import app.scodoc.notesdb as ndb import app.scodoc.sco_utils as scu from app import log from app.models import Formation, FormSemestre from app.scodoc.gen_tables import GenTable from app.scodoc import html_sco_header from app.scodoc import sco_cache from app.scodoc import codes_cursus from app.scodoc import sco_etud from app.scodoc import sco_formations from app.scodoc import sco_formsemestre from app.scodoc import sco_formsemestre_inscriptions from app.scodoc import sco_groups from app.scodoc import sco_preferences from app.scodoc import sco_dict_pv_jury from app.scodoc.sco_exceptions import ScoValueError def list_authorized_etuds_by_sem(sem, delai=274, ignore_jury=False): """Liste des etudiants autorisés à s'inscrire dans sem. delai = nb de jours max entre la date de l'autorisation et celle de debut du semestre cible. ignore_jury: si vrai, considère tous les étudiants comme autorisés, même s'ils n'ont pas de décision de jury. """ src_sems = list_source_sems(sem, delai=delai) inscrits = list_inscrits(sem["formsemestre_id"]) r = {} candidats = {} # etudid : etud (tous les etudiants candidats) nb = 0 # debug for src in src_sems: if ignore_jury: # liste de tous les inscrits au semestre (sans dems) liste = list_inscrits(src["formsemestre_id"]).values() else: # liste des étudiants autorisés par le jury à s'inscrire ici liste = list_etuds_from_sem(src, sem) liste_filtree = [] for e in liste: # Filtre ceux qui se sont déjà inscrit dans un semestre APRES le semestre src auth_used = False # autorisation deja utilisée ? etud = sco_etud.get_etud_info(etudid=e["etudid"], filled=True)[0] for isem in etud["sems"]: if ndb.DateDMYtoISO(isem["date_debut"]) >= ndb.DateDMYtoISO( src["date_fin"] ): auth_used = True if not auth_used: candidats[e["etudid"]] = etud liste_filtree.append(e) nb += 1 r[src["formsemestre_id"]] = { "etuds": liste_filtree, "infos": { "id": src["formsemestre_id"], "title": src["titreannee"], "title_target": "formsemestre_status?formsemestre_id=%s" % src["formsemestre_id"], "filename": "etud_autorises", }, } # ajoute attribut inscrit qui indique si l'étudiant est déjà inscrit dans le semestre dest. for e in r[src["formsemestre_id"]]["etuds"]: e["inscrit"] = e["etudid"] in inscrits # Ajoute liste des etudiants actuellement inscrits for e in inscrits.values(): e["inscrit"] = True r[sem["formsemestre_id"]] = { "etuds": list(inscrits.values()), "infos": { "id": sem["formsemestre_id"], "title": "Semestre cible: " + sem["titreannee"], "title_target": "formsemestre_status?formsemestre_id=%s" % sem["formsemestre_id"], "comment": " actuellement inscrits dans ce semestre", "help": "Ces étudiants sont actuellement inscrits dans ce semestre. Si vous les décochez, il seront désinscrits.", "filename": "etud_inscrits", }, } return r, inscrits, candidats def list_inscrits(formsemestre_id, with_dems=False): """Étudiants déjà inscrits à ce semestre { etudid : etud } """ if not with_dems: ins = sco_formsemestre_inscriptions.do_formsemestre_inscription_listinscrits( formsemestre_id ) # optimized else: args = {"formsemestre_id": formsemestre_id} ins = sco_formsemestre_inscriptions.do_formsemestre_inscription_list(args=args) inscr = {} for i in ins: etudid = i["etudid"] inscr[etudid] = sco_etud.get_etud_info(etudid=etudid, filled=True)[0] return inscr def list_etuds_from_sem(src, dst) -> list[dict]: """Liste des etudiants du semestre src qui sont autorisés à passer dans le semestre dst.""" target = dst["semestre_id"] dpv = sco_dict_pv_jury.dict_pvjury(src["formsemestre_id"]) if not dpv: return [] etuds = [ x["identite"] for x in dpv["decisions"] if target in [a["semestre_id"] for a in x["autorisations"]] ] return etuds def list_inscrits_date(sem): """Liste les etudiants inscrits dans n'importe quel semestre du même département SAUF sem à la date de début de sem. """ cnx = ndb.GetDBConnexion() cursor = cnx.cursor(cursor_factory=ndb.ScoDocCursor) sem["date_debut_iso"] = ndb.DateDMYtoISO(sem["date_debut"]) cursor.execute( """SELECT ins.etudid FROM notes_formsemestre_inscription ins, notes_formsemestre S WHERE ins.formsemestre_id = S.id AND S.id != %(formsemestre_id)s AND S.date_debut <= %(date_debut_iso)s AND S.date_fin >= %(date_debut_iso)s AND S.dept_id = %(dept_id)s """, sem, ) return [x[0] for x in cursor.fetchall()] def do_inscrit(sem, etudids, inscrit_groupes=False): """Inscrit ces etudiants dans ce semestre (la liste doit avoir été vérifiée au préalable) En option: inscrit aux mêmes groupes que dans le semestre origine """ formsemestre: FormSemestre = FormSemestre.query.get(sem["formsemestre_id"]) formsemestre.setup_parcours_groups() log(f"do_inscrit (inscrit_groupes={inscrit_groupes}): {etudids}") for etudid in etudids: sco_formsemestre_inscriptions.do_formsemestre_inscription_with_modules( sem["formsemestre_id"], etudid, etat=scu.INSCRIT, method="formsemestre_inscr_passage", ) if inscrit_groupes: # Inscription dans les mêmes groupes que ceux du semestre d'origine, # s'ils existent. # (mise en correspondance à partir du nom du groupe, sans tenir compte # du nom de la partition: évidemment, cela ne marche pas si on a les # même noms de groupes dans des partitions différentes) etud = sco_etud.get_etud_info(etudid=etudid, filled=True)[0] # recherche le semestre origine (il serait plus propre de l'avoir conservé!) if len(etud["sems"]) < 2: continue prev_formsemestre = etud["sems"][1] sco_groups.etud_add_group_infos( etud, prev_formsemestre["formsemestre_id"] if prev_formsemestre else None, ) cursem_groups_by_name = { g["group_name"]: g for g in sco_groups.get_sem_groups(sem["formsemestre_id"]) if g["group_name"] } # forme la liste des groupes présents dans les deux semestres: partition_groups = [] # [ partition+group ] (ds nouveau sem.) for partition_id in etud["partitions"]: prev_group_name = etud["partitions"][partition_id]["group_name"] if prev_group_name in cursem_groups_by_name: new_group = cursem_groups_by_name[prev_group_name] partition_groups.append(new_group) # Inscrit aux groupes for partition_group in partition_groups: sco_groups.change_etud_group_in_partition( etudid, partition_group["group_id"], partition_group, ) def do_desinscrit(sem, etudids): log("do_desinscrit: %s" % etudids) for etudid in etudids: sco_formsemestre_inscriptions.do_formsemestre_desinscription( etudid, sem["formsemestre_id"] ) def list_source_sems(sem, delai=None) -> list[dict]: """Liste des semestres sources sem est le semestre destination """ # liste des semestres débutant a moins # de delai (en jours) de la date de fin du semestre d'origine. sems = sco_formsemestre.do_formsemestre_list() othersems = [] d, m, y = [int(x) for x in sem["date_debut"].split("/")] date_debut_dst = datetime.date(y, m, d) delais = datetime.timedelta(delai) for s in sems: if s["formsemestre_id"] == sem["formsemestre_id"]: continue # saute le semestre destination if s["date_fin"]: d, m, y = [int(x) for x in s["date_fin"].split("/")] date_fin = datetime.date(y, m, d) if date_debut_dst - date_fin > delais: continue # semestre trop ancien if date_fin > date_debut_dst: continue # semestre trop récent # Elimine les semestres de formations speciales (sans parcours) if s["semestre_id"] == codes_cursus.NO_SEMESTRE_ID: continue # F = sco_formations.formation_list(args={"formation_id": s["formation_id"]})[0] parcours = codes_cursus.get_cursus_from_code(F["type_parcours"]) if not parcours.ALLOW_SEM_SKIP: if s["semestre_id"] < (sem["semestre_id"] - 1): continue othersems.append(s) return othersems def formsemestre_inscr_passage( formsemestre_id, etuds=[], inscrit_groupes=False, submitted=False, dialog_confirmed=False, ignore_jury=False, ) -> str: """Page Form. pour inscription des etudiants d'un semestre dans un autre (donné par formsemestre_id). Permet de selectionner parmi les etudiants autorisés à s'inscrire. Principe: - trouver liste d'etud, par semestre - afficher chaque semestre "boites" avec cases à cocher - si l'étudiant est déjà inscrit, le signaler (gras, nom de groupes): il peut être désinscrit - on peut choisir les groupes TD, TP, TA - seuls les étudiants non inscrits changent (de groupe) - les étudiants inscrit qui se trouvent décochés sont désinscrits - Confirmation: indiquer les étudiants inscrits et ceux désinscrits, le total courant. """ inscrit_groupes = int(inscrit_groupes) ignore_jury = int(ignore_jury) sem = sco_formsemestre.get_formsemestre(formsemestre_id) # -- check lock if not sem["etat"]: raise ScoValueError("opération impossible: semestre verrouille") header = html_sco_header.sco_header(page_title="Passage des étudiants") footer = html_sco_header.sco_footer() H = [header] if isinstance(etuds, str): # list de strings, vient du form de confirmation etuds = [int(x) for x in etuds.split(",") if x] elif isinstance(etuds, int): etuds = [etuds] elif etuds and isinstance(etuds[0], str): etuds = [int(x) for x in etuds] auth_etuds_by_sem, inscrits, candidats = list_authorized_etuds_by_sem( sem, ignore_jury=ignore_jury ) etuds_set = set(etuds) candidats_set = set(candidats) inscrits_set = set(inscrits) candidats_non_inscrits = candidats_set - inscrits_set inscrits_ailleurs = set(list_inscrits_date(sem)) def set_to_sorted_etud_list(etudset): etuds = [candidats[etudid] for etudid in etudset] etuds.sort(key=itemgetter("nom")) return etuds if submitted: a_inscrire = etuds_set.intersection(candidats_set) - inscrits_set a_desinscrire = inscrits_set - etuds_set else: a_inscrire = a_desinscrire = [] if not submitted: H += _build_page( sem, auth_etuds_by_sem, inscrits, candidats_non_inscrits, inscrits_ailleurs, inscrit_groupes=inscrit_groupes, ignore_jury=ignore_jury, ) else: if not dialog_confirmed: # Confirmation if a_inscrire: H.append("

Étudiants à inscrire

    ") for etud in set_to_sorted_etud_list(a_inscrire): H.append("
  1. %(nomprenom)s
  2. " % etud) H.append("
") a_inscrire_en_double = inscrits_ailleurs.intersection(a_inscrire) if a_inscrire_en_double: H.append("

dont étudiants déjà inscrits:

") if a_desinscrire: H.append("

Étudiants à désinscrire

    ") for etudid in a_desinscrire: H.append( '
  1. %(nomprenom)s
  2. ' % inscrits[etudid] ) H.append("
") todo = a_inscrire or a_desinscrire if not todo: H.append("""

Il n'y a rien à modifier !

""") H.append( scu.confirm_dialog( dest_url="formsemestre_inscr_passage" if todo else "formsemestre_status", message="

Confirmer ?

" if todo else "", add_headers=False, cancel_url="formsemestre_inscr_passage?formsemestre_id=" + str(formsemestre_id), OK="Effectuer l'opération" if todo else "", parameters={ "formsemestre_id": formsemestre_id, "etuds": ",".join([str(x) for x in etuds]), "inscrit_groupes": inscrit_groupes, "ignore_jury": ignore_jury, "submitted": 1, }, ) ) else: with sco_cache.DeferredSemCacheManager(): # Inscription des étudiants au nouveau semestre: do_inscrit( sem, a_inscrire, inscrit_groupes=inscrit_groupes, ) # Désinscriptions: do_desinscrit(sem, a_desinscrire) H.append( f"""

Opération effectuée