# -*- 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 # ############################################################################## """ScoDoc : formulaires gestion maquettes Apogee / export resultats """ from cStringIO import StringIO from zipfile import ZipFile from sco_utils import * from notes_log import log import sco_formsemestre import sco_formsemestre_status import notes_table from gen_tables import GenTable import sco_semset import sco_etape_apogee import sco_apogee_csv import sco_portal_apogee from sco_apogee_csv import APO_PORTAL_ENCODING, APO_INPUT_ENCODING import sco_archives def apo_semset_maq_status( context, semset_id="", allow_missing_apo=False, allow_missing_decisions=False, block_export_res_etape=False, block_export_res_sem=False, block_export_res_ues=False, block_export_res_modules=False, block_export_res_sdj=True, REQUEST=None, ): """Page statut / tableau de bord """ if not semset_id: raise ValueError("invalid null semset_id") semset = sco_semset.SemSet(context, semset_id=semset_id) semset.fill_formsemestres(REQUEST) allow_missing_apo = int( allow_missing_apo ) # autorise export meme si etudiants Apo manquants allow_missing_decisions = int( allow_missing_decisions ) # autorise export meme s'il manque des décisions de jury block_export_res_etape = int(block_export_res_etape) block_export_res_sem = int(block_export_res_sem) block_export_res_ues = int(block_export_res_ues) block_export_res_modules = int(block_export_res_modules) block_export_res_sdj = int(block_export_res_sdj) prefs = context.get_preferences() tab_archives = table_apo_csv_list(context, semset, REQUEST=REQUEST) ( ok_for_export, etapes_missing_csv, etuds_without_nip, nips_ok, nips_no_apo, nips_no_sco, apo_dups, maq_elems, sem_elems, ) = sco_etape_apogee.apo_csv_semset_check(context, semset, allow_missing_apo) if not allow_missing_decisions: ok_for_export &= semset["jury_ok"] H = [ context.sco_header( REQUEST, page_title="Export Apogée", javascripts=["js/apo_semset_maq_status.js"], ), """
Aucune maquette chargée
""") # Upload fichier: H.append( """""" % (semset_id,) ) # Récupération sur portail: maquette_url = sco_portal_apogee.get_maquette_url(context) if maquette_url: # portail configuré menu_etapes = """""" menu_etapes += "".join( ['' % (et, et) for et in semset.list_etapes()] ) H.append( """""" % (menu_etapes, semset_id) ) # H.append("Elements Apogée: %s
' % ", ".join( [ e if not e in missing else '' + e + "" for e in sorted(maq_elems) ] ) ) if missing: formation_ids = {sem["formation_id"] for sem in semset.sems} formations = [context.formation_list(formation_id=i)[0] for i in formation_ids] # log('formations=%s' % formations) H.append( 'Retour aux ensembles de semestres
Cette page permet de stocker les fichiers Apogée nécessaires pour l'export des résultats après les jurys, puis de remplir et exporter ces fichiers.
Les fichiers ("maquettes") Apogée sont de type CSV, du texte codé en %s.
On a un fichier par étape Apogée. Pour les obtenir, soit on peut les télécharger directement (si votre ScoDoc est interfacé avec Apogée), soit se débrouiller pour exporter le fichier texte depuis Apogée. Son contenu ressemble à cela:
XX-APO_TITRES-XX apoC_annee 2007/2008 apoC_cod_dip VDTCJ apoC_Cod_Exp 1 apoC_cod_vdi 111 apoC_Fichier_Exp VDTCJ_V1CJ.txt apoC_lib_dip DUT CJ apoC_Titre1 Export Apogée du 13/06/2008 à 14:29 apoC_Titre2 XX-APO_COLONNES-XX apoL_a01_code Type Objet Code Version Année Session Admission/Admissibilité Type Rés. Etudiant Numéro apoL_a02_nom 1 Nom apoL_a03_prenom 1 Prénom apoL_a04_naissance Session Admissibilité Naissance APO_COL_VAL_DEB apoL_c0001 VET V1CJ 111 2007 0 1 N V1CJ - DUT CJ an1 0 1 Note apoL_c0002 VET V1CJ 111 2007 0 1 B 0 1 Barème apoL_c0003 VET V1CJ 111 2007 0 1 R 0 1 Résultat APO_COL_VAL_FIN apoL_c0030 APO_COL_VAL_FIN XX-APO_VALEURS-XX apoL_a01_code apoL_a02_nom apoL_a03_prenom apoL_a04_naissance apoL_c0001 apoL_c0002 apoL_c0003 apoL_c0004 apoL_c0005 apoL_c0006 (...) 11681234 DUPONT TOTO 23/09/1986 18 20 ADM 18 20 ADM (...)
Après avoir obtenu les fichier, stockez-les dans ScoDoc (bouton "Ajouter fichier" en haut de cette page. Après vérification, il va apparaitre dans une table. Vous pouvez supprimer ce fichier, ou en ajouter d'autres si votre semestre correspond à plusieurs étapes Apogée.
ScoDoc vérifie que tous les étudiants du semestre sont mentionnés dans un fichier Apogée et que les étapes correspondent.
Lorsque c'est le cas, et que les décisions de jury sont saisies, un bouton "Export vers Apogée" apparait et vous pouvez exporter les résultats.
Vous obtiendrez alors un fichier ZIP comprenant tous les fichiers nécessaires. Certains de ces fichiers devront être importés dans Apogée.
Retour à la page d'export Apogée""" % semset_id ) return "\n".join(H) + context.sco_footer(REQUEST) def view_apo_csv_store( context, semset_id="", csvfile=None, data="", autodetect=False, REQUEST=None ): """Store CSV data Le semset identifie l'annee scolaire et le semestre Si csvfile, lit depuis FILE, sinon utilise data """ if not semset_id: raise ValueError("invalid null semset_id") semset = sco_semset.SemSet(context, semset_id=semset_id) if csvfile: data = csvfile.read() if autodetect: # check encoding (although documentation states that users SHOULD upload LATIN1) data, message = sco_apogee_csv.fix_data_encoding(data) if message: log("view_apo_csv_store: %s" % message) else: log("view_apo_csv_store: autodetection of encoding disabled by user") if not data: raise ScoValueError("view_apo_csv_store: no data") # check si etape maquette appartient bien au semset apo_data = sco_apogee_csv.ApoData( data, periode=semset["sem_id"] ) # parse le fichier -> exceptions if apo_data.etape not in semset["etapes"]: raise ScoValueError( "Le code étape de ce fichier ne correspond pas à ceux de cet ensemble" ) sco_etape_apogee.apo_csv_store( context, data, semset["annee_scolaire"], semset["sem_id"] ) return REQUEST.RESPONSE.redirect("apo_semset_maq_status?semset_id=" + semset_id) def view_apo_csv_download_and_store(context, etape_apo="", semset_id="", REQUEST=None): """Download maquette and store it """ if not semset_id: raise ValueError("invalid null semset_id") semset = sco_semset.SemSet(context, semset_id=semset_id) data = sco_portal_apogee.get_maquette_apogee( context, etape=etape_apo, annee_scolaire=semset["annee_scolaire"] ) # here, data is utf8 # but we store and generate latin1 files, to ease further import in Apogée data = data.decode(APO_PORTAL_ENCODING).encode(APO_INPUT_ENCODING) return view_apo_csv_store( context, semset_id, data=data, autodetect=False, REQUEST=REQUEST ) def view_apo_csv_delete( context, etape_apo="", semset_id="", dialog_confirmed=False, REQUEST=None ): """Delete CSV file """ if not semset_id: raise ValueError("invalid null semset_id") semset = sco_semset.SemSet(context, semset_id=semset_id) dest_url = "apo_semset_maq_status?semset_id=" + semset_id if not dialog_confirmed: return context.confirmDialog( """
La suppression sera définitive.
""" % (etape_apo,), dest_url="", REQUEST=REQUEST, cancel_url=dest_url, parameters={"semset_id": semset_id, "etape_apo": etape_apo}, ) info = sco_etape_apogee.apo_csv_get_archive( context, etape_apo, semset["annee_scolaire"], semset["sem_id"] ) sco_etape_apogee.apo_csv_delete(context, info["archive_id"]) return REQUEST.RESPONSE.redirect(dest_url + "&head_message=Archive%20supprimée") def view_apo_csv(context, etape_apo="", semset_id="", format="html", REQUEST=None): """Visualise une maquette stockée Si format="raw", renvoie le fichier maquette tel quel """ if not semset_id: raise ValueError("invalid null semset_id") semset = sco_semset.SemSet(context, semset_id=semset_id) annee_scolaire = semset["annee_scolaire"] sem_id = semset["sem_id"] csv_data = sco_etape_apogee.apo_csv_get(context, etape_apo, annee_scolaire, sem_id) if format == "raw": return sendCSVFile(REQUEST, csv_data, etape_apo + ".txt") apo_data = sco_apogee_csv.ApoData(csv_data, periode=semset["sem_id"]) ( ok_for_export, etapes_missing_csv, etuds_without_nip, nips_ok, nips_no_apo, nips_no_sco, apo_dups, maq_elems, sem_elems, ) = sco_etape_apogee.apo_csv_semset_check(context, semset) H = [ context.sco_header( REQUEST, page_title="Maquette Apogée enregistrée pour %s" % etape_apo, init_qtip=True, javascripts=["js/etud_info.js"], ), """Pour l'ensemble %(title)s (indice semestre: %(sem_id)s)
""" % semset, ] # Infos générales H.append( """Aucun étudiant
" + context.sco_footer(REQUEST) # Ajout infos sur ScoDoc vs Apogee for e in etuds: e["in_scodoc"] = e["nip"] not in nips_no_sco e["in_scodoc_str"] = {True: "oui", False: "non"}[e["in_scodoc"]] if e["in_scodoc"]: e["_in_scodoc_str_target"] = "ficheEtud?code_nip=" + e["nip"] e.update(context.getEtudInfo(code_nip=e["nip"], filled=True)[0]) e["_nom_td_attrs"] = 'id="%s" class="etudinfo"' % (e["etudid"],) e["_prenom_td_attrs"] = 'id="pre-%s" class="etudinfo"' % (e["etudid"],) else: e["_css_row_class"] = "apo_not_scodoc" # Construit la table: tab = GenTable( titles={ "nip": "Code NIP", "nom": "Nom", "prenom": "Prénom", "naissance": "Naissance", "in_scodoc_str": "Inscrit dans ces semestres ScoDoc", }, columns_ids=("nip", "nom", "prenom", "naissance", "in_scodoc_str"), rows=etuds, html_sortable=True, html_class="table_leftalign apo_maq_table", base_url="%s?etape_apo=%s&semset_id=%s" % (REQUEST.URL0, etape_apo, semset_id), filename="students_" + etape_apo, caption="Etudiants Apogée en " + etape_apo, preferences=context.get_preferences(), ) if format != "html": return tab.make_page(context, format=format, REQUEST=REQUEST) H += [ tab.html(), """""" % (etape_apo, semset_id), """""" % semset_id, context.sco_footer(REQUEST), ] return "\n".join(H) # called from Web def apo_csv_export_results( context, semset_id, block_export_res_etape=False, block_export_res_sem=False, block_export_res_ues=False, block_export_res_modules=False, block_export_res_sdj=False, REQUEST=None, ): """Remplit les fichiers CSV archivés et donne un ZIP avec tous les résultats. """ # nota: on peut éventuellement exporter même si tout n'est pas ok # mais le lien via le tableau de bord n'est pas actif # Les fichiers résultats ne sont pas stockés: pas besoin de permission particulière prefs = context.get_preferences() export_res_etape = prefs["export_res_etape"] and not int(block_export_res_etape) export_res_sem = prefs["export_res_sem"] and not int(block_export_res_sem) export_res_ues = prefs["export_res_ues"] and not int(block_export_res_ues) export_res_modules = prefs["export_res_modules"] and not int( block_export_res_modules ) export_res_sdj = prefs["export_res_sdj"] and not int(block_export_res_sdj) export_res_rat = prefs["export_res_rat"] if not semset_id: raise ValueError("invalid null semset_id") semset = sco_semset.SemSet(context, semset_id=semset_id) annee_scolaire = semset["annee_scolaire"] periode = semset["sem_id"] data = StringIO() dest_zip = ZipFile(data, "w") etapes_apo = sco_etape_apogee.apo_csv_list_stored_etapes( context, annee_scolaire, periode, etapes=semset.list_etapes() ) for etape_apo in etapes_apo: apo_csv = sco_etape_apogee.apo_csv_get( context, etape_apo, annee_scolaire, periode ) sco_apogee_csv.export_csv_to_apogee( context, apo_csv, periode=periode, export_res_etape=export_res_etape, export_res_sem=export_res_sem, export_res_ues=export_res_ues, export_res_modules=export_res_modules, export_res_sdj=export_res_sdj, export_res_rat=export_res_rat, dest_zip=dest_zip, REQUEST=REQUEST, ) basename = ( context.get_preference("DeptName") + str(annee_scolaire) + "-%s-" % periode + "-".join(etapes_apo) ) basename = sco_archives.sanitize_filename(unescape_html(basename)) dest_zip.close() size = data.tell() content_type = "application/zip" REQUEST.RESPONSE.setHeader( "content-disposition", 'attachement; filename="%s.zip"' % basename ) REQUEST.RESPONSE.setHeader("content-type", content_type) REQUEST.RESPONSE.setHeader("content-length", size) return data.getvalue()