2020-09-26 16:19:37 +02:00
# -*- 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
#
##############################################################################
""" Rapports suivi:
- statistiques decisions
- suivi cohortes
"""
from mx . DateTime import DateTime as mxDateTime
import mx . DateTime
import tempfile , urllib , re
from notesdb import *
from sco_utils import *
from notes_log import log
from gen_tables import GenTable
import sco_excel , sco_pdf
import sco_codes_parcours
from sco_codes_parcours import code_semestre_validant
import sco_parcours_dut
import sco_formsemestre
import sco_formsemestre_status
from sco_pdf import SU
MAX_ETUD_IN_DESCR = 20
def formsemestre_etuds_stats ( context , sem , only_primo = False ) :
2020-11-30 00:11:39 +01:00
""" Récupère liste d ' etudiants avec etat et decision. """
2020-09-26 16:19:37 +02:00
nt = context . _getNotesCache ( ) . get_NotesTable (
context , sem [ " formsemestre_id " ]
) # > get_table_moyennes_triees, identdict, get_etud_decision_sem, get_etud_etat,
T = nt . get_table_moyennes_triees ( )
# Construit liste d'étudiants du semestre avec leur decision
etuds = [ ]
for t in T :
etudid = t [ - 1 ]
etud = context . getEtudInfo ( etudid = etudid , filled = True ) [ 0 ]
decision = nt . get_etud_decision_sem ( etudid )
if decision :
etud [ " codedecision " ] = decision [ " code " ]
etud [ " etat " ] = nt . get_etud_etat ( etudid )
if etud [ " etat " ] == " D " :
etud [ " codedecision " ] = " DEM "
if not etud . has_key ( " codedecision " ) :
etud [ " codedecision " ] = " (nd) " # pas de decision jury
# Ajout devenir (autorisations inscriptions), utile pour stats passage
aut_list = sco_parcours_dut . formsemestre_get_autorisation_inscription (
context , etudid , sem [ " formsemestre_id " ]
)
autorisations = [ " S %s " % x [ " semestre_id " ] for x in aut_list ]
autorisations . sort ( )
autorisations_str = " , " . join ( autorisations )
etud [ " devenir " ] = autorisations_str
# Ajout clé 'bac-specialite'
bs = [ ]
if etud [ " bac " ] :
bs . append ( etud [ " bac " ] )
if etud [ " specialite " ] :
bs . append ( etud [ " specialite " ] )
etud [ " bac-specialite " ] = " " . join ( bs )
#
if ( not only_primo ) or context . isPrimoEtud ( etud , sem ) :
etuds . append ( etud )
return etuds
def _categories_and_results ( etuds , category , result ) :
categories = { }
results = { }
for etud in etuds :
categories [ etud [ category ] ] = True
results [ etud [ result ] ] = True
categories = categories . keys ( )
categories . sort ( )
results = results . keys ( )
results . sort ( )
return categories , results
def _results_by_category (
etuds ,
category = " " ,
result = " " ,
category_name = None ,
context = None ,
formsemestre_id = None ,
) :
""" Construit table: categories (eg types de bacs) en ligne, décisions jury en colonnes
etuds est une liste d ' etuds (dicts).
category et result sont des clés de etud ( category définie les lignes , result les colonnes ) .
Retourne une table .
"""
if category_name is None :
category_name = category
# types de bacs differents:
categories , results = _categories_and_results ( etuds , category , result )
#
Count = { } # { bac : { decision : nb_avec_ce_bac_et_ce_code } }
results = { } # { result_value : True }
for etud in etuds :
results [ etud [ result ] ] = True
if Count . has_key ( etud [ category ] ) :
Count [ etud [ category ] ] [ etud [ result ] ] + = 1
else :
Count [ etud [ category ] ] = DictDefault ( kv_dict = { etud [ result ] : 1 } )
# conversion en liste de dict
C = [ Count [ cat ] for cat in categories ]
# Totaux par lignes et colonnes
tot = 0
for l in [ Count [ cat ] for cat in categories ] :
l [ " sum " ] = sum ( l . values ( ) )
tot + = l [ " sum " ]
# pourcentages sur chaque total de ligne
for l in C :
l [ " sumpercent " ] = " %2.1f %% " % ( ( 100.0 * l [ " sum " ] ) / tot )
#
codes = results . keys ( )
codes . sort ( )
bottom_titles = [ ]
if C : # ligne du bas avec totaux:
bottom_titles = { }
for code in codes :
bottom_titles [ code ] = sum ( [ l [ code ] for l in C ] )
bottom_titles [ " sum " ] = tot
bottom_titles [ " sumpercent " ] = " 100 % "
bottom_titles [ " row_title " ] = " Total "
# ajout titre ligne:
for ( cat , l ) in zip ( categories , C ) :
l [ " row_title " ] = cat or " ? "
#
codes . append ( " sum " )
codes . append ( " sumpercent " )
# on veut { ADM : ADM, ... }, était peu elegant en python 2.3:
# titles = {}
# map( lambda x,titles=titles: titles.__setitem__(x[0],x[1]), zip(codes,codes) )
# Version moderne:
titles = { x : x for x in codes }
titles [ " sum " ] = " Total "
titles [ " sumpercent " ] = " % "
titles [ " DEM " ] = " Dém. " # démissions
titles [ " row_title " ] = category_name
return GenTable (
titles = titles ,
columns_ids = codes ,
rows = C ,
bottom_titles = bottom_titles ,
html_col_width = " 4em " ,
html_sortable = True ,
preferences = context . get_preferences ( formsemestre_id ) ,
)
# pages
def formsemestre_report (
context ,
formsemestre_id ,
etuds ,
REQUEST = None ,
category = " bac " ,
result = " codedecision " ,
category_name = " " ,
result_name = " " ,
title = " Statistiques " ,
only_primo = None ,
) :
"""
Tableau sur résultats ( result ) par type de category bac
"""
sem = sco_formsemestre . get_formsemestre ( context , formsemestre_id )
if not category_name :
category_name = category
if not result_name :
result_name = result
if result_name == " codedecision " :
result_name = " résultats "
#
tab = _results_by_category (
etuds ,
category = category ,
category_name = category_name ,
result = result ,
context = context ,
formsemestre_id = formsemestre_id ,
)
#
tab . filename = make_filename ( " stats " + sem [ " titreannee " ] )
tab . origin = " Généré par %s le " % VERSION . SCONAME + timedate_human_repr ( ) + " "
tab . caption = " Répartition des résultats par %s , semestre %s " % (
category_name ,
sem [ " titreannee " ] ,
)
tab . html_caption = " Répartition des résultats par %s . " % category_name
tab . base_url = " %s ?formsemestre_id= %s " % ( REQUEST . URL0 , formsemestre_id )
if only_primo :
tab . base_url + = " &only_primo=on "
return tab
# def formsemestre_report_bacs(context, formsemestre_id, format='html', REQUEST=None):
# """
# Tableau sur résultats par type de bac
# """
# sem = sco_formsemestre.get_formsemestre(context, formsemestre_id)
# title = 'Statistiques bacs ' + sem['titreannee']
# etuds = formsemestre_etuds_stats(context, sem)
# tab = formsemestre_report(context, formsemestre_id, etuds, REQUEST=REQUEST,
# category='bac', result='codedecision',
# category_name='Bac',
# title=title)
# return tab.make_page(
# context,
# title = """<h2>Résultats de <a href="formsemestre_status?formsemestre_id=%(formsemestre_id)s">%(titreannee)s</a></h2>""" % sem,
# format=format, page_title = title, REQUEST=REQUEST )
def formsemestre_report_counts (
context ,
formsemestre_id ,
format = " html " ,
REQUEST = None ,
category = " bac " ,
result = " codedecision " ,
allkeys = False ,
only_primo = False ,
) :
"""
Tableau comptage avec choix des categories
"""
sem = sco_formsemestre . get_formsemestre ( context , formsemestre_id )
category_name = strcapitalize ( category )
title = " Comptages " + category_name
etuds = formsemestre_etuds_stats ( context , sem , only_primo = only_primo )
tab = formsemestre_report (
context ,
formsemestre_id ,
etuds ,
REQUEST = REQUEST ,
category = category ,
result = result ,
category_name = category_name ,
title = title ,
only_primo = only_primo ,
)
if not etuds :
F = [ """ <p><em>Aucun étudiant</em></p> """ ]
else :
if allkeys :
keys = etuds [ 0 ] . keys ( )
else :
# clés présentées à l'utilisateur:
keys = [
" annee_bac " ,
" annee_naissance " ,
" bac " ,
" specialite " ,
" bac-specialite " ,
" codedecision " ,
" devenir " ,
" etat " ,
" sexe " ,
" qualite " ,
" villelycee " ,
" statut " ,
" type_admission " ,
" boursier_prec " ,
]
keys . sort ( )
F = [
""" <form name= " f " method= " get " action= " %s " ><p>
Colonnes : < select name = " result " onchange = " document.f.submit() " > """
% REQUEST . URL0
]
for k in keys :
if k == result :
selected = " selected "
else :
selected = " "
F . append ( ' <option value= " %s " %s > %s </option> ' % ( k , selected , k ) )
F . append ( " </select> " )
F . append ( ' Lignes: <select name= " category " onchange= " document.f.submit() " > ' )
for k in keys :
if k == category :
selected = " selected "
else :
selected = " "
F . append ( ' <option value= " %s " %s > %s </option> ' % ( k , selected , k ) )
F . append ( " </select> " )
if only_primo :
checked = ' checked= " 1 " '
else :
checked = " "
F . append (
' <br/><input type= " checkbox " name= " only_primo " onchange= " document.f.submit() " %s >Restreindre aux primo-entrants</input> '
% checked
)
F . append (
' <input type= " hidden " name= " formsemestre_id " value= " %s " /> ' % formsemestre_id
)
F . append ( " </p></form> " )
t = tab . make_page (
context ,
title = """ <h2 class= " formsemestre " >Comptes croisés</h2> """ ,
format = format ,
REQUEST = REQUEST ,
with_html_headers = False ,
)
if format != " html " :
return t
H = [
context . sco_header ( REQUEST , page_title = title ) ,
t ,
" \n " . join ( F ) ,
""" <p class= " help " >Le tableau affiche le nombre d ' étudiants de ce semestre dans chacun
des cas choisis : à l ' aide des deux menus, vous pouvez choisir les catégories utilisées
pour les lignes et les colonnes . Le < tt > codedecision < / tt > est le code de la décision
du jury .
< / p > """ ,
context . sco_footer ( REQUEST ) ,
]
return " \n " . join ( H )
# --------------------------------------------------------------------------
def table_suivi_cohorte (
context ,
formsemestre_id ,
percent = False ,
bac = " " , # selection sur type de bac
bacspecialite = " " ,
2020-11-30 00:11:39 +01:00
annee_bac = " " ,
2020-09-26 16:19:37 +02:00
sexe = " " ,
statut = " " ,
only_primo = False ,
) :
"""
2020-11-30 00:11:39 +01:00
Tableau indiquant le nombre d ' etudiants de la cohorte dans chaque état:
2020-09-26 16:19:37 +02:00
Etat date_debut_Sn date1 date2 . . .
S_n #inscrits en Sn
S_n + 1
. . .
S_last
Diplome
Sorties
Determination des dates : on regroupe les semestres commençant à des dates proches
"""
sem = sco_formsemestre . get_formsemestre (
context , formsemestre_id
) # sem est le semestre origine
t0 = time . time ( )
def logt ( op ) :
if 0 : # debug, set to 0 in production
log ( " %s : %s " % ( op , time . time ( ) - t0 ) )
logt ( " table_suivi_cohorte: start " )
# 1-- Liste des semestres posterieurs dans lesquels ont été les etudiants de sem
nt = context . _getNotesCache ( ) . get_NotesTable (
context , formsemestre_id
) # > get_etudids, get_etud_decision_sem
etudids = nt . get_etudids ( )
logt ( " A: orig etuds set " )
S = { formsemestre_id : sem } # ensemble de formsemestre_id
orig_set = Set ( ) # ensemble d'etudid du semestre d'origine
bacs = Set ( )
bacspecialites = Set ( )
2020-11-30 00:11:39 +01:00
annee_bacs = Set ( )
2020-09-26 16:19:37 +02:00
sexes = Set ( )
statuts = Set ( )
for etudid in etudids :
etud = context . getEtudInfo ( etudid = etudid , filled = True ) [ 0 ]
bacspe = etud [ " bac " ] + " / " + etud [ " specialite " ]
# sélection sur bac:
if (
( not bac or ( bac == etud [ " bac " ] ) )
and ( not bacspecialite or ( bacspecialite == bacspe ) )
2020-11-30 00:11:39 +01:00
and ( not annee_bac or ( annee_bac == str ( etud [ " annee_bac " ] ) ) )
2020-09-26 16:19:37 +02:00
and ( not sexe or ( sexe == etud [ " sexe " ] ) )
and ( not statut or ( statut == etud [ " statut " ] ) )
and ( not only_primo or context . isPrimoEtud ( etud , sem ) )
) :
orig_set . add ( etudid )
# semestres suivants:
for s in etud [ " sems " ] :
if DateDMYtoISO ( s [ " date_debut " ] ) > DateDMYtoISO ( sem [ " date_debut " ] ) :
S [ s [ " formsemestre_id " ] ] = s
bacs . add ( etud [ " bac " ] )
bacspecialites . add ( bacspe )
2020-11-30 00:11:39 +01:00
annee_bacs . add ( etud [ " annee_bac " ] )
2020-09-26 16:19:37 +02:00
sexes . add ( etud [ " sexe " ] )
if etud [ " statut " ] : # ne montre pas les statuts non renseignés
statuts . add ( etud [ " statut " ] )
sems = S . values ( )
# tri les semestres par date de debut
for s in sems :
d , m , y = [ int ( x ) for x in s [ " date_debut " ] . split ( " / " ) ]
s [ " date_debut_mx " ] = mxDateTime ( y , m , d )
sems . sort ( lambda x , y : cmp ( x [ " date_debut_mx " ] , y [ " date_debut_mx " ] ) )
# 2-- Pour chaque semestre, trouve l'ensemble des etudiants venant de sem
logt ( " B: etuds sets " )
sem [ " members " ] = orig_set
for s in sems :
ins = context . do_formsemestre_inscription_list (
args = { " formsemestre_id " : s [ " formsemestre_id " ] }
2020-12-01 16:46:45 +01:00
) # sans dems
2020-09-26 16:19:37 +02:00
inset = Set ( [ i [ " etudid " ] for i in ins ] )
s [ " members " ] = orig_set . intersection ( inset )
nb_dipl = 0 # combien de diplomes dans ce semestre ?
if s [ " semestre_id " ] == nt . parcours . NB_SEM :
nt = context . _getNotesCache ( ) . get_NotesTable (
context , s [ " formsemestre_id " ]
) # > get_etud_decision_sem
for etudid in s [ " members " ] :
dec = nt . get_etud_decision_sem ( etudid )
if dec and code_semestre_validant ( dec [ " code " ] ) :
nb_dipl + = 1
s [ " nb_dipl " ] = nb_dipl
# 3-- Regroupe les semestres par date de debut
P = [ ] # liste de periodsem
class periodsem :
pass
# semestre de depart:
porigin = periodsem ( )
d , m , y = [ int ( x ) for x in sem [ " date_debut " ] . split ( " / " ) ]
porigin . datedebut = mxDateTime ( y , m , d )
porigin . sems = [ sem ]
#
tolerance = mx . DateTime . DateTimeDelta ( 45 ) # 45 days
for s in sems :
merged = False
for p in P :
if abs ( s [ " date_debut_mx " ] - p . datedebut ) < tolerance :
p . sems . append ( s )
merged = True
break
if not merged :
p = periodsem ( )
p . datedebut = s [ " date_debut_mx " ]
p . sems = [ s ]
P . append ( p )
# 4-- regroupe par indice de semestre S_i
indices_sems = list ( Set ( [ s [ " semestre_id " ] for s in sems ] ) )
indices_sems . sort ( )
for p in P :
p . nb_etuds = 0 # nombre total d'etudiants dans la periode
p . sems_by_id = DictDefault ( defaultvalue = [ ] )
for s in p . sems :
p . sems_by_id [ s [ " semestre_id " ] ] . append ( s )
p . nb_etuds + = len ( s [ " members " ] )
# 5-- Contruit table
logt ( " C: build table " )
nb_initial = len ( sem [ " members " ] )
def fmtval ( x ) :
if not x :
return " " # ne montre pas les 0
if percent :
return " %2.1f %% " % ( 100.0 * x / nb_initial )
else :
return x
L = [
{
" row_title " : " Origine: S %s " % sem [ " semestre_id " ] ,
porigin . datedebut : nb_initial ,
" _css_row_class " : " sorttop " ,
}
]
if nb_initial < = MAX_ETUD_IN_DESCR :
etud_descr = _descr_etud_set ( context , sem [ " members " ] )
L [ 0 ] [ " _ %s _help " % porigin . datedebut ] = etud_descr
for idx_sem in indices_sems :
if idx_sem > = 0 :
d = { " row_title " : " S %s " % idx_sem }
else :
d = { " row_title " : " Autre semestre " }
for p in P :
etuds_period = Set ( )
for s in p . sems :
if s [ " semestre_id " ] == idx_sem :
etuds_period = etuds_period . union ( s [ " members " ] )
nbetuds = len ( etuds_period )
if nbetuds :
d [ p . datedebut ] = fmtval ( nbetuds )
if nbetuds < = MAX_ETUD_IN_DESCR : # si peu d'etudiants, indique la liste
etud_descr = _descr_etud_set ( context , etuds_period )
d [ " _ %s _help " % p . datedebut ] = etud_descr
L . append ( d )
# Compte nb de démissions et de ré-orientation par période
logt ( " D: cout dems reos " )
sem [ " dems " ] , sem [ " reos " ] = _count_dem_reo ( context , formsemestre_id , sem [ " members " ] )
for p in P :
p . dems = Set ( )
p . reos = Set ( )
for s in p . sems :
d , r = _count_dem_reo ( context , s [ " formsemestre_id " ] , s [ " members " ] )
p . dems . update ( d )
p . reos . update ( r )
# Nombre total d'etudiants par periode
l = {
" row_title " : " Inscrits " ,
" row_title_help " : " Nombre d ' étudiants inscrits " ,
" _table_part " : " foot " ,
porigin . datedebut : fmtval ( nb_initial ) ,
}
for p in P :
l [ p . datedebut ] = fmtval ( p . nb_etuds )
L . append ( l )
# Nombre de démissions par période
l = {
" row_title " : " Démissions " ,
" row_title_help " : " Nombre de démissions pendant la période " ,
" _table_part " : " foot " ,
porigin . datedebut : fmtval ( len ( sem [ " dems " ] ) ) ,
}
if len ( sem [ " dems " ] ) < = MAX_ETUD_IN_DESCR :
etud_descr = _descr_etud_set ( context , sem [ " dems " ] )
l [ " _ %s _help " % porigin . datedebut ] = etud_descr
for p in P :
l [ p . datedebut ] = fmtval ( len ( p . dems ) )
if len ( p . dems ) < = MAX_ETUD_IN_DESCR :
etud_descr = _descr_etud_set ( context , p . dems )
l [ " _ %s _help " % p . datedebut ] = etud_descr
L . append ( l )
# Nombre de réorientations par période
l = {
" row_title " : " Echecs " ,
" row_title_help " : " Ré-orientations (décisions NAR) " ,
" _table_part " : " foot " ,
porigin . datedebut : fmtval ( len ( sem [ " reos " ] ) ) ,
}
if len ( sem [ " reos " ] ) < 10 :
etud_descr = _descr_etud_set ( context , sem [ " reos " ] )
l [ " _ %s _help " % porigin . datedebut ] = etud_descr
for p in P :
l [ p . datedebut ] = fmtval ( len ( p . reos ) )
if len ( p . reos ) < = MAX_ETUD_IN_DESCR :
etud_descr = _descr_etud_set ( context , p . reos )
l [ " _ %s _help " % p . datedebut ] = etud_descr
L . append ( l )
# derniere ligne: nombre et pourcentage de diplomes
l = {
" row_title " : " Diplômes " ,
" row_title_help " : " Nombre de diplômés à la fin de la période " ,
" _table_part " : " foot " ,
}
for p in P :
nb_dipl = 0
for s in p . sems :
nb_dipl + = s [ " nb_dipl " ]
l [ p . datedebut ] = fmtval ( nb_dipl )
L . append ( l )
columns_ids = [ p . datedebut for p in P ]
titles = dict ( [ ( p . datedebut , p . datedebut . strftime ( " %d / % m/ % y " ) ) for p in P ] )
titles [ porigin . datedebut ] = porigin . datedebut . strftime ( " %d / % m/ % y " )
if percent :
pp = " (en % d e la population initiale) "
titles [ " row_title " ] = " % "
else :
pp = " "
titles [ " row_title " ] = " "
if only_primo :
pp + = " (restreint aux primo-entrants) "
if bac :
dbac = " (bacs %s ) " % bac
else :
dbac = " "
if bacspecialite :
dbac + = " (spécialité %s ) " % bacspecialite
2020-11-30 00:11:39 +01:00
if annee_bac :
dbac + = " (année bac %s ) " % annee_bac
2020-09-26 16:19:37 +02:00
if sexe :
dbac + = " genre: %s " % sexe
if statut :
dbac + = " statut: %s " % statut
tab = GenTable (
titles = titles ,
columns_ids = columns_ids ,
rows = L ,
html_col_width = " 4em " ,
html_sortable = True ,
filename = make_filename ( " cohorte " + sem [ " titreannee " ] ) ,
origin = " Généré par %s le " % VERSION . SCONAME + timedate_human_repr ( ) + " " ,
caption = " Suivi cohorte " + pp + sem [ " titreannee " ] + dbac ,
page_title = " Suivi cohorte " + sem [ " titreannee " ] ,
html_class = " table_cohorte " ,
preferences = context . get_preferences ( formsemestre_id ) ,
)
# Explication: liste des semestres associés à chaque date
if not P :
expl = [
' <p class= " help " >(aucun étudiant trouvé dans un semestre ultérieur)</p> '
]
else :
expl = [ " <h3>Semestres associés à chaque date:</h3><ul> " ]
for p in P :
expl . append ( " <li><b> %s </b>: " % p . datedebut . strftime ( " %d / % m/ % y " ) )
ls = [ ]
for s in p . sems :
ls . append (
' <a href= " formsemestre_status?formsemestre_id= %(formsemestre_id)s " > %(titreannee)s </a> '
% s
)
expl . append ( " , " . join ( ls ) + " </li> " )
expl . append ( " </ul> " )
logt ( " Z: table_suivi_cohorte done " )
2020-11-30 00:11:39 +01:00
return tab , " \n " . join ( expl ) , bacs , bacspecialites , annee_bacs , sexes , statuts
2020-09-26 16:19:37 +02:00
def formsemestre_suivi_cohorte (
context ,
formsemestre_id ,
format = " html " ,
percent = 1 ,
bac = " " ,
bacspecialite = " " ,
2020-11-30 00:11:39 +01:00
annee_bac = " " ,
2020-09-26 16:19:37 +02:00
sexe = " " ,
statut = " " ,
only_primo = False ,
REQUEST = None ,
) :
2020-11-30 00:11:39 +01:00
""" Affiche suivi cohortes par numero de semestre """
2020-09-26 16:19:37 +02:00
percent = int ( percent )
sem = sco_formsemestre . get_formsemestre ( context , formsemestre_id )
2020-11-30 00:11:39 +01:00
tab , expl , bacs , bacspecialites , annee_bacs , sexes , statuts = table_suivi_cohorte (
2020-09-26 16:19:37 +02:00
context ,
formsemestre_id ,
percent = percent ,
bac = bac ,
bacspecialite = bacspecialite ,
2020-11-30 00:11:39 +01:00
annee_bac = annee_bac ,
2020-09-26 16:19:37 +02:00
sexe = sexe ,
statut = statut ,
only_primo = only_primo ,
)
tab . base_url = (
" %s ?formsemestre_id= %s &percent= %s &bac= %s &bacspecialite= %s &sexe= %s "
% ( REQUEST . URL0 , formsemestre_id , percent , bac , bacspecialite , sexe )
)
if only_primo :
tab . base_url + = " &only_primo=on "
t = tab . make_page ( context , format = format , with_html_headers = False , REQUEST = REQUEST )
if format != " html " :
return t
base_url = REQUEST . URL0
burl = (
" %s ?formsemestre_id= %s &bac= %s &bacspecialite= %s &sexe= %s &statut= %s "
% ( base_url , formsemestre_id , bac , bacspecialite , sexe , statut )
)
if percent :
pplink = (
' <p><a href= " %s &percent=0 " >Afficher les résultats bruts</a></p> ' % burl
)
else :
pplink = (
' <p><a href= " %s &percent=1 " >Afficher les résultats en pourcentages</a></p> '
% burl
)
help = (
pplink
+ """
< p class = " help " > Nombre d ' étudiants dans chaque semestre. Les dates indiquées sont les dates approximatives de <b>début</b> des semestres (les semestres commençant à des dates proches sont groupés). Le nombre de diplômés est celui à la <b>fin</b> du semestre correspondant. Lorsqu ' il y a moins de % s étudiants dans une case , vous pouvez afficher leurs noms en passant le curseur sur le chiffre . < / p >
< p class = " help " > Les menus permettent de n ' étudier que certaines catégories d ' étudiants ( titulaires d ' un type de bac, garçons ou filles). La case " restreindre aux primo-entrants " permet de ne considérer que les étudiants qui n ' ont jamais été inscrits dans ScoDoc avant le semestre considéré . < / p >
"""
% ( MAX_ETUD_IN_DESCR , )
)
H = [
context . sco_header ( REQUEST , page_title = tab . page_title ) ,
""" <h2 class= " formsemestre " >Suivi cohorte: devenir des étudiants de ce semestre</h2> """ ,
_gen_form_selectetuds (
formsemestre_id ,
REQUEST = REQUEST ,
only_primo = only_primo ,
bac = bac ,
bacspecialite = bacspecialite ,
2020-11-30 00:11:39 +01:00
annee_bac = annee_bac ,
2020-09-26 16:19:37 +02:00
sexe = sexe ,
statut = statut ,
bacs = bacs ,
bacspecialites = bacspecialites ,
2020-11-30 00:11:39 +01:00
annee_bacs = annee_bacs ,
2020-09-26 16:19:37 +02:00
sexes = sexes ,
statuts = statuts ,
percent = percent ,
) ,
t ,
help ,
expl ,
context . sco_footer ( REQUEST ) ,
]
return " \n " . join ( H )
def _gen_form_selectetuds (
formsemestre_id ,
REQUEST = None ,
percent = None ,
only_primo = None ,
bac = None ,
bacspecialite = None ,
2020-11-30 00:11:39 +01:00
annee_bac = None ,
2020-09-26 16:19:37 +02:00
sexe = None ,
statut = None ,
bacs = None ,
bacspecialites = None ,
2020-11-30 00:11:39 +01:00
annee_bacs = None ,
2020-09-26 16:19:37 +02:00
sexes = None ,
statuts = None ,
) :
""" HTML form pour choix criteres selection etudiants """
bacs = list ( bacs )
bacs . sort ( )
bacspecialites = list ( bacspecialites )
bacspecialites . sort ( )
2020-11-30 00:11:39 +01:00
annee_bacs = list ( annee_bacs )
annee_bacs . sort ( )
2020-09-26 16:19:37 +02:00
sexes = list ( sexes )
sexes . sort ( )
statuts = list ( statuts )
statuts . sort ( )
#
if bac :
selected = " "
else :
selected = ' s elected= " selected " '
F = [
""" <form id= " f " method= " get " action= " %s " >
< p > Bac : < select name = " bac " onchange = " javascript: submit(this); " >
< option value = " " % s > tous < / option >
"""
% ( REQUEST . URL0 , selected )
]
for b in bacs :
if bac == b :
selected = ' selected= " selected " '
else :
selected = " "
F . append ( ' <option value= " %s " %s > %s </option> ' % ( b , selected , b ) )
F . append ( " </select> " )
if bacspecialite :
selected = " "
else :
selected = ' selected= " selected " '
F . append (
""" Bac/Specialité: <select name= " bacspecialite " onchange= " javascript: submit(this); " >
< option value = " " % s > tous < / option >
"""
% selected
)
for b in bacspecialites :
if bacspecialite == b :
selected = ' selected= " selected " '
else :
selected = " "
F . append ( ' <option value= " %s " %s > %s </option> ' % ( b , selected , b ) )
F . append ( " </select> " )
2020-11-30 00:11:39 +01:00
#
if annee_bac :
selected = " "
else :
selected = ' selected= " selected " '
F . append (
""" Année bac: <select name= " annee_bac " onchange= " javascript: submit(this); " >
< option value = " " % s > tous < / option >
"""
% selected
)
for b in annee_bacs :
if str ( annee_bac ) == str ( b ) :
selected = ' selected= " selected " '
else :
selected = " "
F . append ( ' <option value= " %s " %s > %s </option> ' % ( b , selected , b ) )
F . append ( " </select> " )
#
2020-09-26 16:19:37 +02:00
F . append (
""" Genre: <select name= " sexe " onchange= " javascript: submit(this); " >
< option value = " " % s > tous < / option >
"""
% selected
)
for b in sexes :
if sexe == b :
selected = ' selected= " selected " '
else :
selected = " "
F . append ( ' <option value= " %s " %s > %s </option> ' % ( b , selected , b ) )
F . append ( " </select> " )
F . append (
""" Statut: <select name= " statut " onchange= " javascript: submit(this); " >
< option value = " " % s > tous < / option >
"""
% selected
)
for b in statuts :
if statut == b :
selected = ' selected= " selected " '
else :
selected = " "
F . append ( ' <option value= " %s " %s > %s </option> ' % ( b , selected , b ) )
F . append ( " </select> " )
if only_primo :
checked = ' checked= " 1 " '
else :
checked = " "
F . append (
' <br/><input type= " checkbox " name= " only_primo " onchange= " javascript: submit(this); " %s />Restreindre aux primo-entrants '
% checked
)
F . append (
' <input type= " hidden " name= " formsemestre_id " value= " %s " /> ' % formsemestre_id
)
F . append ( ' <input type= " hidden " name= " percent " value= " %s " /> ' % percent )
F . append ( " </p></form> " )
return " \n " . join ( F )
def _descr_etud_set ( context , etudids ) :
" textual html description of a set of etudids "
etuds = [ ]
for etudid in etudids :
etuds . append ( context . getEtudInfo ( etudid = etudid , filled = True ) [ 0 ] )
# sort by name
etuds . sort ( lambda x , y : cmp ( x [ " nom " ] , y [ " nom " ] ) )
return " , " . join ( [ e [ " nomprenom " ] for e in etuds ] )
def _count_dem_reo ( context , formsemestre_id , etudids ) :
" count nb of demissions and reorientation in this etud set "
nt = context . _getNotesCache ( ) . get_NotesTable (
context , formsemestre_id
) # > get_etud_etat, get_etud_decision_sem
dems = Set ( )
reos = Set ( )
for etudid in etudids :
if nt . get_etud_etat ( etudid ) == " D " :
dems . add ( etudid )
dec = nt . get_etud_decision_sem ( etudid )
if dec and dec [ " code " ] in sco_codes_parcours . CODES_SEM_REO :
reos . add ( etudid )
return dems , reos
""" OLDGEA:
27 s pour S1 F . I . classique Semestre 1 2006 - 2007
B 2.3 s
C 5.6 s
D 5.9 s
Z 27 s = > cache des semestres pour nt
à chaud : 3 s
B : etuds sets : 2.4 s = > lent : N x getEtudInfo ( non caché )
"""
EXP_LIC = re . compile ( r " licence " , re . I )
EXP_LPRO = re . compile ( r " professionnelle " , re . I )
def _codesem ( sem , short = True , prefix = " " ) :
" code semestre: S1 ou S1d "
idx = sem [ " semestre_id " ]
# semestre décalé ?
# les semestres pairs normaux commencent entre janvier et mars
# les impairs normaux entre aout et decembre
d = " "
if idx and idx > 0 and sem [ " date_debut " ] :
mois_debut = int ( sem [ " date_debut " ] . split ( " / " ) [ 1 ] )
if ( idx % 2 and mois_debut < 3 ) or ( idx % 2 == 0 and mois_debut > = 8 ) :
d = " d "
if idx == - 1 :
if short :
idx = " Autre "
else :
idx = sem [ " titre " ] + " "
idx = EXP_LPRO . sub ( " pro. " , idx )
idx = EXP_LIC . sub ( " Lic. " , idx )
prefix = " " # indique titre au lieu de Sn
return " %s %s %s " % ( prefix , idx , d )
def get_codeparcoursetud ( context , etud , prefix = " " , separator = " " ) :
""" calcule un code de parcours pour un etudiant
exemples :
1234 A pour un etudiant ayant effectué S1 , S2 , S3 , S4 puis diplome
12 D pour un étudiant en S1 , S2 puis démission en S2
12 R pour un etudiant en S1 , S2 réorienté en fin de S2
2020-11-30 00:11:39 +01:00
Construit aussi un dict : { semestre_id : decision_jury | None }
2020-09-26 16:19:37 +02:00
"""
p = [ ]
decisions_jury = { }
# élimine les semestres spéciaux sans parcours (LP...)
sems = [ s for s in etud [ " sems " ] if s [ " semestre_id " ] > = 0 ]
i = len ( sems ) - 1
while i > = 0 :
s = sems [ i ] # 'sems' est a l'envers, du plus recent au plus ancien
nt = context . _getNotesCache ( ) . get_NotesTable (
context , s [ " formsemestre_id " ]
) # > get_etud_etat, get_etud_decision_sem
p . append ( _codesem ( s , prefix = prefix ) )
# code decisions jury de chaque semestre:
if nt . get_etud_etat ( etud [ " etudid " ] ) == " D " :
decisions_jury [ s [ " semestre_id " ] ] = " DEM "
else :
dec = nt . get_etud_decision_sem ( etud [ " etudid " ] )
if not dec :
decisions_jury [ s [ " semestre_id " ] ] = " "
else :
decisions_jury [ s [ " semestre_id " ] ] = dec [ " code " ]
# code etat dans le codeparcours sur dernier semestre seulement
if i == 0 :
# Démission
if nt . get_etud_etat ( etud [ " etudid " ] ) == " D " :
p . append ( " :D " )
else :
dec = nt . get_etud_decision_sem ( etud [ " etudid " ] )
if dec and dec [ " code " ] in sco_codes_parcours . CODES_SEM_REO :
p . append ( " :R " )
if (
dec
and s [ " semestre_id " ] == nt . parcours . NB_SEM
and code_semestre_validant ( dec [ " code " ] )
) :
p . append ( " :A " )
i - = 1
return separator . join ( p ) , decisions_jury
def tsp_etud_list (
context ,
formsemestre_id ,
only_primo = False ,
bac = " " , # selection sur type de bac
bacspecialite = " " ,
2020-11-30 00:11:39 +01:00
annee_bac = " " ,
2020-09-26 16:19:37 +02:00
sexe = " " ,
statut = " " ,
) :
2020-11-30 00:11:39 +01:00
""" Liste des etuds a considerer dans table suivi parcours
ramene aussi ensembles des bacs , genres , statuts de ( tous ) les etudiants
2020-09-26 16:19:37 +02:00
"""
# log('tsp_etud_list(%s, bac="%s")' % (formsemestre_id,bac))
sem = sco_formsemestre . get_formsemestre ( context , formsemestre_id )
nt = context . _getNotesCache ( ) . get_NotesTable (
context , formsemestre_id
) # > get_etudids,
etudids = nt . get_etudids ( )
etuds = [ ]
bacs = Set ( )
bacspecialites = Set ( )
2020-11-30 00:11:39 +01:00
annee_bacs = Set ( )
2020-09-26 16:19:37 +02:00
sexes = Set ( )
statuts = Set ( )
for etudid in etudids :
etud = context . getEtudInfo ( etudid = etudid , filled = True ) [ 0 ]
bacspe = etud [ " bac " ] + " / " + etud [ " specialite " ]
# sélection sur bac, primo, ...:
if (
( not bac or ( bac == etud [ " bac " ] ) )
and ( not bacspecialite or ( bacspecialite == bacspe ) )
2020-11-30 00:11:39 +01:00
and ( not annee_bac or ( annee_bac == str ( etud [ " annee_bac " ] ) ) )
2020-09-26 16:19:37 +02:00
and ( not sexe or ( sexe == etud [ " sexe " ] ) )
and ( not statut or ( statut == etud [ " statut " ] ) )
and ( not only_primo or context . isPrimoEtud ( etud , sem ) )
) :
etuds . append ( etud )
bacs . add ( etud [ " bac " ] )
bacspecialites . add ( bacspe )
2020-11-30 00:11:39 +01:00
annee_bacs . add ( etud [ " annee_bac " ] )
2020-09-26 16:19:37 +02:00
sexes . add ( etud [ " sexe " ] )
if etud [ " statut " ] : # ne montre pas les statuts non renseignés
statuts . add ( etud [ " statut " ] )
# log('tsp_etud_list: %s etuds' % len(etuds))
2020-11-30 00:11:39 +01:00
return etuds , bacs , bacspecialites , annee_bacs , sexes , statuts
2020-09-26 16:19:37 +02:00
def tsp_grouped_list ( context , codes_etuds ) :
2020-11-30 00:11:39 +01:00
""" Liste pour table regroupant le nombre d ' étudiants (+ bulle avec les noms) de chaque parcours """
2020-09-26 16:19:37 +02:00
L = [ ]
parcours = codes_etuds . keys ( )
parcours . sort ( )
for p in parcours :
nb = len ( codes_etuds [ p ] )
l = { " parcours " : p , " nb " : nb }
if nb < = MAX_ETUD_IN_DESCR :
l [ " _nb_help " ] = _descr_etud_set (
context , [ e [ " etudid " ] for e in codes_etuds [ p ] ]
)
L . append ( l )
# tri par effectifs décroissants
L . sort ( lambda x , y : cmp ( y [ " nb " ] , x [ " nb " ] ) )
return L
def table_suivi_parcours (
context , formsemestre_id , only_primo = False , grouped_parcours = True
) :
2020-11-30 00:11:39 +01:00
""" Tableau recapitulant tous les parcours """
2020-09-26 16:19:37 +02:00
sem = sco_formsemestre . get_formsemestre ( context , formsemestre_id )
2020-11-30 00:11:39 +01:00
etuds , bacs , bacspecialites , annee_bacs , sexes , statuts = tsp_etud_list (
2020-09-26 16:19:37 +02:00
context , formsemestre_id , only_primo = only_primo
)
codes_etuds = DictDefault ( defaultvalue = [ ] )
for etud in etuds :
etud [ " codeparcours " ] , etud [ " decisions_jury " ] = get_codeparcoursetud (
context , etud
)
codes_etuds [ etud [ " codeparcours " ] ] . append ( etud )
etud [ " _nom_target " ] = " ficheEtud?etudid= " + etud [ " etudid " ]
etud [ " _prenom_target " ] = " ficheEtud?etudid= " + etud [ " etudid " ]
etud [ " _nom_td_attrs " ] = ' id= " %s " class= " etudinfo " ' % ( etud [ " etudid " ] )
titles = {
" parcours " : " Code parcours " ,
" nb " : " Nombre d ' étudiants " ,
" sexe " : " " ,
" nom " : " Nom " ,
" prenom " : " Prénom " ,
" etudid " : " etudid " ,
" codeparcours " : " Code parcours " ,
" bac " : " Bac " ,
" specialite " : " Spe. " ,
}
if grouped_parcours :
L = tsp_grouped_list ( context , codes_etuds )
columns_ids = ( " parcours " , " nb " )
else :
# Table avec le parcours de chaque étudiant:
L = etuds
columns_ids = (
" etudid " ,
" sexe " ,
" nom " ,
" prenom " ,
" bac " ,
" specialite " ,
" codeparcours " ,
)
# Calcule intitulés de colonnes
S = set ( )
sems_ids = list ( apply ( S . union , [ e [ " decisions_jury " ] . keys ( ) for e in etuds ] ) )
sems_ids . sort ( )
sem_tits = [ " S %s " % s for s in sems_ids ]
titles . update ( [ ( s , s ) for s in sem_tits ] )
columns_ids + = tuple ( sem_tits )
for etud in etuds :
for s in etud [ " decisions_jury " ] :
etud [ " S %s " % s ] = etud [ " decisions_jury " ] [ s ]
if only_primo :
primostr = " primo-entrants du "
else :
primostr = " passés dans le "
tab = GenTable (
columns_ids = columns_ids ,
rows = L ,
titles = titles ,
origin = " Généré par %s le " % VERSION . SCONAME + timedate_human_repr ( ) + " " ,
caption = " Parcours suivis, étudiants %s semestre " % primostr
+ sem [ " titreannee " ] ,
page_title = " Parcours " + sem [ " titreannee " ] ,
html_sortable = True ,
html_class = " table_leftalign table_listegroupe " ,
html_next_section = """ <table class= " help " >
< tr > < td > < tt > 1 , 2 , . . . < / tt > < / td > < td > numéros de semestres < / td > < / tr >
< tr > < td > < tt > 1 d , 2 d , . . . < / tt > < / td > < td > semestres " décalés " < / td > < / tr >
< tr > < td > < tt > : A < / tt > < / td > < td > étudiants diplômés < / td > < / tr >
< tr > < td > < tt > : R < / tt > < / td > < td > étudiants réorientés < / td > < / tr >
< tr > < td > < tt > : D < / tt > < / td > < td > étudiants démissionnaires < / td > < / tr >
< / table > """ ,
bottom_titles = {
" parcours " : " Total " ,
" nb " : len ( etuds ) ,
" codeparcours " : len ( etuds ) ,
} ,
preferences = context . get_preferences ( formsemestre_id ) ,
)
return tab
def tsp_form_primo_group ( REQUEST , only_primo , no_grouping , formsemestre_id , format ) :
2020-11-30 00:11:39 +01:00
""" Element de formulaire pour choisir si restriction aux primos entrants et groupement par lycees """
2020-09-26 16:19:37 +02:00
F = [ """ <form name= " f " method= " get " action= " %s " > """ % REQUEST . URL0 ]
if only_primo :
checked = ' checked= " 1 " '
else :
checked = " "
F . append (
' <input type= " checkbox " name= " only_primo " onchange= " document.f.submit() " %s >Restreindre aux primo-entrants</input> '
% checked
)
if no_grouping :
checked = ' checked= " 1 " '
else :
checked = " "
F . append (
' <input type= " checkbox " name= " no_grouping " onchange= " document.f.submit() " %s >Lister chaque étudiant</input> '
% checked
)
F . append (
' <input type= " hidden " name= " formsemestre_id " value= " %s " /> ' % formsemestre_id
)
F . append ( ' <input type= " hidden " name= " format " value= " %s " /> ' % format )
F . append ( """ </form> """ )
return " \n " . join ( F )
def formsemestre_suivi_parcours (
context ,
formsemestre_id ,
format = " html " ,
only_primo = False ,
no_grouping = False ,
REQUEST = None ,
) :
2020-11-30 00:11:39 +01:00
""" Effectifs dans les differents parcours possibles. """
2020-09-26 16:19:37 +02:00
sem = sco_formsemestre . get_formsemestre ( context , formsemestre_id )
tab = table_suivi_parcours (
context ,
formsemestre_id ,
only_primo = only_primo ,
grouped_parcours = not no_grouping ,
)
tab . base_url = " %s ?formsemestre_id= %s " % ( REQUEST . URL0 , formsemestre_id )
if only_primo :
tab . base_url + = " &only_primo=1 "
if no_grouping :
tab . base_url + = " &no_grouping=1 "
t = tab . make_page ( context , format = format , with_html_headers = False , REQUEST = REQUEST )
if format != " html " :
return t
F = [
tsp_form_primo_group ( REQUEST , only_primo , no_grouping , formsemestre_id , format )
]
H = [
context . sco_header (
REQUEST ,
page_title = tab . page_title ,
init_qtip = True ,
javascripts = [ " js/etud_info.js " ] ,
) ,
""" <h2 class= " formsemestre " >Parcours suivis par les étudiants de ce semestre</h2> """ ,
" \n " . join ( F ) ,
t ,
context . sco_footer ( REQUEST ) ,
]
return " \n " . join ( H )
# -------------
def graph_parcours (
context ,
formsemestre_id ,
format = " svg " ,
only_primo = False ,
bac = " " , # selection sur type de bac
bacspecialite = " " ,
2020-11-30 00:11:39 +01:00
annee_bac = " " ,
2020-09-26 16:19:37 +02:00
sexe = " " ,
statut = " " ,
) :
2020-11-30 00:11:39 +01:00
""" """
2020-09-26 16:19:37 +02:00
if not WITH_PYDOT :
raise ScoValueError ( " pydot module is not installed " )
sem = sco_formsemestre . get_formsemestre ( context , formsemestre_id )
2020-11-30 00:11:39 +01:00
etuds , bacs , bacspecialites , annee_bacs , sexes , statuts = tsp_etud_list (
2020-09-26 16:19:37 +02:00
context ,
formsemestre_id ,
only_primo = only_primo ,
bac = bac ,
bacspecialite = bacspecialite ,
2020-11-30 00:11:39 +01:00
annee_bac = annee_bac ,
2020-09-26 16:19:37 +02:00
sexe = sexe ,
statut = statut ,
)
# log('graph_parcours: %s etuds (only_primo=%s)' % (len(etuds), only_primo))
if not etuds :
2020-11-30 00:11:39 +01:00
return " " , etuds , bacs , bacspecialites , annee_bacs , sexes , statuts
2020-09-26 16:19:37 +02:00
edges = DictDefault (
defaultvalue = Set ( )
) # {(formsemestre_id_origin, formsemestre_id_dest) : etud_set}
sems = { }
effectifs = DictDefault ( defaultvalue = Set ( ) ) # formsemestre_id : etud_set
decisions = DictDefault ( defaultvalue = { } ) # formsemestre_id : { code : nb_etud }
isolated_nodes = [ ]
connected_nodes = Set ( )
diploma_nodes = [ ]
dem_nodes = { } # formsemestre_id : noeud pour demissionnaires
nar_nodes = { } # formsemestre_id : noeud pour NAR
for etud in etuds :
next = None
etudid = etud [ " etudid " ]
for s in etud [ " sems " ] : # du plus recent au plus ancien
nt = context . _getNotesCache ( ) . get_NotesTable (
context , s [ " formsemestre_id " ]
) # > get_etud_decision_sem, get_etud_etat
dec = nt . get_etud_decision_sem ( etudid )
if next :
if (
s [ " semestre_id " ] == nt . parcours . NB_SEM
and dec
and code_semestre_validant ( dec [ " code " ] )
and nt . get_etud_etat ( etudid ) == " I "
) :
# cas particulier du diplome puis poursuite etude
edges [
( " _dipl_ " + s [ " formsemestre_id " ] , next [ " formsemestre_id " ] )
] . add ( etudid )
else :
edges [ ( s [ " formsemestre_id " ] , next [ " formsemestre_id " ] ) ] . add ( etudid )
connected_nodes . add ( s [ " formsemestre_id " ] )
connected_nodes . add ( next [ " formsemestre_id " ] )
else :
isolated_nodes . append ( s [ " formsemestre_id " ] )
sems [ s [ " formsemestre_id " ] ] = s
effectifs [ s [ " formsemestre_id " ] ] . add ( etudid )
next = s
# Compte decisions jury de chaque semestres:
dc = decisions [ s [ " formsemestre_id " ] ]
if dec :
if dec [ " code " ] in dc :
dc [ dec [ " code " ] ] + = 1
else :
dc [ dec [ " code " ] ] = 1
# ajout noeud pour demissionnaires
if nt . get_etud_etat ( etudid ) == " D " :
nid = " _dem_ " + s [ " formsemestre_id " ]
dem_nodes [ s [ " formsemestre_id " ] ] = nid
edges [ ( s [ " formsemestre_id " ] , nid ) ] . add ( etudid )
# ajout noeud pour NAR (seulement pour noeud de depart)
if s [ " formsemestre_id " ] == formsemestre_id and dec and dec [ " code " ] == NAR :
nid = " _nar_ " + s [ " formsemestre_id " ]
nar_nodes [ s [ " formsemestre_id " ] ] = nid
edges [ ( s [ " formsemestre_id " ] , nid ) ] . add ( etudid )
# si "terminal", ajoute noeud pour diplomes
if s [ " semestre_id " ] == nt . parcours . NB_SEM :
if (
dec
and code_semestre_validant ( dec [ " code " ] )
and nt . get_etud_etat ( etudid ) == " I "
) :
nid = " _dipl_ " + s [ " formsemestre_id " ]
edges [ ( s [ " formsemestre_id " ] , nid ) ] . add ( etudid )
diploma_nodes . append ( nid )
#
g = pydot . graph_from_edges ( edges . keys ( ) )
for fid in isolated_nodes :
if not fid in connected_nodes :
n = pydot . Node ( name = fid )
g . add_node ( n )
g . set ( " rankdir " , " LR " ) # left to right
g . set_fontname ( " Helvetica " )
if format == " svg " :
g . set_bgcolor ( " #fffff0 " ) # ou 'transparent'
# titres des semestres:
for s in sems . values ( ) :
n = pydot_get_node ( g , s [ " formsemestre_id " ] )
log ( " s[ ' formsemestre_id ' ] = %s " % s [ " formsemestre_id " ] )
log ( " n= %s " % n )
log ( " get= %s " % g . get_node ( s [ " formsemestre_id " ] ) )
log ( " nodes names = %s " % [ x . get_name ( ) for x in g . get_node_list ( ) ] )
if s [ " modalite " ] and s [ " modalite " ] != " FI " :
modalite = " " + s [ " modalite " ]
else :
modalite = " "
label = " %s %s \\ n %d / %s - %d / %s \\ n %d " % (
_codesem ( s , short = False , prefix = " S " ) ,
modalite ,
s [ " mois_debut_ord " ] ,
s [ " annee_debut " ] [ 2 : ] ,
s [ " mois_fin_ord " ] ,
s [ " annee_fin " ] [ 2 : ] ,
len ( effectifs [ s [ " formsemestre_id " ] ] ) ,
)
n . set ( " label " , suppress_accents ( label ) )
n . set_fontname ( " Helvetica " )
n . set_fontsize ( 8.0 )
n . set_width ( 1.2 )
n . set_shape ( " box " )
n . set_URL ( " formsemestre_status?formsemestre_id= " + s [ " formsemestre_id " ] )
# semestre de depart en vert
n = pydot_get_node ( g , formsemestre_id )
n . set_color ( " green " )
# demissions en rouge, octagonal
for nid in dem_nodes . values ( ) :
n = pydot_get_node ( g , nid )
n . set_color ( " red " )
n . set_shape ( " octagon " )
n . set ( " label " , " Dem. " )
# NAR en rouge, Mcircle
for nid in nar_nodes . values ( ) :
n = pydot_get_node ( g , nid )
n . set_color ( " red " )
n . set_shape ( " Mcircle " )
n . set ( " label " , NAR )
# diplomes:
for nid in diploma_nodes :
n = pydot_get_node ( g , nid )
n . set_color ( " red " )
n . set_shape ( " ellipse " )
n . set ( " label " , " Diplome " ) # bug si accent (pas compris pourquoi)
# Arètes:
bubbles = { } # substitue titres pour bulle aides: src_id:dst_id : etud_descr
for ( src_id , dst_id ) in edges . keys ( ) :
e = g . get_edge ( src_id , dst_id )
e . set ( " arrowhead " , " normal " )
e . set ( " arrowsize " , 1 )
e . set_label ( len ( edges [ ( src_id , dst_id ) ] ) )
e . set_fontname ( " Helvetica " )
e . set_fontsize ( 8.0 )
# bulle avec liste etudiants
if len ( edges [ ( src_id , dst_id ) ] ) < = MAX_ETUD_IN_DESCR :
etud_descr = _descr_etud_set ( context , edges [ ( src_id , dst_id ) ] )
bubbles [ src_id + " : " + dst_id ] = etud_descr
e . set_URL ( " __xxxetudlist__? " + src_id + " : " + dst_id )
# Genere graphe
f , path = tempfile . mkstemp ( " .gr " )
g . write ( path = path , format = format )
data = open ( path , " r " ) . read ( )
log ( " dot generated %d bytes in %s format " % ( len ( data ) , format ) )
if not data :
log ( " graph.to_string= %s " % g . to_string ( ) )
raise ValueError (
" Erreur lors de la génération du document au format %s " % format
)
os . unlink ( path )
if format == " svg " :
# dot génère un document XML complet, il faut enlever l'en-tête
data = " <svg " + " <svg " . join ( data . split ( " <svg " ) [ 1 : ] )
# Substitution des titres des URL des aretes pour bulles aide
def repl ( m ) :
return ' <a title= " %s " ' % bubbles [ m . group ( " sd " ) ]
exp = re . compile (
r ' <a.*?href= " __xxxetudlist__ \ ?(?P<sd> \ w*: \ w*).*? " .*?xlink:title= " .*? " ' , re . M
)
data = exp . sub ( repl , data )
# Substitution des titres des boites (semestres)
exp1 = re . compile (
r ' <a xlink:href= " formsemestre_status \ ?formsemestre_id=(?P<fid> \ w*).*? " .*?xlink:title= " (?P<title>.*?) " ' ,
re . M | re . DOTALL ,
)
def repl_title ( m ) :
fid = m . group ( " fid " )
title = sems [ fid ] [ " titreannee " ]
if decisions [ fid ] :
title + = (
( " ( " + str ( decisions [ fid ] ) + " ) " ) . replace ( " { " , " " ) . replace ( " ' " , " " )
)
return (
' <a xlink:href= " formsemestre_status?formsemestre_id= %s " xlink:title= " %s " '
% ( fid , suppress_accents ( title ) )
) # evite accents car svg utf-8 vs page en latin1...
data = exp1 . sub ( repl_title , data )
# Substitution de Arial par Helvetica (new prblem in Debian 5) ???
# bug turnaround: il doit bien y avoir un endroit ou regler cela ?
# cf http://groups.google.com/group/pydot/browse_thread/thread/b3704c53e331e2ec
data = data . replace ( " font-family:Arial " , " font-family:Helvetica " )
2020-11-30 00:11:39 +01:00
return data , etuds , bacs , bacspecialites , annee_bacs , sexes , statuts
2020-09-26 16:19:37 +02:00
def formsemestre_graph_parcours (
context ,
formsemestre_id ,
format = " html " ,
only_primo = False ,
bac = " " , # selection sur type de bac
bacspecialite = " " ,
2020-11-30 00:11:39 +01:00
annee_bac = " " ,
2020-09-26 16:19:37 +02:00
sexe = " " ,
statut = " " ,
2020-11-30 00:11:39 +01:00
allkeys = False , # unused
2020-09-26 16:19:37 +02:00
REQUEST = None ,
) :
2020-11-30 00:11:39 +01:00
""" Graphe suivi cohortes """
2020-09-26 16:19:37 +02:00
# log("formsemestre_graph_parcours")
sem = sco_formsemestre . get_formsemestre ( context , formsemestre_id )
if format == " pdf " :
2020-11-30 00:11:39 +01:00
doc , etuds , bacs , bacspecialites , annee_bacs , sexes , statuts = graph_parcours (
2020-09-26 16:19:37 +02:00
context ,
formsemestre_id ,
format = " pdf " ,
only_primo = only_primo ,
bac = bac ,
bacspecialite = bacspecialite ,
2020-11-30 00:11:39 +01:00
annee_bac = annee_bac ,
2020-09-26 16:19:37 +02:00
sexe = sexe ,
statut = statut ,
)
filename = make_filename ( " flux " + sem [ " titreannee " ] )
return sco_pdf . sendPDFFile ( REQUEST , doc , filename + " .pdf " )
elif format == " png " :
#
2020-11-30 00:11:39 +01:00
doc , etuds , bacs , bacspecialites , annee_bacs , sexes , statuts = graph_parcours (
2020-09-26 16:19:37 +02:00
context ,
formsemestre_id ,
format = " png " ,
only_primo = only_primo ,
bac = bac ,
bacspecialite = bacspecialite ,
2020-11-30 00:11:39 +01:00
annee_bac = annee_bac ,
2020-09-26 16:19:37 +02:00
sexe = sexe ,
statut = statut ,
)
filename = make_filename ( " flux " + sem [ " titreannee " ] )
REQUEST . RESPONSE . setHeader (
" content-disposition " , ' attachment; filename= " %s " ' % filename
)
REQUEST . RESPONSE . setHeader ( " content-type " , " image/png " )
return doc
elif format == " html " :
if only_primo :
op = " only_primo=on& "
else :
op = " "
url = urllib . quote (
" formsemestre_graph_parcours?formsemestre_id= %s & %s bac= %s &bacspecialite= %s &sexe= %s &statut= %s &format= "
% ( formsemestre_id , op , bac , bacspecialite , sexe , statut )
)
2020-11-30 00:11:39 +01:00
doc , etuds , bacs , bacspecialites , annee_bacs , sexes , statuts = graph_parcours (
2020-09-26 16:19:37 +02:00
context ,
formsemestre_id ,
only_primo = only_primo ,
bac = bac ,
bacspecialite = bacspecialite ,
2020-11-30 00:11:39 +01:00
annee_bac = annee_bac ,
2020-09-26 16:19:37 +02:00
sexe = sexe ,
statut = statut ,
)
H = [
context . sco_header (
REQUEST ,
page_title = " Parcours étudiants de %(titreannee)s " % sem ,
no_side_bar = True ,
) ,
""" <h2 class= " formsemestre " >Parcours des étudiants de ce semestre</h2> """ ,
doc ,
2020-11-30 00:11:39 +01:00
" <p> %d étudiants sélectionnés</p> " % len ( etuds ) ,
2020-09-26 16:19:37 +02:00
_gen_form_selectetuds (
formsemestre_id ,
REQUEST = REQUEST ,
only_primo = only_primo ,
bac = bac ,
bacspecialite = bacspecialite ,
2020-11-30 00:11:39 +01:00
annee_bac = annee_bac ,
2020-09-26 16:19:37 +02:00
sexe = sexe ,
statut = statut ,
bacs = bacs ,
bacspecialites = bacspecialites ,
2020-11-30 00:11:39 +01:00
annee_bacs = annee_bacs ,
2020-09-26 16:19:37 +02:00
sexes = sexes ,
statuts = statuts ,
percent = 0 ,
) ,
""" <p>Origine et devenir des étudiants inscrits dans %(titreannee)s """
% sem ,
# En Debian 4, dot ne genere pas du pdf, et epstopdf ne marche pas sur le .ps ou ps2 générés par dot
# mais c'est OK en Debian 5
""" (<a href= " %s pdf " >version pdf</a> """ % url ,
""" , <a href= " %s png " >image PNG</a>) """ % url ,
""" </p> """ ,
""" <p class= " help " >Cette page ne s ' affiche correctement que sur les navigateurs récents.</p> """ ,
""" <p class= " help " >Le graphe permet de suivre les étudiants inscrits dans le semestre
sélectionné ( dessiné en vert ) . Chaque rectangle représente un semestre ( cliquez dedans
pour afficher son tableau de bord ) . Les flèches indiquent le nombre d ' étudiants passant
d ' un semestre à l ' autre ( s ' il y en a moins de %s , vous pouvez visualiser leurs noms en
passant la souris sur le chiffre ) .
< / p > """
% MAX_ETUD_IN_DESCR ,
context . sco_footer ( REQUEST ) ,
]
REQUEST . RESPONSE . setHeader ( " content-type " , " application/xhtml+xml " )
return " \n " . join ( H )
else :
raise ValueError ( " invalid format: %s " % format )