2020-09-26 16:19:37 +02:00
# -*- mode: python -*-
# -*- coding: utf-8 -*-
##############################################################################
#
# Gestion scolarite IUT
#
2021-01-01 17:51:08 +01:00
# Copyright (c) 1999 - 2021 Emmanuel Viennet. All rights reserved.
2020-09-26 16:19:37 +02:00
#
# 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
#
##############################################################################
2020-12-26 00:11:55 +01:00
""" Accès donnees etudiants
2020-09-26 16:19:37 +02:00
"""
2021-01-17 22:31:28 +01:00
2021-06-19 23:21:37 +02:00
# Ancien module "scolars"
2021-07-25 09:51:09 +02:00
import os
2021-06-19 23:21:37 +02:00
import time
2020-09-26 16:19:37 +02:00
2021-08-22 13:24:36 +02:00
from flask import url_for , g , request
2021-07-11 17:37:12 +02:00
2020-12-15 08:35:44 +01:00
from email . mime . multipart import MIMEMultipart
from email . mime . text import MIMEText
from email . header import Header
from email . mime . base import MIMEBase
2021-07-09 23:19:30 +02:00
from operator import itemgetter
2020-09-26 16:19:37 +02:00
2021-06-19 23:21:37 +02:00
import app . scodoc . sco_utils as scu
from app . scodoc . sco_utils import SCO_ENCODING
import app . scodoc . notesdb as ndb
from app . scodoc . sco_exceptions import ScoGenError , ScoValueError
2021-08-29 19:57:32 +02:00
from app import log
2021-06-19 23:21:37 +02:00
from app . scodoc . TrivialFormulator import TrivialFormulator
from app . scodoc import safehtml
from app . scodoc import sco_preferences
from app . scodoc . scolog import logdb
2021-08-26 23:43:54 +02:00
from flask_mail import Message
from app import mail
2021-06-19 23:21:37 +02:00
MONTH_NAMES_ABBREV = [
2020-09-26 16:19:37 +02:00
" Jan " ,
2021-06-19 23:21:37 +02:00
" Fév " ,
2020-09-26 16:19:37 +02:00
" Mars " ,
" Avr " ,
" Mai " ,
" Juin " ,
" Jul " ,
2021-06-19 23:21:37 +02:00
" Août " ,
2020-09-26 16:19:37 +02:00
" Sept " ,
" Oct " ,
" Nov " ,
2021-06-19 23:21:37 +02:00
" Déc " ,
2020-09-26 16:19:37 +02:00
]
2021-06-19 23:21:37 +02:00
MONTH_NAMES = [
2020-09-26 16:19:37 +02:00
" janvier " ,
" février " ,
" mars " ,
" avril " ,
" mai " ,
" juin " ,
" juillet " ,
2021-06-19 23:21:37 +02:00
" août " ,
2020-09-26 16:19:37 +02:00
" septembre " ,
" octobre " ,
" novembre " ,
" décembre " ,
]
def format_etud_ident ( etud ) :
""" Format identite de l ' étudiant (modifié en place)
nom , prénom et formes associees
"""
etud [ " nom " ] = format_nom ( etud [ " nom " ] )
if " nom_usuel " in etud :
etud [ " nom_usuel " ] = format_nom ( etud [ " nom_usuel " ] )
else :
etud [ " nom_usuel " ] = " "
etud [ " prenom " ] = format_prenom ( etud [ " prenom " ] )
2021-02-13 17:28:55 +01:00
etud [ " civilite_str " ] = format_civilite ( etud [ " civilite " ] )
2020-09-26 16:19:37 +02:00
# Nom à afficher:
if etud [ " nom_usuel " ] :
etud [ " nom_disp " ] = etud [ " nom_usuel " ]
if etud [ " nom " ] :
etud [ " nom_disp " ] + = " ( " + etud [ " nom " ] + " ) "
else :
etud [ " nom_disp " ] = etud [ " nom " ]
etud [ " nomprenom " ] = format_nomprenom ( etud ) # M. Pierre DUPONT
2021-02-13 17:28:55 +01:00
if etud [ " civilite " ] == " M " :
2020-09-26 16:19:37 +02:00
etud [ " ne " ] = " "
2021-02-13 17:28:55 +01:00
elif etud [ " civilite " ] == " F " :
2020-09-26 16:19:37 +02:00
etud [ " ne " ] = " e "
2021-02-13 17:28:55 +01:00
else : # 'X'
etud [ " ne " ] = " (e) "
2020-09-26 16:19:37 +02:00
# Mail à utiliser pour les envois vers l'étudiant:
# choix qui pourrait être controé par une preference
# ici priorité au mail institutionnel:
etud [ " email_default " ] = etud . get ( " email " , " " ) or etud . get ( " emailperso " , " " )
def force_uppercase ( s ) :
2021-08-21 00:24:51 +02:00
return s . upper ( ) if s else s
2020-09-26 16:19:37 +02:00
2021-02-13 22:06:03 +01:00
def format_nomprenom ( etud , reverse = False ) :
""" Formatte civilité/nom/prenom pour affichages: " M. Pierre Dupont "
Si reverse , " Dupont Pierre " , sans civilité .
"""
nom = etud . get ( " nom_disp " , " " ) or etud . get ( " nom_usuel " , " " ) or etud [ " nom " ]
prenom = format_prenom ( etud [ " prenom " ] )
civilite = format_civilite ( etud [ " civilite " ] )
if reverse :
fs = [ nom , prenom ]
else :
fs = [ civilite , prenom , nom ]
return " " . join ( [ x for x in fs if x ] )
2020-09-26 16:19:37 +02:00
def format_prenom ( s ) :
2021-02-13 17:28:55 +01:00
" Formatte prenom etudiant pour affichage "
2020-09-26 16:19:37 +02:00
if not s :
return " "
frags = s . split ( )
r = [ ]
for frag in frags :
fs = frag . split ( " - " )
2021-07-12 00:25:23 +02:00
r . append ( " - " . join ( [ x . lower ( ) . capitalize ( ) for x in fs ] ) )
2020-09-26 16:19:37 +02:00
return " " . join ( r )
def format_nom ( s , uppercase = True ) :
if not s :
return " "
if uppercase :
2021-08-21 00:24:51 +02:00
return s . upper ( )
2020-09-26 16:19:37 +02:00
else :
return format_prenom ( s )
2021-02-13 17:28:55 +01:00
def input_civilite ( s ) :
""" Converts external representation of civilite to internal:
' M ' , ' F ' , or ' X ' ( and nothing else ) .
Raises valueError if conversion fails .
"""
2021-08-21 00:24:51 +02:00
s = s . upper ( ) . strip ( )
2021-02-13 17:28:55 +01:00
if s in ( " M " , " M. " , " MR " , " H " ) :
return " M "
elif s in ( " F " , " MLLE " , " MLLE. " , " MELLE " , " MME " ) :
return " F "
elif s == " X " or not s :
return " X "
raise ValueError ( " valeur invalide pour la civilité: %s " % s )
def format_civilite ( civilite ) :
""" returns ' M. ' ou ' Mme ' ou ' ' (pour le genre neutre,
personne ne souhaitant pas d ' affichage)
"""
try :
return {
" M " : " M. " ,
" F " : " Mme " ,
" X " : " " ,
} [ civilite ]
except KeyError :
raise ValueError ( " valeur invalide pour la civilité: %s " % civilite )
2020-09-26 16:19:37 +02:00
def format_lycee ( nomlycee ) :
nomlycee = nomlycee . strip ( )
2021-08-21 00:24:51 +02:00
s = nomlycee . lower ( )
2020-09-26 16:19:37 +02:00
if s [ : 5 ] == " lycee " or s [ : 5 ] == " lycée " :
return nomlycee [ 5 : ]
else :
return nomlycee
def format_telephone ( n ) :
if n is None :
return " "
if len ( n ) < 7 :
return n
else :
n = n . replace ( " " , " " ) . replace ( " . " , " " )
i = 0
r = " "
j = len ( n ) - 1
while j > = 0 :
r = n [ j ] + r
if i % 2 == 1 and j != 0 :
r = " " + r
i + = 1
j - = 1
if len ( r ) == 13 and r [ 0 ] != " 0 " :
r = " 0 " + r
return r
def format_pays ( s ) :
" laisse le pays seulement si != FRANCE "
2021-08-21 00:24:51 +02:00
if s . upper ( ) != " FRANCE " :
2020-09-26 16:19:37 +02:00
return s
else :
return " "
PIVOT_YEAR = 70
def pivot_year ( y ) :
if y == " " or y is None :
return None
y = int ( round ( float ( y ) ) )
if y > = 0 and y < 100 :
if y < PIVOT_YEAR :
y = y + 2000
else :
y = y + 1900
return y
2021-06-19 23:21:37 +02:00
_identiteEditor = ndb . EditableTable (
2020-09-26 16:19:37 +02:00
" identite " ,
" etudid " ,
(
" etudid " ,
" nom " ,
" nom_usuel " ,
" prenom " ,
2021-02-13 17:28:55 +01:00
" civilite " , # 'M", "F", or "X"
2020-09-26 16:19:37 +02:00
" date_naissance " ,
" lieu_naissance " ,
" dept_naissance " ,
" nationalite " ,
" statut " ,
" boursier " ,
" foto " ,
" photo_filename " ,
" code_ine " ,
" code_nip " ,
2021-08-27 22:16:10 +02:00
" scodoc7_id " ,
2020-09-26 16:19:37 +02:00
) ,
2021-08-13 00:34:58 +02:00
filter_dept = True ,
2020-09-26 16:19:37 +02:00
sortkey = " nom " ,
input_formators = {
" nom " : force_uppercase ,
" prenom " : force_uppercase ,
2021-02-13 17:28:55 +01:00
" civilite " : input_civilite ,
2021-06-19 23:21:37 +02:00
" date_naissance " : ndb . DateDMYtoISO ,
2021-08-11 00:36:07 +02:00
" boursier " : bool ,
2020-09-26 16:19:37 +02:00
} ,
2021-06-19 23:21:37 +02:00
output_formators = { " date_naissance " : ndb . DateISOtoDMY } ,
2020-09-26 16:19:37 +02:00
convert_null_outputs_to_empty = True ,
2021-08-08 16:01:10 +02:00
# allow_set_id=True, # car on specifie le code Apogee a la creation #sco8
2020-09-26 16:19:37 +02:00
)
identite_delete = _identiteEditor . delete
def identite_list ( cnx , * a , * * kw ) :
2021-02-13 17:28:55 +01:00
""" List, adding on the fly ' annee_naissance ' and ' civilite_str ' (M., Mme, " " ). """
2020-09-26 16:19:37 +02:00
objs = _identiteEditor . list ( cnx , * a , * * kw )
for o in objs :
if o [ " date_naissance " ] :
o [ " annee_naissance " ] = int ( o [ " date_naissance " ] . split ( " / " ) [ 2 ] )
else :
o [ " annee_naissance " ] = o [ " date_naissance " ]
2021-02-13 17:28:55 +01:00
o [ " civilite_str " ] = format_civilite ( o [ " civilite " ] )
2020-09-26 16:19:37 +02:00
return objs
def identite_edit_nocheck ( cnx , args ) :
2020-10-12 23:16:41 +02:00
""" Modifie les champs mentionnes dans args, sans verification ni notification. """
2020-09-26 16:19:37 +02:00
_identiteEditor . edit ( cnx , args )
def check_nom_prenom ( cnx , nom = " " , prenom = " " , etudid = None ) :
""" Check if nom and prenom are valid.
2020-10-12 23:16:41 +02:00
Also check for duplicates ( homonyms ) , excluding etudid :
2020-09-26 16:19:37 +02:00
in general , homonyms are allowed , but it may be useful to generate a warning .
Returns :
True | False , NbHomonyms
"""
2021-01-17 22:31:28 +01:00
if not nom or ( not prenom and not scu . CONFIG . ALLOW_NULL_PRENOM ) :
2020-09-26 16:19:37 +02:00
return False , 0
2021-07-12 15:13:10 +02:00
nom = nom . lower ( ) . strip ( )
2020-09-26 16:19:37 +02:00
if prenom :
2021-07-12 15:13:10 +02:00
prenom = prenom . lower ( ) . strip ( )
2020-09-26 16:19:37 +02:00
# Don't allow some special cars (eg used in sql regexps)
2021-01-17 22:31:28 +01:00
if scu . FORBIDDEN_CHARS_EXP . search ( nom ) or scu . FORBIDDEN_CHARS_EXP . search ( prenom ) :
2020-09-26 16:19:37 +02:00
return False , 0
# Now count homonyms:
2021-06-19 23:21:37 +02:00
cursor = cnx . cursor ( cursor_factory = ndb . ScoDocCursor )
2021-08-10 17:12:10 +02:00
req = """ SELECT id
FROM identite
WHERE lower ( nom ) ~ % ( nom ) s
and lower ( prenom ) ~ % ( prenom ) s
"""
2020-09-26 16:19:37 +02:00
if etudid :
2021-08-10 17:12:10 +02:00
req + = " and id <> %(etudid)s "
2020-09-26 16:19:37 +02:00
cursor . execute ( req , { " nom " : nom , " prenom " : prenom , " etudid " : etudid } )
res = cursor . dictfetchall ( )
return True , len ( res )
2021-08-21 00:24:51 +02:00
def _check_duplicate_code (
cnx , args , code_name , disable_notify = False , edit = True , REQUEST = None
) :
2020-09-26 16:19:37 +02:00
etudid = args . get ( " etudid " , None )
if args . get ( code_name , None ) :
etuds = identite_list ( cnx , { code_name : str ( args [ code_name ] ) } )
# log('etuds=%s'%etuds)
nb_max = 0
if edit :
nb_max = 1
if len ( etuds ) > nb_max :
listh = [ ] # liste des doubles
for e in etuds :
listh . append (
2021-07-11 17:37:12 +02:00
""" Autre étudiant: <a href= " %s " > """
% url_for (
" scolar.ficheEtud " ,
scodoc_dept = g . scodoc_dept ,
etudid = e [ " etudid " ] ,
)
+ """ %(nom)s %(prenom)s </a> """ % e
2020-09-26 16:19:37 +02:00
)
if etudid :
OK = " retour à la fiche étudiant "
dest_url = " ficheEtud "
parameters = { " etudid " : etudid }
else :
2021-08-15 09:55:35 +02:00
if " tf_submitted " in args :
del args [ " tf_submitted " ]
2020-09-26 16:19:37 +02:00
OK = " Continuer "
dest_url = " etudident_create_form "
parameters = args
else :
OK = " Annuler "
dest_url = " "
parameters = { }
2021-08-21 00:24:51 +02:00
if not disable_notify :
2021-06-02 22:40:34 +02:00
err_page = scu . confirm_dialog (
2020-09-26 16:19:37 +02:00
message = """ <h3>Code étudiant ( %s ) dupliqué !</h3> """ % code_name ,
helpmsg = """ Le %s %s est déjà utilisé: un seul étudiant peut avoir ce code. Vérifier votre valeur ou supprimer l ' autre étudiant avec cette valeur.<p><ul><li> """
% ( code_name , args [ code_name ] )
+ " </li><li> " . join ( listh )
+ " </li></ul><p> " ,
OK = OK ,
dest_url = dest_url ,
parameters = parameters ,
)
else :
err_page = """ <h3>Code étudiant ( %s ) dupliqué !</h3> """ % code_name
log ( " *** error: code %s duplique: %s " % ( code_name , args [ code_name ] ) )
raise ScoGenError ( err_page )
2021-08-14 10:12:40 +02:00
def _check_civilite ( args ) :
civilite = args . get ( " civilite " , " X " ) or " X "
args [ " civilite " ] = input_civilite ( civilite ) # TODO: A faire valider
2021-08-21 00:24:51 +02:00
def identite_edit ( cnx , args , disable_notify = False , REQUEST = None ) :
2020-09-26 16:19:37 +02:00
""" Modifie l ' identite d ' un étudiant.
2021-08-21 00:24:51 +02:00
Si pref notification et difference , envoie message notification , sauf si disable_notify
2020-09-26 16:19:37 +02:00
"""
2021-08-21 00:24:51 +02:00
_check_duplicate_code (
cnx , args , " code_nip " , disable_notify = disable_notify , edit = True , REQUEST = REQUEST
)
_check_duplicate_code (
cnx , args , " code_ine " , disable_notify = disable_notify , edit = True , REQUEST = REQUEST
)
2020-09-26 16:19:37 +02:00
notify_to = None
2021-08-21 00:24:51 +02:00
if not disable_notify :
2020-09-26 16:19:37 +02:00
try :
2021-07-28 17:03:54 +02:00
notify_to = sco_preferences . get_preference ( " notify_etud_changes_to " )
2020-09-26 16:19:37 +02:00
except :
pass
if notify_to :
# etat AVANT edition pour envoyer diffs
before = identite_list ( cnx , { " etudid " : args [ " etudid " ] } ) [ 0 ]
identite_edit_nocheck ( cnx , args )
# Notification du changement par e-mail:
if notify_to :
2021-06-19 23:21:37 +02:00
etud = get_etud_info ( etudid = args [ " etudid " ] , filled = True ) [ 0 ]
2020-09-26 16:19:37 +02:00
after = identite_list ( cnx , { " etudid " : args [ " etudid " ] } ) [ 0 ]
notify_etud_change (
notify_to ,
etud ,
before ,
after ,
" Modification identite %(nomprenom)s " % etud ,
)
2021-08-21 00:24:51 +02:00
def identite_create ( cnx , args , REQUEST = None ) :
2020-09-26 16:19:37 +02:00
" check unique etudid, then create "
2021-08-21 00:24:51 +02:00
_check_duplicate_code ( cnx , args , " code_nip " , edit = False , REQUEST = REQUEST )
_check_duplicate_code ( cnx , args , " code_ine " , edit = False , REQUEST = REQUEST )
2021-08-14 10:12:40 +02:00
_check_civilite ( args )
2020-09-26 16:19:37 +02:00
2021-07-09 17:47:06 +02:00
if " etudid " in args :
2020-09-26 16:19:37 +02:00
etudid = args [ " etudid " ]
r = identite_list ( cnx , { " etudid " : etudid } )
if r :
raise ScoValueError (
" Code identifiant (etudid) déjà utilisé ! ( %s ) " % etudid
)
return _identiteEditor . create ( cnx , args )
2021-08-21 00:24:51 +02:00
def notify_etud_change ( email_addr , etud , before , after , subject ) :
2020-09-26 16:19:37 +02:00
""" Send email notifying changes to etud
before and after are two dicts , with values before and after the change .
"""
txt = [
" Code NIP: " + etud [ " code_nip " ] ,
2021-02-13 17:28:55 +01:00
" Civilité: " + etud [ " civilite_str " ] ,
2020-09-26 16:19:37 +02:00
" Nom: " + etud [ " nom " ] ,
" Prénom: " + etud [ " prenom " ] ,
2021-09-13 17:10:38 +02:00
" Etudid: " + str ( etud [ " etudid " ] ) ,
2020-09-26 16:19:37 +02:00
" \n " ,
" Changements effectués: " ,
]
n = 0
for key in after . keys ( ) :
if before [ key ] != after [ key ] :
txt . append ( ' %s : %s (auparavant: " %s " ) ' % ( key , after [ key ] , before [ key ] ) )
n + = 1
if not n :
return # pas de changements
txt = " \n " . join ( txt )
# build mail
log ( " notify_etud_change: sending notification to %s " % email_addr )
2020-10-12 23:16:41 +02:00
log ( " notify_etud_change: subject: %s " % subject )
log ( txt )
2021-08-26 23:43:54 +02:00
mail . send_email (
subject , sco_preferences . get_preference ( " email_from_addr " ) , [ email_addr ] , txt
)
2020-10-12 23:16:41 +02:00
return txt
2020-09-26 16:19:37 +02:00
# --------
# Note: la table adresse n'est pas dans dans la table "identite"
# car on prevoit plusieurs adresses par etudiant (ie domicile, entreprise)
2021-06-19 23:21:37 +02:00
_adresseEditor = ndb . EditableTable (
2020-09-26 16:19:37 +02:00
" adresse " ,
" adresse_id " ,
(
" adresse_id " ,
" etudid " ,
" email " ,
" emailperso " ,
" domicile " ,
" codepostaldomicile " ,
" villedomicile " ,
" paysdomicile " ,
" telephone " ,
" telephonemobile " ,
" fax " ,
" typeadresse " ,
" entreprise_id " ,
" description " ,
) ,
convert_null_outputs_to_empty = True ,
)
adresse_create = _adresseEditor . create
adresse_delete = _adresseEditor . delete
adresse_list = _adresseEditor . list
2021-08-21 00:24:51 +02:00
def adresse_edit ( cnx , args , disable_notify = False ) :
2020-09-26 16:19:37 +02:00
""" Modifie l ' adresse d ' un étudiant.
2021-08-21 00:24:51 +02:00
Si pref notification et difference , envoie message notification , sauf si disable_notify
2020-09-26 16:19:37 +02:00
"""
notify_to = None
2021-08-21 00:24:51 +02:00
if not disable_notify :
2020-09-26 16:19:37 +02:00
try :
2021-07-28 17:03:54 +02:00
notify_to = sco_preferences . get_preference ( " notify_etud_changes_to " )
2020-09-26 16:19:37 +02:00
except :
pass
if notify_to :
# etat AVANT edition pour envoyer diffs
before = adresse_list ( cnx , { " etudid " : args [ " etudid " ] } ) [ 0 ]
_adresseEditor . edit ( cnx , args )
# Notification du changement par e-mail:
if notify_to :
2021-06-19 23:21:37 +02:00
etud = get_etud_info ( etudid = args [ " etudid " ] , filled = True ) [ 0 ]
2020-09-26 16:19:37 +02:00
after = adresse_list ( cnx , { " etudid " : args [ " etudid " ] } ) [ 0 ]
notify_etud_change (
notify_to ,
etud ,
before ,
after ,
" Modification adresse %(nomprenom)s " % etud ,
)
def getEmail ( cnx , etudid ) :
" get email institutionnel etudiant (si plusieurs adresses, prend le premier non null "
adrs = adresse_list ( cnx , { " etudid " : etudid } )
for adr in adrs :
if adr [ " email " ] :
return adr [ " email " ]
return " "
# ---------
2021-06-19 23:21:37 +02:00
_admissionEditor = ndb . EditableTable (
2020-09-26 16:19:37 +02:00
" admissions " ,
" adm_id " ,
(
" adm_id " ,
" etudid " ,
" annee " ,
" bac " ,
" specialite " ,
" annee_bac " ,
" math " ,
" physique " ,
" anglais " ,
" francais " ,
" rang " ,
" qualite " ,
" rapporteur " ,
" decision " ,
" score " ,
" classement " ,
" apb_groupe " ,
" apb_classement_gr " ,
" commentaire " ,
" nomlycee " ,
" villelycee " ,
" codepostallycee " ,
" codelycee " ,
" debouche " ,
" type_admission " ,
" boursier_prec " ,
) ,
input_formators = {
" annee " : pivot_year ,
" bac " : force_uppercase ,
" specialite " : force_uppercase ,
" annee_bac " : pivot_year ,
2021-06-19 23:21:37 +02:00
" classement " : ndb . int_null_is_null ,
" apb_classement_gr " : ndb . int_null_is_null ,
2021-08-10 17:12:10 +02:00
" boursier_prec " : bool ,
2020-09-26 16:19:37 +02:00
} ,
2021-01-17 22:31:28 +01:00
output_formators = { " type_admission " : lambda x : x or scu . TYPE_ADMISSION_DEFAULT } ,
2020-09-26 16:19:37 +02:00
convert_null_outputs_to_empty = True ,
)
admission_create = _admissionEditor . create
admission_delete = _admissionEditor . delete
admission_list = _admissionEditor . list
admission_edit = _admissionEditor . edit
# Edition simultanee de identite et admission
2021-07-09 23:31:16 +02:00
class EtudIdentEditor ( object ) :
2021-08-21 00:24:51 +02:00
def create ( self , cnx , args , REQUEST = None ) :
etudid = identite_create ( cnx , args , REQUEST )
2020-09-26 16:19:37 +02:00
args [ " etudid " ] = etudid
admission_create ( cnx , args )
return etudid
def list ( self , * args , * * kw ) :
R = identite_list ( * args , * * kw )
Ra = admission_list ( * args , * * kw )
# print len(R), len(Ra)
# merge: add admission fields to identite
A = { }
for r in Ra :
A [ r [ " etudid " ] ] = r
res = [ ]
for i in R :
res . append ( i )
2021-07-09 17:47:06 +02:00
if i [ " etudid " ] in A :
2020-09-26 16:19:37 +02:00
# merge
res [ - 1 ] . update ( A [ i [ " etudid " ] ] )
else : # pas d'etudiant trouve
# print "*** pas d'info admission pour %s" % str(i)
void_adm = {
k : None
2021-01-17 22:31:28 +01:00
for k in _admissionEditor . dbfields
2020-09-26 16:19:37 +02:00
if k != " etudid " and k != " adm_id "
}
res [ - 1 ] . update ( void_adm )
# tri par nom
2021-07-09 23:19:30 +02:00
res . sort ( key = itemgetter ( " nom " , " prenom " ) )
2020-09-26 16:19:37 +02:00
return res
2021-08-21 00:24:51 +02:00
def edit ( self , cnx , args , disable_notify = False , REQUEST = None ) :
identite_edit ( cnx , args , disable_notify = disable_notify , REQUEST = REQUEST )
2020-09-26 16:19:37 +02:00
if " adm_id " in args : # safety net
admission_edit ( cnx , args )
_etudidentEditor = EtudIdentEditor ( )
etudident_list = _etudidentEditor . list
etudident_edit = _etudidentEditor . edit
etudident_create = _etudidentEditor . create
2021-08-22 13:24:36 +02:00
def make_etud_args ( etudid = None , code_nip = None , use_request = True , raise_exc = True ) :
2020-09-26 16:19:37 +02:00
""" forme args dict pour requete recherche etudiant
On peut specifier etudid
2021-08-22 13:24:36 +02:00
ou bien ( si use_request ) cherche dans la requete http : etudid , code_nip , code_ine
2020-09-26 16:19:37 +02:00
( dans cet ordre ) .
"""
args = None
if etudid :
args = { " etudid " : etudid }
elif code_nip :
args = { " code_nip " : code_nip }
2021-08-22 13:24:36 +02:00
elif use_request : # use form from current request (Flask global)
if request . method == " POST " :
vals = request . form
elif request . method == " GET " :
vals = request . args
else :
vals = { }
if " etudid " in vals :
args = { " etudid " : int ( vals [ " etudid " ] ) }
elif " code_nip " in vals :
args = { " code_nip " : str ( vals [ " code_nip " ] ) }
elif " code_ine " in vals :
args = { " code_ine " : str ( vals [ " code_ine " ] ) }
2020-09-26 16:19:37 +02:00
if not args and raise_exc :
raise ValueError ( " getEtudInfo: no parameter ! " )
return args
2021-08-27 22:16:10 +02:00
def get_etud_info ( etudid = False , code_nip = False , filled = False ) - > list :
""" infos sur un etudiant (API). If not foud, returns empty list.
On peut specifier etudid ou code_nip
2021-06-14 00:23:22 +02:00
ou bien cherche dans REQUEST . form : etudid , code_nip , code_ine
( dans cet ordre ) .
"""
if etudid is None :
return [ ]
2021-06-15 13:59:56 +02:00
cnx = ndb . GetDBConnexion ( )
2021-08-22 13:24:36 +02:00
args = make_etud_args ( etudid = etudid , code_nip = code_nip )
2021-06-19 23:21:37 +02:00
etud = etudident_list ( cnx , args = args )
2021-06-14 00:23:22 +02:00
if filled :
2021-06-19 23:21:37 +02:00
fill_etuds_info ( etud )
2021-06-14 00:23:22 +02:00
return etud
2021-08-21 00:24:51 +02:00
def create_etud ( cnx , args = { } , REQUEST = None ) :
2020-12-26 00:11:55 +01:00
""" Creation d ' un étudiant. génère aussi évenement et " news " .
Args :
args : dict avec les attributs de l ' étudiant
Returns :
etud , l ' étudiant créé.
"""
2021-06-19 23:21:37 +02:00
from app . scodoc import sco_news
2020-12-26 00:11:55 +01:00
# creation d'un etudiant
2021-08-21 00:24:51 +02:00
etudid = etudident_create ( cnx , args , REQUEST = REQUEST )
2020-12-26 00:11:55 +01:00
# crée une adresse vide (chaque etudiant doit etre dans la table "adresse" !)
_ = adresse_create (
cnx ,
{
" etudid " : etudid ,
" typeadresse " : " domicile " ,
" description " : " (creation individuelle) " ,
} ,
)
# event
scolar_events_create (
cnx ,
args = {
" etudid " : etudid ,
" event_date " : time . strftime ( " %d / % m/ % Y " ) ,
" formsemestre_id " : None ,
" event_type " : " CREATION " ,
} ,
)
# log
logdb (
cnx ,
method = " etudident_edit_form " ,
etudid = etudid ,
msg = " creation initiale " ,
)
2021-01-17 22:31:28 +01:00
etud = etudident_list ( cnx , { " etudid " : etudid } ) [ 0 ]
2021-06-19 23:21:37 +02:00
fill_etuds_info ( [ etud ] )
2021-07-11 17:37:12 +02:00
etud [ " url " ] = url_for ( " scolar.ficheEtud " , scodoc_dept = g . scodoc_dept , etudid = etudid )
2020-12-26 00:11:55 +01:00
sco_news . add (
typ = sco_news . NEWS_INSCR ,
object = None , # pas d'object pour ne montrer qu'un etudiant
text = ' Nouvel étudiant <a href= " %(url)s " > %(nomprenom)s </a> ' % etud ,
url = etud [ " url " ] ,
)
return etud
2020-09-26 16:19:37 +02:00
# ---------- "EVENTS"
2021-06-19 23:21:37 +02:00
_scolar_eventsEditor = ndb . EditableTable (
2020-09-26 16:19:37 +02:00
" scolar_events " ,
" event_id " ,
(
" event_id " ,
" etudid " ,
" event_date " ,
" formsemestre_id " ,
" ue_id " ,
" event_type " ,
" comp_formsemestre_id " ,
) ,
sortkey = " event_date " ,
convert_null_outputs_to_empty = True ,
2021-06-19 23:21:37 +02:00
output_formators = { " event_date " : ndb . DateISOtoDMY } ,
input_formators = { " event_date " : ndb . DateDMYtoISO } ,
2020-09-26 16:19:37 +02:00
)
# scolar_events_create = _scolar_eventsEditor.create
scolar_events_delete = _scolar_eventsEditor . delete
scolar_events_list = _scolar_eventsEditor . list
scolar_events_edit = _scolar_eventsEditor . edit
def scolar_events_create ( cnx , args ) :
# several "events" may share the same values
2021-08-01 16:33:09 +02:00
_scolar_eventsEditor . create ( cnx , args )
2020-09-26 16:19:37 +02:00
# --------
2021-06-19 23:21:37 +02:00
_etud_annotationsEditor = ndb . EditableTable (
2020-09-26 16:19:37 +02:00
" etud_annotations " ,
" id " ,
(
" id " ,
" date " ,
" etudid " ,
" author " ,
" comment " ,
2021-08-10 17:12:10 +02:00
" author " ,
2020-09-26 16:19:37 +02:00
) ,
sortkey = " date desc " ,
convert_null_outputs_to_empty = True ,
2021-07-11 22:00:41 +02:00
output_formators = { " comment " : safehtml . html_to_safe_html , " date " : ndb . DateISOtoDMY } ,
2020-09-26 16:19:37 +02:00
)
etud_annotations_create = _etud_annotationsEditor . create
etud_annotations_delete = _etud_annotationsEditor . delete
etud_annotations_list = _etud_annotationsEditor . list
etud_annotations_edit = _etud_annotationsEditor . edit
2021-08-21 00:24:51 +02:00
def add_annotations_to_etud_list ( etuds ) :
2020-09-26 16:19:37 +02:00
""" Add key ' annotations ' describing annotations of etuds
( used to list all annotations of a group )
"""
2021-06-15 13:59:56 +02:00
cnx = ndb . GetDBConnexion ( )
2020-09-26 16:19:37 +02:00
for etud in etuds :
l = [ ]
for a in etud_annotations_list ( cnx , args = { " etudid " : etud [ " etudid " ] } ) :
l . append ( " %(comment)s ( %(date)s ) " % a )
etud [ " annotations_str " ] = " , " . join ( l )
# -------- APPRECIATIONS (sur bulletins) -------------------
# Les appreciations sont dans la table postgres notes_appreciations
2021-06-19 23:21:37 +02:00
_appreciationsEditor = ndb . EditableTable (
2020-09-26 16:19:37 +02:00
" notes_appreciations " ,
" id " ,
(
" id " ,
" date " ,
" etudid " ,
" formsemestre_id " ,
" author " ,
" comment " ,
2021-08-10 17:12:10 +02:00
" author " ,
2020-09-26 16:19:37 +02:00
) ,
sortkey = " date desc " ,
convert_null_outputs_to_empty = True ,
2021-07-11 22:00:41 +02:00
output_formators = { " comment " : safehtml . html_to_safe_html , " date " : ndb . DateISOtoDMY } ,
2020-09-26 16:19:37 +02:00
)
appreciations_create = _appreciationsEditor . create
appreciations_delete = _appreciationsEditor . delete
appreciations_list = _appreciationsEditor . list
appreciations_edit = _appreciationsEditor . edit
# -------- Noms des Lycées à partir du code
def read_etablissements ( ) :
2021-07-25 09:51:09 +02:00
filename = os . path . join ( scu . SCO_TOOLS_DIR , scu . CONFIG . ETABL_FILENAME )
2020-09-26 16:19:37 +02:00
log ( " reading %s " % filename )
f = open ( filename )
L = [ x [ : - 1 ] . split ( " ; " ) for x in f ]
E = { }
for l in L [ 1 : ] :
E [ l [ 0 ] ] = {
" name " : l [ 1 ] ,
" address " : l [ 2 ] ,
" codepostal " : l [ 3 ] ,
" commune " : l [ 4 ] ,
" position " : l [ 5 ] + " , " + l [ 6 ] ,
}
return E
ETABLISSEMENTS = None
def get_etablissements ( ) :
global ETABLISSEMENTS
if ETABLISSEMENTS is None :
ETABLISSEMENTS = read_etablissements ( )
return ETABLISSEMENTS
def get_lycee_infos ( codelycee ) :
E = get_etablissements ( )
return E . get ( codelycee , None )
def format_lycee_from_code ( codelycee ) :
" Description lycee à partir du code "
E = get_etablissements ( )
if codelycee in E :
e = E [ codelycee ]
nomlycee = e [ " name " ]
return " %s ( %s ) " % ( nomlycee , e [ " commune " ] )
else :
return " %s (établissement inconnu) " % codelycee
def etud_add_lycee_infos ( etud ) :
""" Si codelycee est renseigné, ajout les champs au dict """
if etud [ " codelycee " ] :
il = get_lycee_infos ( etud [ " codelycee " ] )
if il :
if not etud [ " codepostallycee " ] :
etud [ " codepostallycee " ] = il [ " codepostal " ]
if not etud [ " nomlycee " ] :
etud [ " nomlycee " ] = il [ " name " ]
if not etud [ " villelycee " ] :
etud [ " villelycee " ] = il [ " commune " ]
if not etud . get ( " positionlycee " , None ) :
if il [ " position " ] != " 0.0,0.0 " :
etud [ " positionlycee " ] = il [ " position " ]
return etud
""" Conversion fichier original:
f = open ( ' etablissements.csv ' )
o = open ( ' etablissements2.csv ' , ' w ' )
o . write ( f . readline ( ) )
for l in f :
fs = l . split ( ' ; ' )
2021-08-21 00:24:51 +02:00
nom = ' ' . join ( [ x . capitalize ( ) for x in fs [ 1 ] . split ( ) ] )
adr = ' ' . join ( [ x . capitalize ( ) for x in fs [ 2 ] . split ( ) ] )
ville = ' ' . join ( [ x . capitalize ( ) for x in fs [ 4 ] . split ( ) ] )
2020-09-26 16:19:37 +02:00
o . write ( ' %s ; %s ; %s ; %s ; %s \n ' % ( fs [ 0 ] , nom , adr , fs [ 3 ] , ville ) )
o . close ( )
"""
2021-06-02 22:40:34 +02:00
2021-08-21 00:24:51 +02:00
def list_scolog ( etudid ) :
2021-06-02 22:40:34 +02:00
" liste des operations effectuees sur cet etudiant "
2021-06-15 13:59:56 +02:00
cnx = ndb . GetDBConnexion ( )
2021-06-02 22:40:34 +02:00
cursor = cnx . cursor ( cursor_factory = ndb . ScoDocCursor )
cursor . execute (
" select * from scolog where etudid= %(etudid)s ORDER BY DATE DESC " ,
{ " etudid " : etudid } ,
)
return cursor . dictfetchall ( )
2021-06-19 23:21:37 +02:00
def fill_etuds_info ( etuds ) :
2021-06-02 22:40:34 +02:00
""" etuds est une liste d ' etudiants (mappings)
Pour chaque etudiant , ajoute ou formatte les champs
- > informations pour fiche etudiant ou listes diverses
"""
2021-06-21 10:17:16 +02:00
from app . scodoc import sco_formsemestre
2021-06-19 23:21:37 +02:00
from app . scodoc import sco_formsemestre_inscriptions
2021-06-15 13:59:56 +02:00
cnx = ndb . GetDBConnexion ( )
2021-06-02 22:40:34 +02:00
# open('/tmp/t','w').write( str(etuds) )
for etud in etuds :
etudid = etud [ " etudid " ]
2021-08-21 15:17:14 +02:00
etud [ " dept " ] = g . scodoc_dept
2021-06-02 22:40:34 +02:00
adrs = adresse_list ( cnx , { " etudid " : etudid } )
if not adrs :
# certains "vieux" etudiants n'ont pas d'adresse
adr = { } . fromkeys ( _adresseEditor . dbfields , " " )
adr [ " etudid " ] = etudid
else :
adr = adrs [ 0 ]
if len ( adrs ) > 1 :
2021-06-19 23:21:37 +02:00
log ( " fill_etuds_info: etudid= %s a %d adresses " % ( etudid , len ( adrs ) ) )
2021-06-02 22:40:34 +02:00
etud . update ( adr )
format_etud_ident ( etud )
# Semestres dans lesquel il est inscrit
2021-06-19 23:21:37 +02:00
ins = sco_formsemestre_inscriptions . do_formsemestre_inscription_list (
2021-08-19 10:28:35 +02:00
{ " etudid " : etudid }
2021-06-19 23:21:37 +02:00
)
2021-06-02 22:40:34 +02:00
etud [ " ins " ] = ins
sems = [ ]
cursem = None # semestre "courant" ou il est inscrit
for i in ins :
2021-08-19 10:28:35 +02:00
sem = sco_formsemestre . get_formsemestre ( i [ " formsemestre_id " ] )
2021-08-21 00:24:51 +02:00
if sco_formsemestre . sem_est_courant ( sem ) :
2021-06-02 22:40:34 +02:00
cursem = sem
curi = i
sem [ " ins " ] = i
sems . append ( sem )
# trie les semestres par date de debut, le plus recent d'abord
# (important, ne pas changer (suivi cohortes))
2021-07-09 23:19:30 +02:00
sems . sort ( key = itemgetter ( " dateord " ) , reverse = True )
2021-06-02 22:40:34 +02:00
etud [ " sems " ] = sems
etud [ " cursem " ] = cursem
if cursem :
etud [ " inscription " ] = cursem [ " titremois " ]
etud [ " inscriptionstr " ] = " Inscrit en " + cursem [ " titremois " ]
etud [ " inscription_formsemestre_id " ] = cursem [ " formsemestre_id " ]
etud [ " etatincursem " ] = curi [ " etat " ]
2021-08-21 00:24:51 +02:00
etud [ " situation " ] = descr_situation_etud ( etudid , etud [ " ne " ] )
2021-08-19 10:28:35 +02:00
# XXX est-ce utile ? sco_groups.etud_add_group_infos( etud, cursem)
2021-06-02 22:40:34 +02:00
else :
if etud [ " sems " ] :
if etud [ " sems " ] [ 0 ] [ " dateord " ] > time . strftime (
" % Y- % m- %d " , time . localtime ( )
) :
etud [ " inscription " ] = " futur "
etud [ " situation " ] = " futur élève "
else :
etud [ " inscription " ] = " ancien "
etud [ " situation " ] = " ancien élève "
else :
etud [ " inscription " ] = " non inscrit "
etud [ " situation " ] = etud [ " inscription " ]
etud [ " inscriptionstr " ] = etud [ " inscription " ]
etud [ " inscription_formsemestre_id " ] = None
# XXXetud['partitions'] = {} # ne va pas chercher les groupes des anciens semestres
etud [ " etatincursem " ] = " ? "
# nettoyage champs souvents vides
if etud [ " nomlycee " ] :
etud [ " ilycee " ] = " Lycée " + format_lycee ( etud [ " nomlycee " ] )
if etud [ " villelycee " ] :
etud [ " ilycee " ] + = " ( %s ) " % etud [ " villelycee " ]
etud [ " ilycee " ] + = " <br/> "
else :
if etud [ " codelycee " ] :
etud [ " ilycee " ] = format_lycee_from_code ( etud [ " codelycee " ] )
else :
etud [ " ilycee " ] = " "
rap = " "
if etud [ " rapporteur " ] or etud [ " commentaire " ] :
rap = " Note du rapporteur "
if etud [ " rapporteur " ] :
rap + = " ( %s ) " % etud [ " rapporteur " ]
rap + = " : "
if etud [ " commentaire " ] :
rap + = " <em> %s </em> " % etud [ " commentaire " ]
etud [ " rap " ] = rap
# if etud['boursier_prec']:
# pass
if etud [ " telephone " ] :
etud [ " telephonestr " ] = " <b>Tél.:</b> " + format_telephone ( etud [ " telephone " ] )
else :
etud [ " telephonestr " ] = " "
if etud [ " telephonemobile " ] :
etud [ " telephonemobilestr " ] = " <b>Mobile:</b> " + format_telephone (
etud [ " telephonemobile " ]
)
else :
etud [ " telephonemobilestr " ] = " "
2021-08-21 00:24:51 +02:00
def descr_situation_etud ( etudid , ne = " " ) :
2021-06-02 22:40:34 +02:00
""" chaine decrivant la situation actuelle de l ' etudiant """
2021-06-21 10:17:16 +02:00
from app . scodoc import sco_formsemestre
2021-06-15 13:59:56 +02:00
cnx = ndb . GetDBConnexion ( )
2021-06-02 22:40:34 +02:00
cursor = cnx . cursor ( cursor_factory = ndb . ScoDocCursor )
cursor . execute (
2021-08-11 13:01:37 +02:00
""" SELECT I.formsemestre_id, I.etat
FROM notes_formsemestre_inscription I , notes_formsemestre S
WHERE etudid = % ( etudid ) s
and S . id = I . formsemestre_id
and date_debut < now ( )
and date_fin > now ( )
ORDER BY S . date_debut DESC ; """ ,
2021-06-02 22:40:34 +02:00
{ " etudid " : etudid } ,
)
r = cursor . dictfetchone ( )
if not r :
situation = " non inscrit "
else :
2021-08-19 10:28:35 +02:00
sem = sco_formsemestre . get_formsemestre ( r [ " formsemestre_id " ] )
2021-06-02 22:40:34 +02:00
if r [ " etat " ] == " I " :
situation = " inscrit %s en %s " % ( ne , sem [ " titremois " ] )
# Cherche la date d'inscription dans scolar_events:
2021-06-19 23:21:37 +02:00
events = scolar_events_list (
2021-06-02 22:40:34 +02:00
cnx ,
args = {
" etudid " : etudid ,
" formsemestre_id " : sem [ " formsemestre_id " ] ,
" event_type " : " INSCRIPTION " ,
} ,
)
if not events :
log (
" *** situation inconsistante pour %s (inscrit mais pas d ' event) "
% etudid
)
date_ins = " ??? " # ???
else :
date_ins = events [ 0 ] [ " event_date " ]
situation + = " le " + str ( date_ins )
else :
situation = " démission de %s " % sem [ " titremois " ]
# Cherche la date de demission dans scolar_events:
2021-06-19 23:21:37 +02:00
events = scolar_events_list (
2021-06-02 22:40:34 +02:00
cnx ,
args = {
" etudid " : etudid ,
" formsemestre_id " : sem [ " formsemestre_id " ] ,
" event_type " : " DEMISSION " ,
} ,
)
if not events :
log (
" *** situation inconsistante pour %s (demission mais pas d ' event) "
% etudid
)
date_dem = " ??? " # ???
else :
date_dem = events [ 0 ] [ " event_date " ]
situation + = " le " + str ( date_dem )
2021-08-14 10:12:40 +02:00
return situation