# -*- mode: python -*- # -*- coding: utf-8 -*- ############################################################################## # # Gestion scolarite IUT # # Copyright (c) 1999 - 2020 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 # ############################################################################## """Gestion des ensembles de semestres: class SemSet: un ensemble de semestres d'un département, à exporter ves Apogée. En principe de la meme annee scolaire. SemSet.annees_scolaires() : les annees scolaires. e.g. [ 2015, 2016 ], ou le plus souvent, une seule: [2016] SemSet.list_etapes(): listes des étapes apogee et vdi des semestres (instances de ApoEtapeVDI) SemSet.add(sem): ajoute un semestre à l'ensemble sem_set_list(context) """ from sco_utils import * from notesdb import * from notes_log import log import sco_formsemestre from sco_formsemestre import ApoEtapeVDI import sco_formsemestre_status import sco_etape_apogee from sco_etape_bilan import EtapeBilan import sco_portal_apogee from gen_tables import GenTable _semset_editor = EditableTable( "notes_semset", "semset_id", ("semset_id", "title", "annee_scolaire", "sem_id") ) semset_create = _semset_editor.create semset_edit = _semset_editor.edit semset_list = _semset_editor.list semset_delete = _semset_editor.delete class SemSet(dict): def __init__(self, context, semset_id=None, title="", annee_scolaire="", sem_id=""): """Load and init, or, if semset_id is not specified, create """ if not annee_scolaire and not semset_id: # on autorise annee_scolaire null si sem_id pour pouvoir lire les anciens semsets # mal construits... raise ScoValueError("Année scolaire invalide !") self.semset_id = semset_id self["semset_id"] = semset_id self.context = context self.sems = [] self.formsemestre_ids = [] cnx = context.GetDBConnexion() if semset_id: # read existing set L = semset_list(cnx, args={"semset_id": semset_id}) if not L: raise ValueError("invalid semset_id %s" % semset_id) self["title"] = L[0]["title"] self["annee_scolaire"] = L[0]["annee_scolaire"] self["sem_id"] = L[0]["sem_id"] r = SimpleDictFetch( context, "SELECT formsemestre_id FROM notes_semset_formsemestre WHERE semset_id = %(semset_id)s", {"semset_id": semset_id}, ) if r: self.formsemestre_ids = {x["formsemestre_id"] for x in r} # a set else: # create a new empty set self.semset_id = semset_create( cnx, {"title": title, "annee_scolaire": annee_scolaire, "sem_id": sem_id}, ) log("created new semset_id=%s" % self.semset_id) self.load_sems() # analyse des semestres pour construire le bilan par semestre et par étape self.bilan = EtapeBilan(context) for sem in self.sems: self.bilan.add_sem(sem) def delete(self): """delete""" cnx = self.context.GetDBConnexion() semset_delete(cnx, self.semset_id) def edit(self, args): cnx = self.context.GetDBConnexion() semset_edit(cnx, args) def load_sems(self): """Load formsemestres""" self.sems = [] for formsemestre_id in self.formsemestre_ids: self.sems.append( sco_formsemestre.get_formsemestre(self.context, formsemestre_id) ) if self.sems: self["date_debut"] = min([sem["date_debut_iso"] for sem in self.sems]) self["date_fin"] = max([sem["date_fin_iso"] for sem in self.sems]) else: self["date_debut"] = "" self["date_fin"] = "" self["etapes"] = self.list_etapes() self["semtitles"] = [sem["titre_num"] for sem in self.sems] # Construction du ou des lien(s) vers le semestre pattern = '%(titreannee)s' self["semlinks"] = [(pattern % sem) for sem in self.sems] self["semtitles_str"] = "
".join(self["semlinks"]) def fill_formsemestres(self, REQUEST): for sem in self.sems: sco_formsemestre_status.fill_formsemestre(self.context, sem, REQUEST) ets = sco_etape_apogee.apo_get_sem_etapes(self.context, sem) sem["etapes_apo_str"] = sco_formsemestre.etapes_apo_str(sorted(list(ets))) def add(self, formsemestre_id): # check if formsemestre_id in self.formsemestre_ids: return # already there if formsemestre_id not in [ sem["formsemestre_id"] for sem in self.list_possible_sems() ]: raise ValueError( "can't add %s to set %s: incompatible sem_id" % (formsemestre_id, self.semset_id) ) SimpleQuery( self.context, "INSERT INTO notes_semset_formsemestre (formsemestre_id, semset_id) VALUES (%(formsemestre_id)s, %(semset_id)s)", {"formsemestre_id": formsemestre_id, "semset_id": self.semset_id}, ) self.load_sems() # update our list def remove(self, formsemestre_id): SimpleQuery( self.context, "DELETE FROM notes_semset_formsemestre WHERE semset_id=%(semset_id)s AND formsemestre_id=%(formsemestre_id)s", {"formsemestre_id": formsemestre_id, "semset_id": self.semset_id}, ) self.load_sems() # update our list def annees_scolaires(self): """Les annees scolaires. e.g. [ 2015, 2016 ], ou le plus souvent, une seule: [2016] L'année scolaire est l'année de début du semestre (2015 pour 2015-2016) """ annees = list(set([int(s["annee_debut"]) for s in self.sems])) annees.sort() return annees def list_etapes(self): """Listes triée des étapes Apogée des semestres (instances de ApoEtapeVDI). Chaque étape apparait une seule fois, dans sa forme la plus générale. Si on a [ 'V1RT', 'V1RT!111' ], le résultat sera [ 'V1RT' ] Si on a [ 'V1RT!111', 'V1RT!112' ], le résultat sera [ 'V1RT!111', 'V1RT!112' ] """ D = {} # { etape : { versions vdi } } for s in self.sems: for et in s["etapes"]: if et: if et.etape in D: D[et.etape].add(et.vdi) else: D[et.etape] = {et.vdi} # enlève les versions excédentaires: for etape in D: if "" in D[etape]: D[etape] = [""] # forme liste triée d'instances: etapes = [] for etape in D: for vdi in D[etape]: etapes.append(ApoEtapeVDI(etape=etape, vdi=vdi)) etapes.sort() return etapes def list_possible_sems(self): """List sems that can be added to this set """ sems = sco_formsemestre.do_formsemestre_list(self.context) # remove sems already here: sems = [ sem for sem in sems if sem["formsemestre_id"] not in self.formsemestre_ids ] # filter annee, sem_id: # Remplacement du filtre de proposition des semestres potentiels # au lieu de la parité (sem 1 et 3 / sem 2 et 4) on filtre sur la date de # debut du semestre: ceci permet d'ajouter les semestres décalés if self["annee_scolaire"]: sems = [ sem for sem in sems if sco_formsemestre.sem_in_semestre_scolaire( self.context, sem, year=self["annee_scolaire"], saison=self["sem_id"], ) ] return sems def load_etuds(self): context = self.context self["etuds_without_nip"] = set() # etudids self["jury_ok"] = True for sem in self.sems: nt = context._getNotesCache().get_NotesTable( context, sem["formsemestre_id"] ) sem["etuds"] = nt.identdict.values() sem["nips"] = {e["code_nip"] for e in sem["etuds"] if e["code_nip"]} sem["etuds_without_nip"] = { e["etudid"] for e in sem["etuds"] if not e["code_nip"] } self["etuds_without_nip"] |= sem["etuds_without_nip"] sem["jury_ok"] = nt.all_etuds_have_sem_decisions() self["jury_ok"] &= sem["jury_ok"] def html_descr(self): """Short HTML description """ H = [ """Ensemble de semestres %(title)s""" % self ] if self["annee_scolaire"]: H.append("

Année scolaire: %(annee_scolaire)s

" % self) else: H.append( "

Année(s) scolaire(s) présentes: %s" % ", ".join([str(x) for x in self.annees_scolaires()]) ) if len(self.annees_scolaires()) > 1: H.append( ' (attention, plusieurs années !)' ) H.append("

") if self["sem_id"]: H.append( "

Période: %(sem_id)s (1: septembre, 2: janvier)

" % self ) H.append( "

Etapes: %s

" % sco_formsemestre.etapes_apo_str(self.list_etapes()) ) H.append("""

Semestres de l'ensemble: