From 432831140cd8dada643fa19322ca9edd9a3744cd Mon Sep 17 00:00:00 2001 From: Jean-Marie Place Date: Sat, 14 Aug 2021 10:12:40 +0200 Subject: [PATCH] =?UTF-8?q?Export/Import=20d'=C3=A9tudiant=20via=20fichier?= =?UTF-8?q?s=20xlsx.=20Point=20d=C3=A9licats:=20*=20Le=20message=20d'erreu?= =?UTF-8?q?r=20pour=20une=20case=20vide=20=C3=A9tait=20une=20exception=20p?= =?UTF-8?q?ython.=20diagnostic:=20la=20cr=C3=A9ation=20de=20l'=C3=A9tudian?= =?UTF-8?q?t=20dans=20la=20BDD=20se=20faisait=20avant=20le=20controle=20de?= =?UTF-8?q?=20la=20civilit=C3=A9=20et=20plantait=20quand=20None=20correcti?= =?UTF-8?q?f:=20ajout=20d'une=20methode=20=5Fcheck=5Fcivilite=20(a=20cote?= =?UTF-8?q?=20des=20m=C3=A9thodes=20de=20contr=C3=B4le=20d=20unicit=C3=A9?= =?UTF-8?q?=20de=20nip=20et=20d=20ine=20(sco=5Fetud.py)=20*=20Le=20format?= =?UTF-8?q?=20de=20date=20a=20chang=C3=A9=20entre=20pyExcelerator=20et=20o?= =?UTF-8?q?penpyxl=20(r=C3=A9=C3=A9criture=20de=20sco=5Fexcel.xldate=5Fas?= =?UTF-8?q?=5Fdatetime)=20le=20format=20xlxs=20d=20import=20pr=C3=A9cise?= =?UTF-8?q?=20qu'une=20date=20peut=20=C3=AAtre=20sp=C3=A9cifi=C3=A9=20soit?= =?UTF-8?q?=20en=20ISO=20soit=20sous=20forme=20d'un=20nombre.=20c=20est=20?= =?UTF-8?q?test=C3=A9=20avec=20des=20=C3=A9criture=20de=20fichier=20xlsx?= =?UTF-8?q?=20depuis=20Excel=202019=20et=20LibreOffice=207=20(mais=20sans?= =?UTF-8?q?=20maitrise=20sur=20la=20forme=20de=20date=20utilis=C3=A9e)=20p?= =?UTF-8?q?ar=20contre=20plantage=20si=20tentative=20de=20lire=20un=20fich?= =?UTF-8?q?ier=20ods=20(fonction=20excel=5Fbytes=5Fto=5Flist=20a=20fixer)?= =?UTF-8?q?=20*=20Le=20renvoi=20vers=20la=20page=20de=20formation=5Fid=20s?= =?UTF-8?q?e=20faisait=20mal=20correction:=20calcul=20de=20l'url=20(sco=5F?= =?UTF-8?q?import=5Fetuds.py:245)=20et=20(scolar.py:1710=20celle-ci=20peut?= =?UTF-8?q?=20=C3=AAtre=20pas=20necessaire)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/scodoc/sco_etud.py | 9 +++-- app/scodoc/sco_excel.py | 65 ++++++++++++++++++---------------- app/scodoc/sco_import_etuds.py | 19 +++++----- app/views/scolar.py | 9 +++-- 4 files changed, 59 insertions(+), 43 deletions(-) diff --git a/app/scodoc/sco_etud.py b/app/scodoc/sco_etud.py index 002fe5ada..4b1bf1dc7 100644 --- a/app/scodoc/sco_etud.py +++ b/app/scodoc/sco_etud.py @@ -366,6 +366,11 @@ def _check_duplicate_code(cnx, args, code_name, context, edit=True, REQUEST=None raise ScoGenError(err_page) +def _check_civilite(args): + civilite = args.get("civilite", "X") or "X" + args["civilite"] = input_civilite(civilite) # TODO: A faire valider + + def identite_edit(cnx, args, context=None, REQUEST=None): """Modifie l'identite d'un étudiant. Si context et notification et difference, envoie message notification. @@ -403,6 +408,7 @@ def identite_create(cnx, args, context=None, REQUEST=None): "check unique etudid, then create" _check_duplicate_code(cnx, args, "code_nip", context, edit=False, REQUEST=REQUEST) _check_duplicate_code(cnx, args, "code_ine", context, edit=False, REQUEST=REQUEST) + _check_civilite(args) if "etudid" in args: etudid = args["etudid"] @@ -755,7 +761,6 @@ _etud_annotationsEditor = ndb.EditableTable( output_formators={"comment": safehtml.html_to_safe_html, "date": ndb.DateISOtoDMY}, ) - etud_annotations_create = _etud_annotationsEditor.create etud_annotations_delete = _etud_annotationsEditor.delete etud_annotations_list = _etud_annotationsEditor.list @@ -1049,4 +1054,4 @@ def descr_situation_etud(context, etudid, ne=""): else: date_dem = events[0]["event_date"] situation += " le " + str(date_dem) - return situation \ No newline at end of file + return situation diff --git a/app/scodoc/sco_excel.py b/app/scodoc/sco_excel.py index 06e2ad70b..832658030 100644 --- a/app/scodoc/sco_excel.py +++ b/app/scodoc/sco_excel.py @@ -34,6 +34,7 @@ import time from enum import Enum from tempfile import NamedTemporaryFile +import openpyxl.utils.datetime from openpyxl import Workbook, load_workbook from openpyxl.cell import WriteOnlyCell from openpyxl.styles import Font, Border, Side, Alignment, PatternFill @@ -86,40 +87,42 @@ def send_excel_file(request, data, filename, mime=scu.XLSX_MIMETYPE): # a datetime.time object will be returned. #
Note: 1904-01-01 is not regarded as a valid date in the datemode 1 system; its "serial number" # is zero. - -_XLDAYS_TOO_LARGE = (2958466, 2958466 - 1462) # This is equivalent to 10000-01-01 +# +# _XLDAYS_TOO_LARGE = (2958466, 2958466 - 1462) # This is equivalent to 10000-01-01 +# def xldate_as_datetime(xldate, datemode=0): - if datemode not in (0, 1): - raise ValueError("invalid mode %s" % datemode) - if xldate == 0.00: - return datetime.time(0, 0, 0) - if xldate < 0.00: - raise ValueError("invalid date code %s" % xldate) - xldays = int(xldate) - frac = xldate - xldays - seconds = int(round(frac * 86400.0)) - assert 0 <= seconds <= 86400 - if seconds == 86400: - seconds = 0 - xldays += 1 - if xldays >= _XLDAYS_TOO_LARGE[datemode]: - raise ValueError("date too large %s" % xldate) - - if xldays == 0: - # second = seconds % 60; minutes = seconds // 60 - minutes, second = divmod(seconds, 60) - # minute = minutes % 60; hour = minutes // 60 - hour, minute = divmod(minutes, 60) - return datetime.time(hour, minute, second) - - if xldays < 61 and datemode == 0: - raise ValueError("ambiguous date %s" % xldate) - - return datetime.datetime.fromordinal( - xldays + 693594 + 1462 * datemode - ) + datetime.timedelta(seconds=seconds) + return openpyxl.utils.datetime.from_ISO8601(xldate) + # if datemode not in (0, 1): + # raise ValueError("invalid mode %s" % datemode) + # if xldate == 0.00: + # return datetime.time(0, 0, 0) + # if xldate < 0.00: + # raise ValueError("invalid date code %s" % xldate) + # xldays = int(xldate) + # frac = xldate - xldays + # seconds = int(round(frac * 86400.0)) + # assert 0 <= seconds <= 86400 + # if seconds == 86400: + # seconds = 0 + # xldays += 1 + # if xldays >= _XLDAYS_TOO_LARGE[datemode]: + # raise ValueError("date too large %s" % xldate) + # + # if xldays == 0: + # # second = seconds % 60; minutes = seconds // 60 + # minutes, second = divmod(seconds, 60) + # # minute = minutes % 60; hour = minutes // 60 + # hour, minute = divmod(minutes, 60) + # return datetime.time(hour, minute, second) + # + # if xldays < 61 and datemode == 0: + # raise ValueError("ambiguous date %s" % xldate) + # + # return datetime.datetime.fromordinal( + # xldays + 693594 + 1462 * datemode + # ) + datetime.timedelta(seconds=seconds) class ScoExcelBook: diff --git a/app/scodoc/sco_import_etuds.py b/app/scodoc/sco_import_etuds.py index 499059152..d31e13bf3 100644 --- a/app/scodoc/sco_import_etuds.py +++ b/app/scodoc/sco_import_etuds.py @@ -32,10 +32,14 @@ import collections import os import re import time +from datetime import date + +import flask import app.scodoc.sco_utils as scu import app.scodoc.notesdb as ndb from app.scodoc.notes_log import log +from app.scodoc.sco_excel import COLORS from app.scodoc.sco_formsemestre_inscriptions import ( do_formsemestre_inscription_with_modules, ) @@ -164,7 +168,7 @@ def sco_import_generate_excel_sample( If group_ids, liste les etudiants de ces groupes """ style = sco_excel.excel_make_style(bold=True) - style_required = sco_excel.excel_make_style(bold=True, color="red") + style_required = sco_excel.excel_make_style(bold=True, color=COLORS.RED) titles = [] titlesStyles = [] for l in fmt: @@ -214,7 +218,7 @@ def sco_import_generate_excel_sample( else: lines = [[]] # empty content, titles only return sco_excel.excel_simple_table( - titles=titles, titlesStyles=titlesStyles, sheet_name="Etudiants", lines=lines + titles=titles, titles_styles=titlesStyles, sheet_name="Etudiants", lines=lines ) @@ -238,7 +242,7 @@ def students_import_excel( ) if REQUEST: if formsemestre_id: - dest = "formsemestre_status?formsemestre_id=%s" % formsemestre_id + dest = "Notes/formsemestre_status?formsemestre_id=%s" % formsemestre_id else: dest = scu.NotesURL() H = [html_sco_header.sco_header(page_title="Import etudiants")] @@ -271,12 +275,11 @@ def scolars_import_excel_file( exceldata = datafile.read() if not exceldata: raise ScoValueError("Ficher excel vide ou invalide") - diag, data = sco_excel.Excel_to_list(exceldata) + diag, data = sco_excel.excel_bytes_to_list(exceldata) if not data: # probably a bug raise ScoException("scolars_import_excel_file: empty file !") formsemestre_to_invalidate = set() - # 1- --- check title line titles = {} fmt = sco_import_format() @@ -378,8 +381,8 @@ def scolars_import_excel_file( # Excel date conversion: if scu.strlower(titleslist[i]) == "date_naissance": if val: - if re.match(r"^[0-9]*\.?[0-9]*$", str(val)): - val = sco_excel.xldate_as_datetime(float(val)) + # if re.match(r"^[0-9]*\.?[0-9]*$", str(val)): + val = sco_excel.xldate_as_datetime(val) # INE if ( scu.strlower(titleslist[i]) == "code_ine" @@ -625,7 +628,7 @@ def scolars_import_admission( etuds_by_nomprenom[np] = m exceldata = datafile.read() - diag2, data = sco_excel.Excel_to_list(exceldata, convert_to_string=False) + diag2, data = sco_excel.excel_bytes_to_list(exceldata) if not data: raise ScoException("scolars_import_admission: empty file !") diag += diag2 diff --git a/app/views/scolar.py b/app/views/scolar.py index d541efd01..8579c3ecf 100644 --- a/app/views/scolar.py +++ b/app/views/scolar.py @@ -1699,7 +1699,7 @@ def check_group_apogee( return "\n".join(H) + html_sco_header.sco_footer() -@bp.route("/form_students_import_excel") +@bp.route("/form_students_import_excel", methods=["GET", "POST"]) @permission_required(Permission.ScoEtudInscrit) @scodoc7func(context) def form_students_import_excel(context, REQUEST, formsemestre_id=None): @@ -1707,7 +1707,12 @@ def form_students_import_excel(context, REQUEST, formsemestre_id=None): if formsemestre_id: sem = sco_formsemestre.get_formsemestre(context, formsemestre_id) dest_url = ( - scu.ScoURL() + "/formsemestre_status?formsemestre_id=%s" % formsemestre_id + # scu.ScoURL() + "/formsemestre_status?formsemestre_id=%s" % formsemestre_id # TODO: Remplacer par for_url ? + url_for( + "notes.formsemestre_status", + scodoc_dept=g.scodoc_dept, + formsemestre_id=formsemestre_id, + ) ) else: sem = None