Up-to-date with 7.20a

This commit is contained in:
viennet 2021-01-01 23:46:51 +01:00
commit aec2d58dbf
249 changed files with 1781 additions and 890 deletions

View File

@ -5,7 +5,7 @@
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved.
# Copyright (c) 1999 - 2021 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

View File

@ -1,7 +1,7 @@
# SCODOC - gestion de la scolarité
(c) Emmanuel Viennet 1999 - 2020 (voir LICENCE.txt)
(c) Emmanuel Viennet 1999 - 2021 (voir LICENCE.txt)
Installation: voir instructions à jour sur <https://scodoc.org>

View File

@ -8,6 +8,7 @@ SCONAME = "ScoDoc"
SCONEWS = """
<h4>Année 2020</h4>
<ul>
<li>Corrections d'erreurs, améliorations saise absences< et affichage bulletins</li>
<li>Nouveau site <a href="https://scodoc.org">scodoc.org</a> pour la documentation.</li>
<li>Enregistrement de semestres extérieurs</li>
<li>Améliorations PV de Jury</li>

View File

@ -5,7 +5,7 @@
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved.
# Copyright (c) 1999 - 2021 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
@ -45,17 +45,20 @@ L'API de plus bas niveau est en gros:
"""
import urllib
import datetime
import jaxml
import cgi
# ---------------
from sco_zope import *
# ---------------
from notesdb import *
import sco_utils as scu
import notesdb
from notes_log import log
from scolog import logdb
from sco_utils import *
# import notes_users
from sco_permissions import ScoAbsAddBillet, ScoAbsChange, ScoView
from sco_exceptions import ScoValueError, ScoInvalidDateError
from TrivialFormulator import TrivialFormulator, TF
from gen_tables import GenTable
import scolars
@ -201,7 +204,7 @@ class ddmmyyyy:
return self.prev(self.weekday)
def __cmp__(self, other):
"""return a negative integer if self < other,
"""return a negative integer if self < other,
zero if self == other, a positive integer if self > other"""
return int(self.time - other.time)
@ -356,7 +359,7 @@ class ZAbsences(
estjust = _toboolean(estjust)
matin = _toboolean(matin)
cnx = self.GetDBConnexion()
cursor = cnx.cursor(cursor_factory=ScoDocCursor)
cursor = cnx.cursor(cursor_factory=notesdb.ScoDocCursor)
cursor.execute(
"insert into absences (etudid,jour,estabs,estjust,matin,description, moduleimpl_id) values (%(etudid)s, %(jour)s, TRUE, %(estjust)s, %(matin)s, %(description)s, %(moduleimpl_id)s )",
vars(),
@ -380,7 +383,7 @@ class ZAbsences(
raise ScoValueError("date justificatif trop loin dans le futur !")
matin = _toboolean(matin)
cnx = self.GetDBConnexion()
cursor = cnx.cursor(cursor_factory=ScoDocCursor)
cursor = cnx.cursor(cursor_factory=notesdb.ScoDocCursor)
cursor.execute(
"insert into absences (etudid,jour,estabs,estjust,matin, description) values (%(etudid)s,%(jour)s, FALSE, TRUE, %(matin)s, %(description)s )",
vars(),
@ -402,7 +405,7 @@ class ZAbsences(
# unpublished
matin = _toboolean(matin)
cnx = self.GetDBConnexion()
cursor = cnx.cursor(cursor_factory=ScoDocCursor)
cursor = cnx.cursor(cursor_factory=notesdb.ScoDocCursor)
req = "delete from absences where jour=%(jour)s and matin=%(matin)s and etudid=%(etudid)s and estabs"
if moduleimpl_id:
req += " and moduleimpl_id=%(moduleimpl_id)s"
@ -423,7 +426,7 @@ class ZAbsences(
# unpublished
matin = _toboolean(matin)
cnx = self.GetDBConnexion()
cursor = cnx.cursor(cursor_factory=ScoDocCursor)
cursor = cnx.cursor(cursor_factory=notesdb.ScoDocCursor)
cursor.execute(
"delete from absences where jour=%(jour)s and matin=%(matin)s and etudid=%(etudid)s and ESTJUST AND NOT ESTABS",
vars(),
@ -450,7 +453,7 @@ class ZAbsences(
# """
# # unpublished
# cnx = self.GetDBConnexion()
# cursor = cnx.cursor(cursor_factory=ScoDocCursor)
# cursor = cnx.cursor(cursor_factory=notesdb.ScoDocCursor)
# # supr les absences non justifiees
# cursor.execute("delete from absences where etudid=%(etudid)s and (not estjust) and moduleimpl_id=(moduleimpl_id)s and jour BETWEEN %(datedebut)s AND %(datefin)s",
# vars() )
@ -487,7 +490,7 @@ class ZAbsences(
self._AnnuleAbsence(etudid, jour, matin, moduleimpl_id, REQUEST)
return
cnx = self.GetDBConnexion()
cursor = cnx.cursor(cursor_factory=ScoDocCursor)
cursor = cnx.cursor(cursor_factory=notesdb.ScoDocCursor)
# supr les absences non justifiees
for date in dates:
cursor.execute(
@ -534,7 +537,7 @@ class ZAbsences(
else:
modul = ""
cnx = self.GetDBConnexion()
cursor = cnx.cursor(cursor_factory=ScoDocCursor)
cursor = cnx.cursor(cursor_factory=notesdb.ScoDocCursor)
cursor.execute(
"""SELECT COUNT(*) AS NbAbs FROM (
SELECT DISTINCT A.JOUR, A.MATIN
@ -565,7 +568,7 @@ class ZAbsences(
else:
modul = ""
cnx = self.GetDBConnexion()
cursor = cnx.cursor(cursor_factory=ScoDocCursor)
cursor = cnx.cursor(cursor_factory=notesdb.ScoDocCursor)
cursor.execute(
"""SELECT COUNT(*) AS NbAbsJust FROM (
SELECT DISTINCT A.JOUR, A.MATIN
@ -588,7 +591,7 @@ class ZAbsences(
def _ListeAbsDate(self, etudid, beg_date, end_date):
# Liste des absences et justifs entre deux dates
cnx = self.GetDBConnexion()
cursor = cnx.cursor(cursor_factory=ScoDocCursor)
cursor = cnx.cursor(cursor_factory=notesdb.ScoDocCursor)
cursor.execute(
"""SELECT jour, matin, estabs, estjust, description FROM ABSENCES A
WHERE A.ETUDID = %(etudid)s
@ -598,7 +601,6 @@ class ZAbsences(
vars(),
)
Abs = cursor.dictfetchall()
# log('ListeAbsDate: abs=%s' % Abs)
# remove duplicates
A = {} # { (jour, matin) : abs }
for a in Abs:
@ -625,7 +627,6 @@ class ZAbsences(
# sort
R = A.values()
R.sort(key=lambda x: (x["begin"]))
# log('R=%s' % R)
return R
security.declareProtected(ScoView, "ListeAbsJust")
@ -633,7 +634,7 @@ class ZAbsences(
def ListeAbsJust(self, etudid, datedebut):
"Liste des absences justifiees (par ordre chronologique)"
cnx = self.GetDBConnexion()
cursor = cnx.cursor(cursor_factory=ScoDocCursor)
cursor = cnx.cursor(cursor_factory=notesdb.ScoDocCursor)
cursor.execute(
"""SELECT DISTINCT A.ETUDID, A.JOUR, A.MATIN FROM ABSENCES A, ABSENCES B
WHERE A.ETUDID = %(etudid)s
@ -654,7 +655,7 @@ class ZAbsences(
def ListeAbsNonJust(self, etudid, datedebut):
"Liste des absences NON justifiees (par ordre chronologique)"
cnx = self.GetDBConnexion()
cursor = cnx.cursor(cursor_factory=ScoDocCursor)
cursor = cnx.cursor(cursor_factory=notesdb.ScoDocCursor)
cursor.execute(
"""SELECT ETUDID, JOUR, MATIN FROM ABSENCES A
WHERE A.ETUDID = %(etudid)s
@ -680,7 +681,7 @@ class ZAbsences(
Si only_no_abs: seulement les justificatifs correspondant aux jours sans absences relevées.
"""
cnx = self.GetDBConnexion()
cursor = cnx.cursor(cursor_factory=ScoDocCursor)
cursor = cnx.cursor(cursor_factory=notesdb.ScoDocCursor)
req = """SELECT DISTINCT ETUDID, JOUR, MATIN FROM ABSENCES A
WHERE A.ETUDID = %(etudid)s
AND A.ESTJUST
@ -704,7 +705,7 @@ class ZAbsences(
"Description associee a l'absence"
if not cursor:
cnx = self.GetDBConnexion()
cursor = cnx.cursor(cursor_factory=ScoDocCursor)
cursor = cnx.cursor(cursor_factory=notesdb.ScoDocCursor)
a = a.copy()
# a['jour'] = a['jour'].date()
if a["matin"]: # devrait etre booleen... :-(
@ -732,7 +733,6 @@ class ZAbsences(
if desc:
return "(%s) %s" % (desc, module)
return desc
if module:
return module
return ""
@ -745,7 +745,7 @@ class ZAbsences(
is_just: idem
"""
cnx = self.GetDBConnexion()
cursor = cnx.cursor(cursor_factory=ScoDocCursor)
cursor = cnx.cursor(cursor_factory=notesdb.ScoDocCursor)
req = """SELECT DISTINCT etudid, jour, matin FROM ABSENCES A
WHERE A.jour = %(date)s
"""
@ -769,7 +769,7 @@ class ZAbsences(
def ListeAbsNonJustJour(self, date, am=True, pm=True):
"Liste des absences non justifiees ce jour"
cnx = self.GetDBConnexion()
cursor = cnx.cursor(cursor_factory=ScoDocCursor)
cursor = cnx.cursor(cursor_factory=notesdb.ScoDocCursor)
reqa = ""
if not am:
reqa += " AND NOT matin "
@ -849,7 +849,7 @@ class ZAbsences(
def CalSelectWeek(self, year=None, REQUEST=None):
"display calendar allowing week selection"
if not year:
year = AnneeScolaire(REQUEST)
year = scu.AnneeScolaire(REQUEST)
sems = sco_formsemestre.do_formsemestre_list(self)
if not sems:
js = ""
@ -886,10 +886,9 @@ class ZAbsences(
security.declareProtected(ScoView, "ListMondays")
def ListMondays(self, year=None, REQUEST=None):
"""return list of mondays (ISO dates), from september to june
"""
"""return list of mondays (ISO dates), from september to june"""
if not year:
year = AnneeScolaire(REQUEST)
year = scu.AnneeScolaire(REQUEST)
d = ddmmyyyy("1/9/%d" % year, work_saturday=self.is_work_saturday())
while d.weekday != 0:
d = d.next()
@ -934,7 +933,7 @@ class ZAbsences(
):
"Saisie hebdomadaire des absences"
if not moduleimpl_id:
moduleimp_id = None
moduleimpl_id = None
groups_infos = sco_groups_view.DisplayedGroupsInfos(
self, group_ids, REQUEST=REQUEST
@ -953,20 +952,32 @@ class ZAbsences(
)
formsemestre_id = groups_infos.formsemestre_id
require_module = self.get_preference("abs_require_module", formsemestre_id)
etuds = [
self.getEtudInfo(etudid=m["etudid"], filled=True)[0]
for m in groups_infos.members
]
# Restreint aux inscrits au module sélectionné
if moduleimpl_id:
mod_inscrits = set(
[
x["etudid"]
for x in self.Notes.do_moduleimpl_inscription_list(
moduleimpl_id=moduleimpl_id
)
]
)
etuds = [e for e in etuds if e["etudid"] in mod_inscrits]
nt = self.Notes._getNotesCache().get_NotesTable(self.Notes, formsemestre_id)
sem = sco_formsemestre.do_formsemestre_list(
self, {"formsemestre_id": formsemestre_id}
)[0]
# calcule dates jours de cette semaine
datessem = [DateDMYtoISO(datelundi)]
for jour in self.day_names()[1:]:
# liste de dates iso "yyyy-mm-dd"
datessem = [notesdb.DateDMYtoISO(datelundi)]
for _ in self.day_names()[1:]:
datessem.append(self.NextISODay(datessem[-1]))
#
if groups_infos.tous_les_etuds_du_sem:
gr_tit = "en"
@ -1045,7 +1056,7 @@ class ZAbsences(
)
H += self._gen_form_saisie_groupe(
etuds, self.day_names(), datessem, destination, None, moduleimpl_id
etuds, datessem, destination, moduleimpl_id, require_module
)
H.append(self.sco_footer(REQUEST))
@ -1063,8 +1074,7 @@ class ZAbsences(
moduleimpl_id=None,
REQUEST=None,
):
"""Saisie des absences sur une journée sur un semestre (ou intervalle de dates) entier
"""
"""Saisie des absences sur une journée sur un semestre (ou intervalle de dates) entier"""
# log('SignaleAbsenceGrSemestre: moduleimpl_id=%s destination=%s' % (moduleimpl_id, destination))
groups_infos = sco_groups_view.DisplayedGroupsInfos(
self, group_ids, REQUEST=REQUEST
@ -1075,23 +1085,30 @@ class ZAbsences(
+ "<h3>Aucun étudiant !</h3>"
+ self.sco_footer(REQUEST)
)
formsemestre_id = groups_infos.formsemestre_id
require_module = self.get_preference("abs_require_module", formsemestre_id)
etuds = [
self.getEtudInfo(etudid=m["etudid"], filled=True)[0]
for m in groups_infos.members
]
if not moduleimpl_id:
moduleimp_id = None
base_url_noweeks = (
"SignaleAbsenceGrSemestre?datedebut=%s&amp;datefin=%s&amp;%s&amp;destination=%s"
% (
datedebut,
datefin,
groups_infos.groups_query_args,
urllib.quote(destination),
# Restreint aux inscrits au module sélectionné
if moduleimpl_id:
mod_inscrits = set(
[
x["etudid"]
for x in self.Notes.do_moduleimpl_inscription_list(
moduleimpl_id=moduleimpl_id
)
]
)
etuds = [e for e in etuds if e["etudid"] in mod_inscrits]
if not moduleimpl_id:
moduleimpl_id = None
base_url_noweeks = "SignaleAbsenceGrSemestre?datedebut=%s&amp;datefin=%s&amp;%s&amp;destination=%s" % (
datedebut,
datefin,
groups_infos.groups_query_args,
urllib.quote(destination),
)
base_url = (
base_url_noweeks + "&amp;nbweeks=%s" % nbweeks
@ -1139,7 +1156,6 @@ class ZAbsences(
if moduleimpl_id:
url_link_semaines += "&amp;moduleimpl_id=" + moduleimpl_id
#
colnames = [str(x) for x in dates]
dates = [x.ISO() for x in dates]
dayname = self.day_names()[jourdebut.weekday]
@ -1147,9 +1163,9 @@ class ZAbsences(
gr_tit = "en"
else:
if len(groups_infos.group_ids) > 1:
p = "des groupes"
p = "des groupes "
else:
p = "du groupe"
p = "du groupe "
gr_tit = (
p + '<span class="fontred">' + groups_infos.groups_titles + "</span>"
)
@ -1209,26 +1225,45 @@ class ZAbsences(
sel = "selected" # aucun module specifie
H.append(
"""<p>
Module concerné par ces absences (optionnel): <select id="moduleimpl_id" name="moduleimpl_id" onchange="document.location='%(url)s&amp;moduleimpl_id='+document.getElementById('moduleimpl_id').value">
Module concerné par ces absences (%(optionel_txt)s):
<select id="moduleimpl_id" name="moduleimpl_id"
onchange="document.location='%(url)s&amp;moduleimpl_id='+document.getElementById('moduleimpl_id').value">
<option value="" %(sel)s>non spécifié</option>
%(menu_module)s
</select>
</p>"""
% {"menu_module": menu_module, "url": base_url, "sel": sel}
% {
"menu_module": menu_module,
"url": base_url,
"sel": sel,
"optionel_txt": '<span class="redboldtext">requis</span>'
if require_module
else "optionnel",
}
)
H += self._gen_form_saisie_groupe(
etuds, colnames, dates, destination, dayname, moduleimpl_id
etuds, dates, destination, moduleimpl_id, require_module
)
H.append(self.sco_footer(REQUEST))
return "\n".join(H)
def _gen_form_saisie_groupe(
self, etuds, colnames, dates, destination="", dayname="", moduleimpl_id=None
self, etuds, dates, destination="", moduleimpl_id=None, require_module=False
):
"""Formulaire saisie absences
Args:
etuds: liste des étudiants
dates: liste de dates iso, par exemple: [ '2020-12-24', ... ]
moduleimpl_id: optionnel, module concerné.
"""
H = [
"""
<script type="text/javascript">
$(function() {
$(".abs_form_table input").prop( "disabled", %s );
});
function colorize(obj) {
if (obj.checked) {
obj.parentNode.className = 'absent';
@ -1250,27 +1285,34 @@ class ZAbsences(
<table rules="cols" frame="box" class="abs_form_table">
<tr><th class="formabs_contetud">%d étudiants</th>
"""
% len(etuds)
% (
"true" if (require_module and not moduleimpl_id) else "false",
len(etuds),
)
]
# Dates
odates = [datetime.date(*[int(x) for x in d.split("-")]) for d in dates]
# Titres colonnes
if dayname:
for jour in colnames:
H.append(
'<th colspan="2" width="100px" style="padding-left: 5px; padding-right: 5px;">'
+ dayname
+ "</th>"
)
H.append("</tr><tr><td>&nbsp;</td>")
for jour in colnames:
noms_jours = [] # eg [ "Lundi", "mardi", "Samedi", ... ]
jn = self.day_names()
for d in odates:
idx_jour = d.weekday()
noms_jours.append(jn[idx_jour])
for jour in noms_jours:
H.append(
'<th colspan="2" width="100px" style="padding-left: 5px; padding-right: 5px;">'
+ jour
+ "</th>"
)
H.append("</tr><tr><td>&nbsp;</td>")
H.append("<th>AM</th><th>PM</th>" * len(colnames))
for d in odates:
H.append(
'<th colspan="2" width="100px" style="padding-left: 5px; padding-right: 5px;">'
+ d.strftime("%d/%m/%Y")
+ "</th>"
)
H.append("</tr><tr><td>&nbsp;</td>")
H.append("<th>AM</th><th>PM</th>" * len(dates))
H.append("</tr>")
#
if not etuds:
@ -1309,17 +1351,26 @@ class ZAbsences(
checked = "checked"
else:
checked = ""
# bulle lors du passage souris
coljour = DAYNAMES[
(calendar.weekday(int(date[:4]), int(date[5:7]), int(date[8:])))
]
datecol = coljour + " " + date[8:] + "/" + date[5:7] + "/" + date[:4]
bulle_am = '"' + etud["nomprenom"] + " - " + datecol + ' (matin)"'
bulle_pm = '"' + etud["nomprenom"] + " - " + datecol + ' (ap.midi)"'
H.append(
'<td class="%s"><input type="checkbox" name="abslist:list" value="%s" %s onclick="on_toggled(this, \'%s\', \'%s\')"/></td>'
'<td class="%s"><a title=%s><input type="checkbox" name="abslist:list" value="%s" %s onclick="on_toggled(this, \'%s\', \'%s\')"/></a></td>'
% (
td_matin_class,
bulle_am,
etudid + ":" + date + ":" + "am",
checked,
etudid,
date + ":am",
)
)
# apres midi
# après-midi
if self.CountAbs(
etudid, date, date, False, moduleimpl_id=moduleimpl_id
):
@ -1327,8 +1378,14 @@ class ZAbsences(
else:
checked = ""
H.append(
'<td><input type="checkbox" name="abslist:list" value="%s" %s onclick="on_toggled(this, \'%s\', \'%s\')"/></td>'
% (etudid + ":" + date + ":" + "pm", checked, etudid, date + ":pm")
'<td><a title=%s><input type="checkbox" name="abslist:list" value="%s" %s onclick="on_toggled(this, \'%s\', \'%s\')"/></a></td>'
% (
bulle_pm,
etudid + ":" + date + ":" + "pm",
checked,
etudid,
date + ":pm",
)
)
H.append("</tr>")
H.append("</table>")
@ -1373,14 +1430,13 @@ class ZAbsences(
absjust_only=0,
REQUEST=None,
):
"""Tables des absences justifiees et non justifiees d'un étudiant sur l'année en cours
"""
"""Tables des absences justifiees et non justifiees d'un étudiant sur l'année en cours"""
absjust = self.ListeAbsJust(etudid=etudid, datedebut=datedebut)
absnonjust = self.ListeAbsNonJust(etudid=etudid, datedebut=datedebut)
# examens ces jours là ?
if with_evals:
cnx = self.GetDBConnexion()
cursor = cnx.cursor(cursor_factory=ScoDocCursor)
cursor = cnx.cursor(cursor_factory=notesdb.ScoDocCursor)
for a in absnonjust + absjust:
cursor.execute(
"""select eval.*
@ -1407,7 +1463,7 @@ class ZAbsences(
if x:
return "matin"
else:
return "après midi"
return "après-midi"
def descr_exams(a):
if not a.has_key("evals"):
@ -1499,10 +1555,9 @@ class ZAbsences(
format="html",
REQUEST=None,
):
"""Liste les absences de groupes
"""
datedebut = DateDMYtoISO(debut)
datefin = DateDMYtoISO(fin)
"""Liste les absences de groupes"""
datedebut = notesdb.DateDMYtoISO(debut)
datefin = notesdb.DateDMYtoISO(fin)
# Informations sur les groupes à afficher:
groups_infos = sco_groups_view.DisplayedGroupsInfos(
self, group_ids, REQUEST=REQUEST
@ -1603,7 +1658,7 @@ class ZAbsences(
base_url="%s&amp;formsemestre_id=%s&amp;debut=%s&amp;fin=%s"
% (groups_infos.base_url, formsemestre_id, debut, fin),
filename="etat_abs_"
+ make_filename(
+ scu.make_filename(
"%s de %s" % (groups_infos.groups_filename, sem["titreannee"])
),
caption=title,
@ -1632,26 +1687,20 @@ ou entrez une date pour visualiser les absents un jour donné&nbsp;:
def EtatAbsencesDate(
self, group_ids=[], date=None, REQUEST=None # list of groups to display
):
"""Etat des absences pour un groupe à une date donnée
"""
"""Etat des absences pour un groupe à une date donnée"""
# Informations sur les groupes à afficher:
groups_infos = sco_groups_view.DisplayedGroupsInfos(
self, group_ids, REQUEST=REQUEST
)
formsemestre_id = groups_infos.formsemestre_id
sem = sco_formsemestre.do_formsemestre_list(
self, {"formsemestre_id": formsemestre_id}
)[0]
H = [self.sco_header(page_title="Etat des absences", REQUEST=REQUEST)]
if date:
dateiso = DateDMYtoISO(date)
dateiso = notesdb.DateDMYtoISO(date)
nbetud = 0
t_nbabsjustam = 0
t_nbabsam = 0
t_nbabsjustpm = 0
t_nbabspm = 0
etuds = self.getEtudInfoGroupes(groups_infos.group_ids)
H.append("<h2>Etat des absences le %s</h2>" % date)
H.append("<h2>État des absences le %s</h2>" % date)
H.append(
"""<table border="0" cellspacing="4" cellpadding="0">
<tr><th>&nbsp;</th>
@ -1738,7 +1787,7 @@ ou entrez une date pour visualiser les absents un jour donné&nbsp;:
etudid=etudid, code_nip=code_nip, REQUEST=REQUEST, filled=True
)
if not etuds:
return log_unknown_etud(self, REQUEST=REQUEST)
return scu.log_unknown_etud(self, REQUEST=REQUEST)
etud = etuds[0]
# check dates
begin_date = ParseDateTimeUTC(begin) # may raises ValueError
@ -1763,7 +1812,7 @@ ou entrez une date pour visualiser les absents un jour donné&nbsp;:
if xml_reply:
# Renvoie le nouveau billet en XML
if REQUEST:
REQUEST.RESPONSE.setHeader("content-type", XML_MIMETYPE)
REQUEST.RESPONSE.setHeader("content-type", scu.XML_MIMETYPE)
billets = billet_absence_list(cnx, {"billet_id": billet_id})
tab = self._tableBillets(billets, etud=etud)
@ -1827,12 +1876,12 @@ ou entrez une date pour visualiser les absents un jour donné&nbsp;:
if b["abs_begin"].hour < 12:
m = " matin"
else:
m = " après midi"
m = " après-midi"
b["abs_begin_str"] = b["abs_begin"].strftime("%d/%m/%Y") + m
if b["abs_end"].hour < 12:
m = " matin"
else:
m = " après midi"
m = " après-midi"
b["abs_end_str"] = b["abs_end"].strftime("%d/%m/%Y") + m
if b["etat"] == 0:
if b["justified"] == 0:
@ -1884,11 +1933,10 @@ ou entrez une date pour visualiser les absents un jour donné&nbsp;:
security.declareProtected(ScoView, "listeBilletsEtud")
def listeBilletsEtud(self, etudid=False, REQUEST=None, format="html"):
"""Liste billets pour un etudiant
"""
"""Liste billets pour un etudiant"""
etuds = self.getEtudInfo(etudid=etudid, filled=1, REQUEST=REQUEST)
if not etuds:
return log_unknown_etud(self, format=format, REQUEST=REQUEST)
return scu.log_unknown_etud(self, format=format, REQUEST=REQUEST)
etud = etuds[0]
cnx = self.GetDBConnexion()
@ -1899,8 +1947,7 @@ ou entrez une date pour visualiser les absents un jour donné&nbsp;:
security.declareProtected(ScoView, "XMLgetBilletsEtud")
def XMLgetBilletsEtud(self, etudid=False, REQUEST=None):
"""Liste billets pour un etudiant
"""
"""Liste billets pour un etudiant"""
if not self.get_preference("handle_billets_abs"):
return ""
t0 = time.time()
@ -1937,8 +1984,7 @@ ou entrez une date pour visualiser les absents un jour donné&nbsp;:
security.declareProtected(ScoAbsChange, "deleteBilletAbsence")
def deleteBilletAbsence(self, billet_id, REQUEST=None, dialog_confirmed=False):
"""Supprime un billet.
"""
"""Supprime un billet."""
cnx = self.GetDBConnexion()
billets = billet_absence_list(cnx, {"billet_id": billet_id})
if not billets:
@ -1962,7 +2008,7 @@ ou entrez une date pour visualiser les absents un jour donné&nbsp;:
def _ProcessBilletAbsence(self, billet, estjust, description, REQUEST):
"""Traite un billet: ajoute absence(s) et éventuellement justificatifs,
et change l'état du billet à 1.
NB: actuellement, les heures ne sont utilisées que pour déterminer si matin et/ou après midi.
NB: actuellement, les heures ne sont utilisées que pour déterminer si matin et/ou après-midi.
"""
cnx = self.GetDBConnexion()
if billet["etat"] != 0:
@ -1974,7 +2020,7 @@ ou entrez une date pour visualiser les absents un jour donné&nbsp;:
datedebut = billet["abs_begin"].strftime("%d/%m/%Y")
datefin = billet["abs_end"].strftime("%d/%m/%Y")
dates = self.DateRangeISO(datedebut, datefin)
# commence apres midi ?
# commence après-midi ?
if dates and billet["abs_begin"].hour > 11:
self._AddAbsence(
billet["etudid"], dates[0], 0, estjust, REQUEST, description=description
@ -2107,8 +2153,8 @@ ou entrez une date pour visualiser les absents un jour donné&nbsp;:
Abs = self._ListeAbsDate(etud["etudid"], beg_date, end_date)
REQUEST.RESPONSE.setHeader("content-type", XML_MIMETYPE)
doc = jaxml.XML_document(encoding=SCO_ENCODING)
REQUEST.RESPONSE.setHeader("content-type", scu.XML_MIMETYPE)
doc = jaxml.XML_document(encoding=scu.SCO_ENCODING)
doc.absences(etudid=etud["etudid"], beg_date=beg_date, end_date=end_date)
doc._push()
for a in Abs:
@ -2126,7 +2172,7 @@ ou entrez une date pour visualiser les absents un jour donné&nbsp;:
return repr(doc)
_billet_absenceEditor = EditableTable(
_billet_absenceEditor = notesdb.EditableTable(
"billet_absence",
"billet_id",
(
@ -2210,7 +2256,6 @@ def MonthTableTail():
def MonthTableBody(
month, year, events=[], halfday=0, trattributes="", work_saturday=False, pad_width=8
):
# log('XXX events=%s' % events)
firstday, nbdays = calendar.monthrange(year, month)
localtime = time.localtime()
current_weeknum = time.strftime("%U", localtime)
@ -2270,7 +2315,7 @@ def MonthTableBody(
if href:
href = 'href="%s"' % href
if descr:
descr = 'title="%s"' % descr
descr = 'title="%s"' % cgi.escape(descr, quote=True)
if href or descr:
cc.append("<a %s %s>" % (href, descr))
@ -2362,7 +2407,7 @@ def MonthTableBody(
if href:
href = 'href="%s"' % href
if descr:
descr = 'title="%s"' % descr
descr = 'title="%s"' % cgi.escape(descr, quote=True)
if href or descr:
cc.append("<a %s %s>" % (href, descr))
if legend or d == 1:
@ -2443,8 +2488,8 @@ class CAbsSemEtud:
self.sem = sco_formsemestre.get_formsemestre(
self.context, self.sem["formsemestre_id"]
)
debut_sem = DateDMYtoISO(self.sem["date_debut"])
fin_sem = DateDMYtoISO(self.sem["date_fin"])
debut_sem = notesdb.DateDMYtoISO(self.sem["date_debut"])
fin_sem = notesdb.DateDMYtoISO(self.sem["date_fin"])
self._CountAbs = self.context.Absences.CountAbs(
etudid=self.etudid, debut=debut_sem, fin=fin_sem

View File

@ -5,7 +5,7 @@
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved.
# Copyright (c) 1999 - 2021 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

View File

@ -5,7 +5,7 @@
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved.
# Copyright (c) 1999 - 2021 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
@ -1531,6 +1531,8 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl
)
_expr_help = """<p class="help">Expérimental: formule de calcul de la moyenne %(target)s</p>
<p class="help">Attention: l'utilisation de formules ralenti considérablement
les traitements. A utiliser uniquement dans els cas ne pouvant pas être traités autrement.</p>
<p class="help">Dans la formule, les variables suivantes sont définies:</p>
<ul class="help">
<li><tt>moy</tt> la moyenne, calculée selon la règle standard (moyenne pondérée)</li>
@ -1542,8 +1544,9 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl
<li>Nombre d'absences: <tt>nb_abs</tt>, <tt>nb_abs_just</tt>, <tt>nb_abs_nojust</tt> (en demi-journées)</li>
</ul>
<p class="help">Les éléments des vecteurs sont ordonnés dans l'ordre des %(objs)s%(ordre)s.</p>
<p class="help">Les fonctions suivantes sont utilisables: <tt>abs, cmp, dot, len, map, max, min, pow, reduce, round, sum, ifelse</tt></p>
<p class="help">La notation <tt>V(1,2,3)</tt> représente un vecteur <tt>(1,2,3)</tt></p>
<p class="help">Les fonctions suivantes sont utilisables: <tt>abs, cmp, dot, len, map, max, min, pow, reduce, round, sum, ifelse</tt>.</p>
<p class="help">La notation <tt>V(1,2,3)</tt> représente un vecteur <tt>(1,2,3)</tt>.</p>
<p class="help"></p>Pour indiquer que la note calculée n'existe pas, utiliser la chaîne <tt>'NA'</tt>.</p>
<p class="help">Vous pouvez désactiver la formule (et revenir au mode de calcul "classique")
en supprimant le texte ou en faisant précéder la première ligne par <tt>#</tt></p>
"""
@ -1617,7 +1620,7 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl
security.declareProtected(ScoView, "view_module_abs")
def view_module_abs(self, REQUEST, moduleimpl_id, format="html"):
"""Visulalisation des absences a un module"""
"""Visualisation des absences a un module"""
M = self.do_moduleimpl_withmodule_list(moduleimpl_id=moduleimpl_id)[0]
sem = sco_formsemestre.get_formsemestre(self, M["formsemestre_id"])
debut_sem = DateDMYtoISO(sem["date_debut"])

View File

@ -5,7 +5,7 @@
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved.
# Copyright (c) 1999 - 2021 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
@ -308,6 +308,7 @@ class ZScoDoc(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Imp
raise ValueError("nom de departement invalide")
if not pass2:
# 1- Creation de repertoire Dept
log("creating Zope folder " + DeptId)
add_method = self.manage_addProduct["OFSP"].manage_addFolder
add_method(DeptId, title="Site dept. " + DeptId)
@ -315,10 +316,12 @@ class ZScoDoc(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Imp
if not pass2:
# 2- Creation du repertoire Fotos
log("creating Zope folder %s/Fotos" % DeptId)
add_method = DeptFolder.manage_addProduct["OFSP"].manage_addFolder
add_method("Fotos", title="Photos identites " + DeptId)
# 3- Creation instance ScoDoc
log("creating Zope ZScolar instance")
add_method = DeptFolder.manage_addProduct["ScoDoc"].manage_addZScolarForm
return add_method(DeptId, REQUEST=REQUEST)

View File

@ -5,7 +5,7 @@
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved.
# Copyright (c) 1999 - 2021 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
@ -277,7 +277,7 @@ class ZScoUsers(
def user_info(self, user_name=None, user=None):
"""Donne infos sur l'utilisateur (qui peut ne pas etre dans notre base).
Si user_name est specifie, interroge la BD. Sinon, user doit etre un dict.
Si user_name est specifie, interroge la BD. Sinon, user doit etre un dict.
"""
if user_name:
infos = self._user_list(args={"user_name": user_name})
@ -919,7 +919,7 @@ class ZScoUsers(
self, edit, user_name="", nom="", prenom="", email="", roles=[]
):
"""Vérifie que et utilisateur peut etre crée (edit=0) ou modifié (edit=1)
Cherche homonymes.
Cherche homonymes.
returns (ok, msg)
- ok : si vrai, peut continuer avec ces parametres
(si ok est faux, l'utilisateur peut quand même forcer la creation)
@ -1252,8 +1252,7 @@ class ZScoUsers(
security.declareProtected(ScoView, "get_user_name_from_nomplogin")
def get_user_name_from_nomplogin(self, nomplogin):
"""Returns user_name (login) from nomplogin
"""
"""Returns user_name (login) from nomplogin"""
m = re.match(r".*\((.*)\)", nomplogin.strip())
if m:
return m.group(1)

View File

@ -5,7 +5,7 @@
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved.
# Copyright (c) 1999 - 2021 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
@ -2037,49 +2037,8 @@ function tweakmenu( gname ) {
)
if not edit:
# creation d'un etudiant
etudid = scolars.etudident_create(
cnx, tf[2], context=self, REQUEST=REQUEST
)
# crée une adresse vide (chaque etudiant doit etre dans la table "adresse" !)
adresse_id = scolars.adresse_create(
cnx,
{
"etudid": etudid,
"typeadresse": "domicile",
"description": "(creation individuelle)",
},
)
# event
scolars.scolar_events_create(
cnx,
args={
"etudid": etudid,
"event_date": time.strftime("%d/%m/%Y"),
"formsemestre_id": None,
"event_type": "CREATION",
},
)
# log
logdb(
REQUEST,
cnx,
method="etudident_edit_form",
etudid=etudid,
msg="creation initiale",
)
etud = scolars.etudident_list(cnx, {"etudid": etudid})[0]
self.fillEtudsInfo([etud])
etud["url"] = "ficheEtud?etudid=%(etudid)s" % etud
sco_news.add(
self,
REQUEST,
typ=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"],
)
etud = scolars.create_etud(self, cnx, args=tf[2], REQUEST=REQUEST)
etudid = etud["etudid"]
else:
# modif d'un etudiant
scolars.etudident_edit(cnx, tf[2], context=self, REQUEST=REQUEST)

View File

@ -5,7 +5,7 @@
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved.
# Copyright (c) 1999 - 2021 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

View File

@ -5,7 +5,7 @@
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved.
# Copyright (c) 1999 - 2021 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

View File

@ -15,19 +15,37 @@ source utils.sh
check_uid_root "$0"
usage() {
echo "$0 [-n DEPT]"
echo "(default to interactive mode)"
exit 1
}
[ $# = 0 ] || [ $# = 2 ] || usage
echo -n "Nom du departement (un mot sans ponctuation, exemple \"Info\"): "
read -r DEPT
if [ "$1" = "-n" ]
then
interactive=0
if [ $# -lt 2 ]
then
usage
fi
DEPT=$2
else
interactive=1
echo -n "Nom du departement (un mot sans ponctuation, exemple \"Info\"): "
read -r DEPT
fi
if [[ ! "$DEPT" =~ ^[A-Za-z0-9]+$ ]]
then
echo 'Nom de departement invalide !'
exit 1
exit 2
fi
export DEPT
export db_name=SCO$(to_upper "$DEPT")
db_name=SCO$(to_upper "$DEPT")
export db_name
cfg_pathname="${SCODOC_VAR_DIR}/config/depts/$DEPT".cfg
@ -41,29 +59,39 @@ fi
init_postgres_user
# ----------------------- Create database
su -c ./create_database.sh $POSTGRES_SUPERUSER
su -c ./create_database.sh "$POSTGRES_SUPERUSER"
# ----------------------- Create tables
# POSTGRES_USER == regular unix user (www-data)
su -c ./initialize_database.sh $POSTGRES_USER
# ----------------------- Enregistre fichier config
echo "dbname=$db_name" > "$cfg_pathname"
# ----------------------- Force mise à jour
echo -n "Voulez vous mettre a jour ScoDoc (tres recommande) ? (y/n) [y] "
read -r ans
if [ "$(norm_ans "$ans")" != 'N' ]
if [ "$interactive" = 1 ]
then
(cd "$SCODOC_DIR/config"; ./upgrade.sh)
su -c ./initialize_database.sh "$POSTGRES_USER"
else
su -c ./initialize_database.sh "$POSTGRES_USER" > /dev/null 2>&1
fi
# -----------------------
echo
echo " Departement $DEPT cree"
echo
echo " Attention: la base de donnees n'a pas de copies de sauvegarde"
echo
echo " Maintenant, vous pouvez ajouter le departement via l'application web"
echo " en suivant le lien \"Administration de ScoDoc\" sur la page d'accueil."
echo
# ----------------------- Enregistre fichier config
echo "dbname=${db_name}" > "$cfg_pathname"
if [ "$interactive" = 1 ]
then
# ----------------------- Force mise à jour
echo -n "Voulez vous mettre a jour ScoDoc (tres recommande) ? (y/n) [y] "
read -r ans
if [ "$(norm_ans "$ans")" != 'N' ]
then
(cd "$SCODOC_DIR/config" || terminate "no config directory"; ./upgrade.sh)
fi
# -----------------------
echo
echo " Departement $DEPT cree"
echo
echo " Attention: la base de donnees n'a pas de copies de sauvegarde"
echo
echo " Maintenant, vous pouvez ajouter le departement via l'application web"
echo " en suivant le lien \"Administration de ScoDoc\" sur la page d'accueil."
echo
fi

View File

@ -17,23 +17,38 @@
source config.sh
source utils.sh
check_uid_root "$0"
echo
echo "Ce script supprime la base de donnees ScoDoc d'un departement"
echo
echo "Attention: le departement doit au prealable avoir ete supprime via l'interface web !"
echo "faites le AVANT d'executer ce script !!!"
echo
echo -n "Nom du departement a supprimer (un mot sans ponctuation, exemple \"Info\"): "
read -r DEPT
check_uid_root $0
usage() {
echo "$0 [-n DEPT]"
echo "(default to interactive mode)"
exit 1
}
[ $# = 0 ] || [ $# = 2 ] || usage
if [ "$1" = "-n" ]
then
interactive=0
if [ $# -lt 2 ]
then
usage
fi
DEPT=$2
else
interactive=1
echo
echo "Ce script supprime la base de donnees ScoDoc d'un departement"
echo
echo "Attention: le departement doit au prealable avoir ete supprime via l'interface web !"
echo "faites le AVANT d'executer ce script !!!"
echo
echo -n "Nom du departement a supprimer (un mot sans ponctuation, exemple \"Info\"): "
read -r DEPT
fi
if [[ ! "$DEPT" =~ ^[A-Za-z0-9]+$ ]]
then
echo "Nom de departement invalide !"
exit 1
fi
export DEPT
cfg_pathname="${SCODOC_VAR_DIR}/config/depts/$DEPT".cfg
@ -50,11 +65,14 @@ then
# suppression du fichier de config
/bin/rm -f "$cfg_pathname" || terminate "ne peux supprimer $cfg_pathname"
# relance ScoDoc
echo -n "Demarrer le serveur ScoDoc ? (y/n) [n]"
read -r ans
if [ "$(norm_ans "$ans")" = 'Y' ]
if [ "$interactive" = 1 ]
then
scodocctl start
echo -n "Demarrer le serveur ScoDoc ? (y/n) [n]"
read -r ans
if [ "$(norm_ans "$ans")" = 'Y' ]
then
scodocctl start
fi
fi
exit 0
else

0
config/migre-7-a-8.sh Executable file → Normal file
View File

View File

@ -57,7 +57,7 @@ then
echo "Opening tgz archive..."
tmp=$(mktemp -d)
chmod a+rx "$tmp"
cd "$tmp" || { echo "Fatal error: directory not available"; exit 1; }
cd "$tmp" || terminate "directory error"
tar xfz "$SRC"
SRC=$(ls -1d "$tmp"/*)
IS_TMP=1
@ -83,18 +83,18 @@ su -c "$SCODOC_DIR/config/psql_restore_databases.sh $PG_DUMPFILE" postgres
#
echo Copying data files...
rm -rf "$INSTANCE_DIR/var"
rm -rf "${INSTANCE_DIR:?}/var"
$COPY "$SRC/var" "$INSTANCE_DIR"
if [ ! -e "${SCODOC_VAR_DIR}/config/" ]
if [ ! -e "${SCODOC_VAR_DIR:?}/config/" ]
then
mkdir "${SCODOC_VAR_DIR}/config/"
chown www-data.www-data "${SCODOC_VAR_DIR}/config/"
chmod 775 "${SCODOC_VAR_DIR}/config/"
mkdir "${SCODOC_VAR_DIR:?}/config/"
chown www-data.www-data "${SCODOC_VAR_DIR:?}/config/"
chmod 775 "${SCODOC_VAR_DIR:?}/config/"
fi
rm -rf "$SCODOC_DIR/config/depts"
if [ -e "$SRC/depts" ]
rm -rf "${SCODOC_DIR:?}/config/depts"
if [ -e "${SRC:?}/depts" ]
then
# legacy depts => move them to var
$COPY "$SRC/depts" "${SCODOC_VAR_DIR}/config/"
@ -107,7 +107,7 @@ then
$COPY "$SRC/photos" "${SCODOC_VAR_DIR}/"
fi
rm -rf "$SCODOC_DIR/logos"
rm -rf "${SCODOC_DIR:?}/logos"
$COPY "$SRC/logos" "$SCODOC_DIR/"
mv "$SCODOC_DIR/config/scodoc_config.py" "$SCODOC_DIR/config/scodoc_config.py.$(date +%Y%m%d-%H%M%S)"
@ -119,7 +119,7 @@ then
iconv -f iso8859-15 -t utf-8 "$SCODOC_DIR/config/scodoc_config.py.orig" > "$SCODOC_DIR/config/scodoc_config.py"
fi
rm -rf "$INSTANCE_DIR/log"
rm -rf "${INSTANCE_DIR:?}/log"
$COPY "$SRC/log" "$INSTANCE_DIR/"
# Fix file ownership and access rights
@ -130,13 +130,13 @@ chown -R www-data.root "$SCODOC_DIR"
chmod -R 775 "$SCODOC_DIR"
# Remove tmp directory
if [ $IS_TMP = "1" ]
if [ "$IS_TMP" = "1" ]
then
rm -rf "$tmp"
rm -rf "${tmp}"
fi
# Mise a jour BD ScoDoc
cd "$SCODOC_DIR"/config || { echo "Fatal error: invalid directory"; exit 2; }
cd ${SCODOC_DIR:?}/config || terminate "no config directory"
./upgrade.sh
#

View File

@ -36,6 +36,12 @@ fi
INSTANCE_DIR=/opt/scodoc
SCODOC_DIR="$INSTANCE_DIR/Products/ScoDoc"
<<<<<<< HEAD
=======
source utils.sh
check_uid_root "$0"
>>>>>>> e2a2b0f0836fc6de922c35b77b236379783e7590
echo "Stopping ScoDoc..."
scodocctl stop
@ -43,9 +49,13 @@ scodocctl stop
echo "Dumping SQL database..."
chown postgres "$DEST"
su -c "pg_dumpall > \"$DEST\"/scodoc.dump.txt" postgres
if [ ! $? -eq 0 ]
if [ ! "$?" -eq 0 ]
then
<<<<<<< HEAD
printf "Error dumping postgresql database\nPlease check that SQL server is running\nAborting.\n"
=======
printf "Error dumping postgresql database\nPlease check that SQL server is running\nAborting."
>>>>>>> e2a2b0f0836fc6de922c35b77b236379783e7590
exit 1
fi
chown root "$DEST"
@ -85,6 +95,6 @@ cp -rp "$INSTANCE_DIR/log" "$DEST"
echo
echo "Archiving backup files in a $DEST.tgz..."
base=$(basename "$DEST")
(cd "$DEST"/..; tar cfz "$DEST".tgz "$base")
(cd "$DEST"/.. || terminate "directory error"; tar cfz "$DEST".tgz "$base")
echo "Done (you can copy " "$DEST"".tgz to destination machine)."

View File

@ -6,7 +6,7 @@
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved.
# Copyright (c) 1999 - 2021 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

View File

@ -31,6 +31,8 @@ nt = context.Notes._getNotesCache().get_NotesTable(context.Notes, formsemestre_i
"""
import pdb
from notesdb import *
from notes_log import log
from sco_utils import *
@ -78,6 +80,9 @@ class FakeUser:
def has_permission(self, op, context):
return True
def has_role(self, role):
return True
class DummyResponse:
"""Emulation vide de Reponse http Zope"""
@ -104,6 +109,7 @@ class DummyRequest:
self.URL1 = self.URL
self.URL0 = self.URL
self.BASE0 = "localhost"
self.REMOTE_HOST = "localhost"
self.REMOTE_ADDR = "127.0.0.1"
self.HTTP_REFERER = ""
self.REQUEST_METHOD = "get"

View File

@ -4,7 +4,27 @@
#
# Command: ./csv2rules.py misc/parcoursDUT.csv
#
from sco_codes_parcours import *
from sco_codes_parcours import (
DUTRule,
ADC,
ADJ,
ADM,
AJ,
ALL,
ATB,
ATJ,
ATT,
CMP,
NAR,
NEXT,
RA_OR_NEXT,
RA_OR_RS,
RAT,
REO,
REDOANNEE,
REDOSEM,
RS_OR_NEXT,
)
rules_source_file = "misc/parcoursDUT.csv"

View File

@ -5,7 +5,7 @@
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved.
# Copyright (c) 1999 - 2021 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
@ -45,9 +45,20 @@ from collections import OrderedDict
# XML generation package (apt-get install jaxml)
import jaxml
import json
from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Frame, PageBreak
from reportlab.platypus import Table, TableStyle, Image, KeepInFrame
from reportlab.lib.colors import Color
from reportlab.lib import styles
from reportlab.lib.units import inch, cm, mm
from reportlab.rl_config import defaultPageSize # pylint: disable=no-name-in-module
import sco_utils as scu
import sco_excel
from sco_pdf import *
import sco_pdf
from sco_pdf import SU
from notes_log import log
def mark_paras(L, tags):
@ -280,7 +291,7 @@ class GenTable:
if not row:
return "<tr></tr>" # empty row
if self.html_col_width: # XXXX Obsolete ?
if self.html_col_width:
std = ' style="width:%s;"' % self.html_col_width
else:
std = ""
@ -382,10 +393,6 @@ class GenTable:
else:
cls = ""
if self.html_col_width:
std = ' style="width:%s;"' % self.html_col_width
else:
std = ""
H = [self.html_before_table, "<table%s%s>" % (hid, cls)]
line_num = 0
@ -438,14 +445,14 @@ class GenTable:
if self.xls_link:
H.append(
' <a href="%s&amp;format=xls">%s</a>'
% (self.base_url, ICON_XLS)
% (self.base_url, scu.ICON_XLS)
)
if self.xls_link and self.pdf_link:
H.append("&nbsp;&nbsp;")
if self.pdf_link:
H.append(
' <a href="%s&amp;format=pdf">%s</a>'
% (self.base_url, ICON_PDF)
% (self.base_url, scu.ICON_PDF)
)
H.append("</p>")
@ -483,10 +490,10 @@ class GenTable:
"PDF representation: returns a ReportLab's platypus Table instance"
r = []
try:
PDFLOCK.acquire()
sco_pdf.PDFLOCK.acquire()
r = self._pdf()
finally:
PDFLOCK.release()
sco_pdf.PDFLOCK.release()
return r
def _pdf(self):
@ -513,7 +520,7 @@ class GenTable:
CellStyle.leading = 1.0 * self.preferences["SCOLAR_FONT_SIZE"] # vertical space
LINEWIDTH = 0.5
#
titles = ["<para><b>%s</b></para>" % x for x in self.get_titles_list()]
# titles = ["<para><b>%s</b></para>" % x for x in self.get_titles_list()]
pdf_style_list = []
Pt = [
[Paragraph(SU(str(x)), CellStyle) for x in line]
@ -530,10 +537,10 @@ class GenTable:
# log('len(Pt)=%s' % len(Pt))
# log( 'line lens=%s' % [ len(x) for x in Pt ] )
# log( 'style=\n%s' % pdf_style_list)
col_min = min([x[1][0] for x in pdf_style_list])
col_max = max([x[2][0] for x in pdf_style_list])
lin_min = min([x[1][1] for x in pdf_style_list])
lin_max = max([x[2][1] for x in pdf_style_list])
# col_min = min([x[1][0] for x in pdf_style_list])
# col_max = max([x[2][0] for x in pdf_style_list])
# lin_min = min([x[1][1] for x in pdf_style_list])
# lin_max = max([x[2][1] for x in pdf_style_list])
# log('col_min=%s col_max=%s lin_min=%s lin_max=%s' % (col_min, col_max, lin_min, lin_max))
T = Table(Pt, repeatRows=1, colWidths=self.pdf_col_widths, style=pdf_style_list)
@ -559,7 +566,7 @@ class GenTable:
The tag names <table> and <row> can be changed using
xml_outer_tag and xml_row_tag
"""
doc = jaxml.XML_document(encoding=SCO_ENCODING)
doc = jaxml.XML_document(encoding=scu.SCO_ENCODING)
getattr(doc, self.xml_outer_tag)(
id=self.table_id, origin=self.origin or "", caption=self.caption or ""
)
@ -593,7 +600,7 @@ class GenTable:
v = str(v)
r[cid] = v
d.append(r)
return json.dumps(d, encoding=SCO_ENCODING)
return json.dumps(d, encoding=scu.SCO_ENCODING)
def make_page(
self,
@ -637,11 +644,11 @@ class GenTable:
return "\n".join(H)
elif format == "pdf":
objects = self.pdf()
doc = pdf_basic_page(
doc = sco_pdf.pdf_basic_page(
objects, title=title, preferences=self.preferences, context=context
)
if publish:
return sendPDFFile(REQUEST, doc, filename + ".pdf")
return scu.sendPDFFile(REQUEST, doc, filename + ".pdf")
else:
return doc
elif format == "xls":
@ -653,16 +660,16 @@ class GenTable:
elif format == "text":
return self.text()
elif format == "csv":
return sendCSVFile(REQUEST, self.text(), filename + ".csv")
return scu.sendCSVFile(REQUEST, self.text(), filename + ".csv")
elif format == "xml":
xml = self.xml()
if REQUEST and publish:
REQUEST.RESPONSE.setHeader("content-type", XML_MIMETYPE)
REQUEST.RESPONSE.setHeader("content-type", scu.XML_MIMETYPE)
return xml
elif format == "json":
js = self.json()
if REQUEST and publish:
REQUEST.RESPONSE.setHeader("content-type", JSON_MIMETYPE)
REQUEST.RESPONSE.setHeader("content-type", scu.JSON_MIMETYPE)
return js
else:
log("make_page: format=%s" % format)
@ -687,7 +694,7 @@ class SeqGenTable:
def excel(self):
"""Export des genTables dans un unique fichier excel avec plusieurs feuilles tagguées"""
book = sco_excel.Workbook() # Le fichier xls en devenir
for (name, gt) in self.genTables.items():
for (_, gt) in self.genTables.items():
gt.excel(wb=book) # Ecrit dans un fichier excel
return book.savetostr()

View File

@ -5,7 +5,7 @@
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved.
# Copyright (c) 1999 - 2021 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
@ -257,6 +257,5 @@ def sco_header(
def sco_footer(context, REQUEST=None):
"""Main HTMl pages footer
"""
"""Main HTMl pages footer"""
return """</div><!-- /gtrcontent -->""" + CUSTOM_HTML_FOOTER + """</body></html>"""

View File

@ -5,7 +5,7 @@
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved.
# Copyright (c) 1999 - 2021 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
@ -158,8 +158,7 @@ def sidebar(context, REQUEST=None):
def sidebar_dept(context, REQUEST=None):
"""Partie supérieure de la marge de gauche
"""
"""Partie supérieure de la marge de gauche"""
infos = {
"BASE0": REQUEST.BASE0,
"DeptIntranetTitle": context.get_preference("DeptIntranetTitle"),

View File

@ -5,7 +5,7 @@
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved.
# Copyright (c) 1999 - 2021 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
@ -49,7 +49,7 @@ def histogram_notes(notes):
"HTML code drawing histogram"
if not notes:
return ""
bins, H = listhistogram.ListHistogram(notes, 21, minmax=(0, 20))
_, H = listhistogram.ListHistogram(notes, 21, minmax=(0, 20))
D = ['<ul id="vhist-q-graph"><li class="vhist-qtr" id="vhist-q1"><ul>']
left = 5
colwidth = 16 # must match #q-graph li.bar width in stylesheet

View File

@ -10,53 +10,53 @@ from itertools import izip
class intervalmap(object):
"""
This class maps a set of intervals to a set of values.
>>> i = intervalmap()
>>> i[0:5] = '0-5'
>>> i[8:12] = '8-12'
>>> print i[2]
0-5
>>> print i[10]
8-12
>>> print repr(i[-1])
None
>>> print repr(i[17])
None
>>> i[4:9] = '4-9'
>>> print [(j,i[j]) for j in range(6)]
[(0, '0-5'), (1, '0-5'), (2, '0-5'), (3, '0-5'), (4, '4-9'), (5, '4-9')]
>>> print list(i.items())
[((0, 4), '0-5'), ((4, 9), '4-9'), ((9, 12), '8-12')]
>>> i[:0] = 'less than 0'
>>> i[-5]
'less than 0'
>>> i[0]
'0-5'
>>> print list(i.items())
[((None, 0), 'less than 0'), ((0, 4), '0-5'), ((4, 9), '4-9'), ((9, 12), '8-12')]
>>> i[21:] = 'more than twenty'
>>> i[42]
'more than twenty'
>>> i[10.5:15.5] = '10.5-15.5'
>>> i[11.5]
'10.5-15.5'
>>> i[0.5]
'0-5'
>>> print list(i.items())
[((None, 0),... ((9, 10.5), '8-12'), ((10.5, 15.5), '10.5-15.5'), ((21, None),...
>>> i = intervalmap()
>>> i[0:2] = 1
>>> i[2:8] = 2
>>> i[4:] = 3
>>> i[5:6] = 4
>>> i
{[0, 2] => 1, [2, 4] => 2, [4, 5] => 3, [5, 6] => 4, [6, None] => 3}
This class maps a set of intervals to a set of values.
>>> i = intervalmap()
>>> i[0:5] = '0-5'
>>> i[8:12] = '8-12'
>>> print i[2]
0-5
>>> print i[10]
8-12
>>> print repr(i[-1])
None
>>> print repr(i[17])
None
>>> i[4:9] = '4-9'
>>> print [(j,i[j]) for j in range(6)]
[(0, '0-5'), (1, '0-5'), (2, '0-5'), (3, '0-5'), (4, '4-9'), (5, '4-9')]
>>> print list(i.items())
[((0, 4), '0-5'), ((4, 9), '4-9'), ((9, 12), '8-12')]
>>> i[:0] = 'less than 0'
>>> i[-5]
'less than 0'
>>> i[0]
'0-5'
>>> print list(i.items())
[((None, 0), 'less than 0'), ((0, 4), '0-5'), ((4, 9), '4-9'), ((9, 12), '8-12')]
>>> i[21:] = 'more than twenty'
>>> i[42]
'more than twenty'
>>> i[10.5:15.5] = '10.5-15.5'
>>> i[11.5]
'10.5-15.5'
>>> i[0.5]
'0-5'
>>> print list(i.items())
[((None, 0),... ((9, 10.5), '8-12'), ((10.5, 15.5), '10.5-15.5'), ((21, None),...
>>> i = intervalmap()
>>> i[0:2] = 1
>>> i[2:8] = 2
>>> i[4:] = 3
>>> i[5:6] = 4
>>> i
{[0, 2] => 1, [2, 4] => 2, [4, 5] => 3, [5, 6] => 4, [6, None] => 3}
"""
def __init__(self):
"""
Initializes an empty intervalmap.
Initializes an empty intervalmap.
"""
self._bounds = []
self._items = []
@ -64,7 +64,7 @@ class intervalmap(object):
def __setitem__(self, _slice, _value):
"""
Sets an interval mapping.
Sets an interval mapping.
"""
assert isinstance(_slice, slice), "The key must be a slice object"
@ -112,7 +112,7 @@ class intervalmap(object):
def __getitem__(self, _point):
"""
Gets a value from the mapping.
Gets a value from the mapping.
"""
assert not isinstance(_point, slice), "The key cannot be a slice object"
@ -124,9 +124,9 @@ class intervalmap(object):
def items(self):
"""
Returns an iterator with each item being
((low_bound,high_bound), value). The items are returned
in order.
Returns an iterator with each item being
((low_bound,high_bound), value). The items are returned
in order.
"""
previous_bound = None
for b, v in izip(self._bounds, self._items):
@ -138,8 +138,8 @@ class intervalmap(object):
def values(self):
"""
Returns an iterator with each item being a stored value. The items
are returned in order.
Returns an iterator with each item being a stored value. The items
are returned in order.
"""
for v in self._items:
if v is not None:

View File

@ -394,7 +394,7 @@ CREATE TABLE notes_formsemestre (
elt_annee_apo text -- code element annee Apogee, eg VRT1A ou V2INLA,V2INCA
) WITH OIDS;
-- id des utilsateurs responsables (aka directeurs des etudes) du semestre:
-- id des utilisateurs responsables (aka directeurs des etudes) du semestre:
CREATE TABLE notes_formsemestre_responsables (
formsemestre_id text REFERENCES notes_formsemestre(formsemestre_id) ON DELETE CASCADE,
responsable_id text NOT NULL,

View File

@ -51,5 +51,5 @@ telephone; text; adresse; 1; num. telephone (fixe)
telephonemobile; text; adresse; 1; num. telephone (mobile)
#
# Pas tout à fait admission:
debouche;text; admissions;1;situation APRES être passé par chez nous;
debouche;text; admissions;1;(OBSOLETE, ne plus utiliser) situation APRES être passé par chez nous;

View File

@ -5,7 +5,7 @@
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved.
# Copyright (c) 1999 - 2021 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
@ -61,5 +61,5 @@ class CacheFunc:
def inval_cache(self): # >
"clear whole cache"
log("inval_cache %s(%s)" % (str(self.func), str(args))) # >
log("inval_cache %s" % (str(self.func))) # >
self.cache = {}

View File

@ -35,7 +35,7 @@ class _logguer:
if LOG_FILENAME:
path = os.path.join(self.directory, LOG_FILENAME)
self.file = open(path, "a")
self("new _logguer")
self("new _logguer (%s)" % path)
else:
self.file = None # logging disabled

View File

@ -5,7 +5,7 @@
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved.
# Copyright (c) 1999 - 2021 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
@ -51,7 +51,7 @@ BONUS_TWO_ARGS = len(inspect.getargspec(CONFIG.compute_bonus)[0]) == 2
def comp_ranks(T):
"""Calcul rangs à partir d'une liste ordonnée de tuples [ (valeur, ..., etudid) ]
"""Calcul rangs à partir d'une liste ordonnée de tuples [ (valeur, ..., etudid) ]
(valeur est une note numérique), en tenant compte des ex-aequos
Le resultat est: { etudid : rang } rang est une chaine decrivant le rang
"""
@ -135,8 +135,8 @@ class NotesTable:
- inscrlist: étudiants inscrits à ce semestre, par ordre alphabétique (avec demissions)
- identdict: { etudid : ident }
- sem : le formsemestre
get_table_moyennes_triees: [ (moy_gen, moy_ue1, moy_ue2, ... moy_ues, moy_mod1, ..., moy_modn, etudid) ]
( toutes les valeurs sont soit des nombrs soit des chaines spéciales comme 'NA', 'NI'),
get_table_moyennes_triees: [ (moy_gen, moy_ue1, moy_ue2, ... moy_ues, moy_mod1, ..., moy_modn, etudid) ]
( toutes les valeurs sont soit des nombrs soit des chaines spéciales comme 'NA', 'NI'),
incluant les UE de sport
- bonus[etudid] : valeur du bonus "sport".
@ -145,7 +145,7 @@ class NotesTable:
- _modmoys : { moduleimpl_id : { etudid: note_moyenne_dans_ce_module } }
- _ues : liste des UE de ce semestre (hors capitalisees)
- _matmoys : { matiere_id : { etudid: note moyenne dans cette matiere } }
"""
def __init__(self, context, formsemestre_id):
@ -408,7 +408,7 @@ class NotesTable:
def get_ues(self, filter_sport=False, filter_non_inscrit=False, etudid=None):
"""liste des ue, ordonnée par numero.
Si filter_non_inscrit, retire les UE dans lesquelles l'etudiant n'est
Si filter_non_inscrit, retire les UE dans lesquelles l'etudiant n'est
inscrit à aucun module.
Si filter_sport, retire les UE de type SPORT
"""
@ -580,11 +580,11 @@ class NotesTable:
return matmoy.get(etudid, "NA")
def comp_etud_moy_ue(self, etudid, ue_id=None, cnx=None):
"""Calcule moyenne gen. pour un etudiant dans une UE
"""Calcule moyenne gen. pour un etudiant dans une UE
Ne prend en compte que les evaluations toutes les notes sont entrées
Return a dict(moy, nb_notes, nb_missing, sum_coefs)
Si pas de notes, moy == 'NA' et sum_coefs==0
Si non inscrit, moy == 'NI' et sum_coefs==0
Si non inscrit, moy == 'NI' et sum_coefs==0
"""
assert ue_id
modimpls = self.get_modimpls(ue_id)
@ -608,7 +608,6 @@ class NotesTable:
est_inscrit = False # inscrit à l'un des modules de cette UE ?
for modimpl in modimpls:
mod_ue_id = modimpl["ue"]["ue_id"]
# module ne faisant pas partie d'une UE capitalisee
val = self._modmoys[modimpl["moduleimpl_id"]].get(etudid, "NI")
# si 'NI', etudiant non inscrit a ce module
@ -711,7 +710,7 @@ class NotesTable:
def comp_etud_moy_gen(self, etudid, cnx):
"""Calcule moyenne gen. pour un etudiant
Return a dict:
moy : moyenne générale
moy : moyenne générale
nb_notes, nb_missing, sum_coefs
ects_pot : (float) nb de crédits ECTS qui seraient validés (sous réserve de validation par le jury),
ects_pot_fond: (float) nb d'ECTS issus d'UE fondamentales (non électives)
@ -721,14 +720,14 @@ class NotesTable:
'est_inscrit' : True si étudiant inscrit à au moins un module de cette UE
'moy' : moyenne, avec capitalisation eventuelle
'coef_ue' : coef de l'UE utilisé pour le calcul de la moyenne générale
(la somme des coefs des modules, ou le coef d'UE capitalisée,
(la somme des coefs des modules, ou le coef d'UE capitalisée,
ou encore le coef d'UE si l'option use_ue_coefs est active)
'cur_moy_ue' : moyenne de l'UE en cours (sans considérer de capitalisation)
'cur_coef_ue': coefficient de l'UE courante
'is_capitalized' : True|False,
'ects_pot' : (float) nb de crédits ECTS qui seraient validés (sous réserve de validation par le jury),
'ects_pot_fond': 0. si UE non fondamentale, = ects_pot sinon,
'ects_pot_pro' : 0 si UE non pro, = ects_pot sinon,
'ects_pot_fond': 0. si UE non fondamentale, = ects_pot sinon,
'ects_pot_pro' : 0 si UE non pro, = ects_pot sinon,
'formsemestre_id' : (si capitalisee),
'event_date' : (si capitalisee)
}
@ -760,7 +759,6 @@ class NotesTable:
sem_ects_pot_pro = 0.0
for ue in self.get_ues():
ue_id = ue["ue_id"]
# - On calcule la moyenne d'UE courante:
if not block_computation:
mu = self.comp_etud_moy_ue(etudid, ue_id=ue["ue_id"], cnx=cnx)
@ -956,10 +954,10 @@ class NotesTable:
Ne considère que les UE ayant des notes (moyenne calculée).
(les UE sans notes ne sont pas comptées comme sous la barre)
Prend en compte les éventuelles UE capitalisées.
Pour les parcours habituels, cela revient à vérifier que
les moyennes d'UE sont toutes > à leur barre (sauf celles sans notes)
Pour les parcours non standards (LP2014), cela peut être plus compliqué.
Return: True|False, message explicatif
@ -1000,8 +998,7 @@ class NotesTable:
)
def get_table_moyennes_dict(self):
"""{ etudid : (liste des moyennes) } comme get_table_moyennes_triees
"""
"""{ etudid : (liste des moyennes) } comme get_table_moyennes_triees"""
D = {}
for t in self.T:
D[t[-1]] = t
@ -1209,13 +1206,13 @@ class NotesTable:
# return sum(c_list)
def get_etud_ue_cap_coef(self, etudid, ue, ue_cap, cnx=None):
"""Calcule le coefficient d'une UE capitalisée, pour cet étudiant,
"""Calcule le coefficient d'une UE capitalisée, pour cet étudiant,
injectée dans le semestre courant.
ue : ue du semestre courant
ue_cap = resultat de formsemestre_get_etud_capitalisation
{ 'ue_id' (dans le semestre source),
{ 'ue_id' (dans le semestre source),
'ue_code', 'moy', 'event_date','formsemestre_id' }
"""
# log("get_etud_ue_cap_coef\nformsemestre_id='%s'\netudid='%s'\nue=%s\nue_cap=%s\n" % (self.formsemestre_id, etudid, ue, ue_cap))
@ -1291,8 +1288,7 @@ class NotesTable:
return len(cursor.fetchall()) > 0
def get_evaluations_etats(self): # evaluation_list_in_sem
"""[ {...evaluation et son etat...} ]
"""
"""[ {...evaluation et son etat...} ]"""
if self._evaluations_etats is None:
self._evaluations_etats = sco_evaluations.do_evaluation_list_in_sem(
self.context, self.formsemestre_id
@ -1301,13 +1297,11 @@ class NotesTable:
return self._evaluations_etats
def get_sem_evaluation_etat_list(self):
"""Liste des evaluations de ce semestre, avec leur etat
"""
"""Liste des evaluations de ce semestre, avec leur etat"""
return self.get_evaluations_etats()
def get_mod_evaluation_etat_list(self, moduleimpl_id):
"""Liste des évaluations de ce module
"""
"""Liste des évaluations de ce module"""
return [
e
for e in self.get_evaluations_etats()

View File

@ -5,7 +5,7 @@
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved.
# Copyright (c) 1999 - 2021 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

View File

@ -248,8 +248,7 @@ def DBDelete(cnx, table, colid, val, commit=False):
class EditableTable:
""" --- generic class: SQL table with create/edit/list/delete
"""
"""--- generic class: SQL table with create/edit/list/delete"""
def __init__(
self,
@ -377,7 +376,11 @@ class EditableTable:
# format value
for title in vals.keys():
if self.input_formators.has_key(title):
vals[title] = self.input_formators[title](vals[title])
try:
vals[title] = self.input_formators[title](vals[title])
except:
log("exception while converting %s=%s" % (title, vals[title]))
raise
DBUpdateArgs(
cnx,
self.table_name,

View File

@ -5,7 +5,7 @@
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved.
# Copyright (c) 1999 - 2021 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

View File

@ -5,7 +5,7 @@
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved.
# Copyright (c) 1999 - 2021 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
@ -88,7 +88,7 @@ class JuryPE:
{'etudid : { 'nom', 'prenom', 'sexe', 'diplome', '', }}
Rq: il contient à la fois les étudiants qui vont être diplomés à la date prévue
et ceux qui sont éliminés (abandon, redoublement, ...) pour affichage alternatif
Note (EV:): les attributs sont des chaines encodées (utf8), comme dans ScoDoc (pas des unicodes)
"""
@ -210,8 +210,7 @@ class JuryPE:
# ------------------------------------------------------------------------------------------------------------------
def get_zipped_data(self):
"""returns zipped data with all generated (CSV) files
"""
"""returns zipped data with all generated (CSV) files"""
if self.zipfile:
self.zipfile.close()
self.zipfile = None
@ -366,7 +365,7 @@ class JuryPE:
# ------------------------------------------------------------------------------------------------------------------
def add_etudiants(self, etudid):
""" Ajoute un étudiant (via son etudid) au dictionnaire de synthèse jurydict.
"""Ajoute un étudiant (via son etudid) au dictionnaire de synthèse jurydict.
L'ajout consiste à :
> insérer une entrée pour l'étudiant en mémorisant ses infos (get_etudInfo),
avec son nom, prénom, etc...
@ -479,7 +478,7 @@ class JuryPE:
pour autant avoir été indiqué NAR ou DEM ; recherche son dernier semestre validé et regarde s'il
n'existe pas parmi les semestres existants dans scodoc un semestre postérieur (en terme de date de
début) de n° au moins égal à celui de son dernier semestre valide dans lequel il aurait pu
s'inscrire mais ne l'a pas fait. """
s'inscrire mais ne l'a pas fait."""
sessems = self.get_semestresDUT_d_un_etudiant(
etudid
) # les semestres de l'étudiant
@ -868,9 +867,7 @@ class JuryPE:
return semDeb["annee_debut"]
def get_parcoursIUT(self, etudid):
"""Renvoie une liste d'infos sur les semestres du parcours d'un étudiant
"""
"""Renvoie une liste d'infos sur les semestres du parcours d'un étudiant"""
etudinfo = self.ETUDINFO_DICT[etudid]
sems = self.get_semestresDUT_d_un_etudiant(etudid)
@ -934,7 +931,7 @@ class JuryPE:
def get_allTagForAggregat(self, nom_aggregat):
"""Extrait du dictionnaire syntheseJury la liste des tags d'un semestre ou
d'un aggrégat donné par son nom (S1, S2, S3 ou S4, 1A, ...). Renvoie [] si aucun tag."""
d'un aggrégat donné par son nom (S1, S2, S3 ou S4, 1A, ...). Renvoie [] si aucun tag."""
taglist = set()
for etudid in self.get_etudids_du_jury():
taglist = taglist.union(
@ -1127,10 +1124,9 @@ class JuryPE:
# ------------------------------------------------------------------------------------------------------------------
def get_cache_notes_d_un_semestre(
cls, context, formsemestre_id
self, context, formsemestre_id
): # inutile en realité !
"""Charge la table des notes d'un formsemestre
"""
"""Charge la table des notes d'un formsemestre"""
return context.Notes._getNotesCache().get_NotesTable(
context.Notes, formsemestre_id
)
@ -1196,7 +1192,7 @@ class JuryPE:
# ----------------------------------------------------------------------------------------
def get_annee_diplome_semestre(sem):
""" Pour un semestre donne, décrit par le biais du dictionnaire sem usuel :
"""Pour un semestre donne, décrit par le biais du dictionnaire sem usuel :
sem = {'formestre_id': ..., 'semestre_id': ..., 'annee_debut': ...},
à condition qu'il soit un semestre de formation DUT,
predit l'annee à laquelle sera remis le diplome DUT des etudiants scolarisés dans le semestre
@ -1230,12 +1226,12 @@ def get_annee_diplome_semestre(sem):
# ----------------------------------------------------------------------------------
def get_cosemestres_diplomants(context, semBase, avec_meme_formation=False):
""" Partant d'un semestre de Base = {'formsemestre_id': ..., 'semestre_id': ..., 'annee_debut': ...},
renvoie la liste de tous ses co-semestres (lui-meme inclus)
Par co-semestre, s'entend les semestres :
> dont l'annee predite pour la remise du diplome DUT est la meme
> dont la formation est la même (optionnel)
> ne prenant en compte que les etudiants sans redoublement
"""Partant d'un semestre de Base = {'formsemestre_id': ..., 'semestre_id': ..., 'annee_debut': ...},
renvoie la liste de tous ses co-semestres (lui-meme inclus)
Par co-semestre, s'entend les semestres :
> dont l'annee predite pour la remise du diplome DUT est la meme
> dont la formation est la même (optionnel)
> ne prenant en compte que les etudiants sans redoublement
"""
tousLesSems = (
context.Notes.formsemestre_list()

View File

@ -5,7 +5,7 @@
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved.
# Copyright (c) 1999 - 2021 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

View File

@ -5,7 +5,7 @@
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved.
# Copyright (c) 1999 - 2021 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
@ -144,8 +144,7 @@ class SetTag(pe_tagtable.TableTag):
# -------------------------------------------------------------------------------------------------------------------
def do_tagdict(self):
"""Synthétise la liste des modules pris en compte dans le calcul d'un tag (pour analyse des résultats)
"""
"""Synthétise la liste des modules pris en compte dans le calcul d'un tag (pour analyse des résultats)"""
self.tagdict = {}
for semtag in self.SemTagDict.values():
for tag in semtag.get_all_tags():
@ -208,8 +207,8 @@ class SetTag(pe_tagtable.TableTag):
class SetTagInterClasse(pe_tagtable.TableTag):
"""Récupère les moyennes de SetTag aggrégant un même parcours (par ex un ['S1', 'S2'] n'ayant pas fini au même S2
pour fournir un interclassement sur un groupe d'étudiant => seul compte alors la promo
nom_combinaison = 'S1' ou '1A'
pour fournir un interclassement sur un groupe d'étudiant => seul compte alors la promo
nom_combinaison = 'S1' ou '1A'
"""
# -------------------------------------------------------------------------------------------------------------------

View File

@ -5,7 +5,7 @@
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved.
# Copyright (c) 1999 - 2021 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
@ -95,7 +95,7 @@ class TableTag:
# -----------------------------------------------------------------------------------------------------------
def get_coeff_from_resultats(self, tag, etudid):
"""Renvoie la somme des coeffs de pondération normalisée utilisés dans le calcul de la moyenne à un tag d'un étudiant
"""Renvoie la somme des coeffs de pondération normalisée utilisés dans le calcul de la moyenne à un tag d'un étudiant
au regard du format de self.resultats.
"""
return (
@ -329,7 +329,7 @@ def moyenne_ponderee_terme_a_terme(notes, coeffs=None, force=False):
# -------------------------------------------------------------------------------------------
def conversionDate_StrToDate(date_fin):
""" Conversion d'une date fournie sous la forme d'une chaine de caractère de
"""Conversion d'une date fournie sous la forme d'une chaine de caractère de
type 'jj/mm/aaaa' en un objet date du package datetime.
Fonction servant au tri des semestres par date
"""

View File

@ -5,7 +5,7 @@
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved.
# Copyright (c) 1999 - 2021 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

View File

@ -5,7 +5,7 @@
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved.
# Copyright (c) 1999 - 2021 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
@ -89,7 +89,7 @@ def pe_view_sem_recap(
REQUEST=None,
):
"""Génération des avis de poursuite d'étude
mode_debug = Pour "squeezer" le calcul du jury pe (long)
et debugger uniquement la partie avis latex
"""

View File

@ -5,7 +5,7 @@
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved.
# Copyright (c) 1999 - 2021 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

View File

@ -5,7 +5,7 @@
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved.
# Copyright (c) 1999 - 2021 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
@ -53,8 +53,7 @@ def doSignaleAbsence(
description=None,
REQUEST=None,
): # etudid implied
"""Signalement d'une absence
"""
"""Signalement d'une absence"""
etud = context.getEtudInfo(filled=1, REQUEST=REQUEST)[0]
etudid = etud["etudid"]
@ -124,21 +123,51 @@ def doSignaleAbsence(
def SignaleAbsenceEtud(context, REQUEST=None): # etudid implied
"""Formulaire individuel simple de signalement d'une absence
"""
"""Formulaire individuel simple de signalement d'une absence"""
# brute-force portage from very old dtml code ...
etud = context.getEtudInfo(filled=1, REQUEST=REQUEST)[0]
etudid = etud["etudid"]
disabled = False
if not etud["cursem"]:
menu_module = ""
require_module = context.get_preference(
"abs_require_module"
) # on utilise la pref globale car pas de sem courant
if require_module:
menu_module = """<div class="ue_warning">Pas inscrit dans un semestre courant,
et l'indication du module est requise. Donc pas de saisie d'absence possible !</div>"""
disabled = True
else:
menu_module = ""
else:
formsemestre_id = etud["cursem"]["formsemestre_id"]
require_module = context.get_preference("abs_require_module", formsemestre_id)
nt = context.Notes._getNotesCache().get_NotesTable(
context.Notes, formsemestre_id
)
ues = nt.get_ues(etudid=etudid)
menu_module = """<p><select name="moduleimpl_id">
<option value="NULL" selected>(Module)</option>"""
if require_module:
menu_module = """
<script type="text/javascript">
function form_enable_disable() {
if ( $("select#sel_moduleimpl_id").val() == "NULL" ) {
$("#butsubmit").prop("disabled", true);
} else {
$("#butsubmit").prop("disabled", false);
};
}
$(document).ready(function() {
form_enable_disable();
});
</script>
<p>Module:
<select id="sel_moduleimpl_id" name="moduleimpl_id"
onChange="form_enable_disable();">"""
else:
menu_module = (
"""<p>Module: <select id="sel_moduleimpl_id" name="moduleimpl_id">"""
)
menu_module += """<option value="NULL" selected>(Module)</option>"""
for ue in ues:
modimpls = nt.get_modimpls(ue_id=ue["ue_id"])
for modimpl in modimpls:
@ -162,7 +191,10 @@ def SignaleAbsenceEtud(context, REQUEST=None): # etudid implied
% etud,
"""<a href="%s/ficheEtud?etudid=%s">""" % (context.ScoURL(), etud["etudid"]),
sco_photos.etud_photo_html(
context, etudid=etudid, title="fiche de " + etud["nomprenom"], REQUEST=REQUEST
context,
etudid=etudid,
title="fiche de " + etud["nomprenom"],
REQUEST=REQUEST,
),
"""</a></td></tr></table>""",
"""
@ -170,35 +202,40 @@ def SignaleAbsenceEtud(context, REQUEST=None): # etudid implied
<input type="hidden" name="etudid" value="%(etudid)s">
<p>
<table><tr>
<td>Date d&eacute;but : </td>
<td>Date début : </td>
<td><input type="text" name="datedebut" size="10" class="datepicker"/> <em>j/m/a</em></td>
<td>&nbsp;&nbsp;&nbsp;Date Fin (optionnel):</td>
<td>&nbsp;&nbsp;&nbsp;Date fin (optionnelle):</td>
<td><input type="text" name="datefin" size="10" class="datepicker"/> <em>j/m/a</em></td>
</tr>
</table>
<br/>
<input type="radio" name="demijournee" value="2" checked>journ&eacute;e(s)
<input type="radio" name="demijournee" value="2" checked>Journée(s)
&nbsp;<input type="radio" name="demijournee" value="1">Matin(s)
&nbsp;<input type="radio" name="demijournee" value="0">Apr&egrave;s midi
&nbsp;<input type="radio" name="demijournee" value="0">Après-midi
%(menu_module)s
<p>
<input type="checkbox" name="estjust"/>Absence justifi&eacute;e.
<input type="checkbox" name="estjust"/>Absence justifiée.
<br/>
Raison: <input type="text" name="description" size="42"/> (optionnel)
</p>
<p>
<input type="submit" value="Envoyer"/>
<input id="butsubmit" type="submit" value="Envoyer" disable="%(disabled)s"/>
<em>
<p>Seuls les modules du semestre en cours apparaissent.</p><p> Evitez de saisir une absence pour un module qui n'est pas en place à cette date.</p>
<p>Toutes les dates sont au format jour/mois/annee</p>
<p>Seuls les modules du semestre en cours apparaissent.</p>
<p>Évitez de saisir une absence pour un module qui n'est pas en place à cette date.</p>
<p>Toutes les dates sont au format jour/mois/annee.</p>
</em>
</form>
"""
% {"etudid": etud["etudid"], "menu_module": menu_module},
% {
"etudid": etud["etudid"],
"menu_module": menu_module,
"disabled": "disabled" if disabled else "",
},
context.sco_footer(REQUEST),
]
return "\n".join(H)
@ -207,8 +244,7 @@ Raison: <input type="text" name="description" size="42"/> (optionnel)
def doJustifAbsence(
context, datedebut, datefin, demijournee, description=None, REQUEST=None
): # etudid implied
"""Justification d'une absence
"""
"""Justification d'une absence"""
etud = context.getEtudInfo(filled=1, REQUEST=REQUEST)[0]
etudid = etud["etudid"]
description_abs = description
@ -274,8 +310,7 @@ def doJustifAbsence(
def JustifAbsenceEtud(context, REQUEST=None): # etudid implied
"""Formulaire individuel simple de justification d'une absence
"""
"""Formulaire individuel simple de justification d'une absence"""
# brute-force portage from very old dtml code ...
etud = context.getEtudInfo(filled=1, REQUEST=REQUEST)[0]
etudid = etud["etudid"]
@ -290,7 +325,10 @@ def JustifAbsenceEtud(context, REQUEST=None): # etudid implied
% etud,
"""<a href="%s/ficheEtud?etudid=%s">""" % (context.ScoURL(), etud["etudid"]),
sco_photos.etud_photo_html(
context, etudid=etudid, title="fiche de " + etud["nomprenom"], REQUEST=REQUEST
context,
etudid=etudid,
title="fiche de " + etud["nomprenom"],
REQUEST=REQUEST,
),
"""</a></td></tr></table>""",
"""
@ -309,7 +347,7 @@ def JustifAbsenceEtud(context, REQUEST=None): # etudid implied
</table>
<br/>
<input type="radio" name="demijournee" value="2" checked>journ&eacute;e(s)
<input type="radio" name="demijournee" value="2" checked>Journée(s)
&nbsp;<input type="radio" name="demijournee" value="1">Matin(s)
&nbsp;<input type="radio" name="demijournee" value="0">Apr&egrave;s midi
@ -329,8 +367,7 @@ Raison: <input type="text" name="description" size="42"/> (optionnel)
def doAnnuleAbsence(
context, datedebut, datefin, demijournee, REQUEST=None
): # etudid implied
"""Annulation des absences pour une demi journée
"""
"""Annulation des absences pour une demi journée"""
etud = context.getEtudInfo(filled=1, REQUEST=REQUEST)[0]
etudid = etud["etudid"]
@ -378,8 +415,7 @@ autre absence pour <b>%(nomprenom)s</b></a></li>
def AnnuleAbsenceEtud(context, REQUEST=None): # etudid implied
"""Formulaire individuel simple d'annulation d'une absence
"""
"""Formulaire individuel simple d'annulation d'une absence"""
# brute-force portage from very old dtml code ...
etud = context.getEtudInfo(filled=1, REQUEST=REQUEST)[0]
etudid = etud["etudid"]
@ -395,7 +431,10 @@ def AnnuleAbsenceEtud(context, REQUEST=None): # etudid implied
% etud, # "
"""<a href="%s/ficheEtud?etudid=%s">""" % (context.ScoURL(), etud["etudid"]),
sco_photos.etud_photo_html(
context, etudid=etudid, title="fiche de " + etud["nomprenom"], REQUEST=REQUEST
context,
etudid=etudid,
title="fiche de " + etud["nomprenom"],
REQUEST=REQUEST,
),
"""</a></td></tr></table>""",
"""<p>A n'utiliser que suite à une erreur de saisie ou lorsqu'il s'avère que l'étudiant était en fait présent. </p>
@ -464,14 +503,13 @@ def AnnuleAbsenceEtud(context, REQUEST=None): # etudid implied
def doAnnuleJustif(
context, datedebut0, datefin0, demijournee, REQUEST=None
): # etudid implied
"""Annulation d'une justification
"""
"""Annulation d'une justification"""
etud = context.getEtudInfo(filled=1, REQUEST=REQUEST)[0]
etudid = etud["etudid"]
dates = context.DateRangeISO(datedebut0, datefin0)
nbadded = 0
for jour in dates:
# Attention: supprime matin et après midi
# Attention: supprime matin et après-midi
if demijournee == "2":
context._AnnuleJustif(etudid, jour, False, REQUEST=REQUEST)
context._AnnuleJustif(etudid, jour, True, REQUEST=REQUEST)
@ -551,7 +589,6 @@ def formChoixSemestreGroupe(context, all=False):
if not sems:
raise ScoValueError("aucun semestre !")
H = ['<select name="group_ids">']
nbgroups = 0
for sem in sems:
for p in sco_groups.get_partitions_list(context, sem["formsemestre_id"]):
for group in sco_groups.get_partition_groups(context, p):
@ -569,8 +606,7 @@ def formChoixSemestreGroupe(context, all=False):
def CalAbs(context, REQUEST=None): # etud implied
"""Calendrier des absences d un etudiant
"""
"""Calendrier des absences d un etudiant"""
# crude portage from 1999 DTML
etud = context.getEtudInfo(filled=1, REQUEST=REQUEST)[0]
etudid = etud["etudid"]
@ -621,7 +657,10 @@ def CalAbs(context, REQUEST=None): # etud implied
context.ScoURL(),
etudid,
sco_photos.etud_photo_html(
context, etudid=etudid, title="fiche de " + etud["nomprenom"], REQUEST=REQUEST
context,
etudid=etudid,
title="fiche de " + etud["nomprenom"],
REQUEST=REQUEST,
),
),
CalHTML,

View File

@ -5,7 +5,7 @@
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved.
# Copyright (c) 1999 - 2021 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
@ -62,8 +62,7 @@ _help_txt = """
def apo_compare_csv_form(context, REQUEST=None):
"""Form: submit 2 CSV files to compare them.
"""
"""Form: submit 2 CSV files to compare them."""
H = [
context.sco_header(REQUEST, page_title="Comparaison de fichiers Apogée"),
"""<h2>Comparaison de fichiers Apogée</h2>
@ -90,8 +89,7 @@ def apo_compare_csv_form(context, REQUEST=None):
def apo_compare_csv(context, A_file, B_file, autodetect=True, REQUEST=None):
"""Page comparing 2 Apogee CSV files
"""
"""Page comparing 2 Apogee CSV files"""
A = _load_apo_data(A_file, autodetect=autodetect)
B = _load_apo_data(B_file, autodetect=autodetect)
@ -182,7 +180,11 @@ def _apo_compare_csv(context, A, B, REQUEST=None):
elts_only_B = B_elts - A_elts.intersection(B_elts)
L.append(
'<span class="val_dif">différents (%d en commun, %d seulement dans A, %d seulement dans B)</span>'
% (len(elts_communs), len(elts_only_A), len(elts_only_B),)
% (
len(elts_communs),
len(elts_only_A),
len(elts_only_B),
)
)
if elts_only_A:
L.append(
@ -244,8 +246,7 @@ def _apo_compare_csv(context, A, B, REQUEST=None):
def apo_table_compare_etud_results(context, A, B, REQUEST=None):
"""
"""
""""""
D = compare_etuds_res(A, B)
T = GenTable(
rows=D,

View File

@ -5,7 +5,7 @@
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved.
# Copyright (c) 1999 - 2021 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
@ -112,8 +112,7 @@ APO_NEWLINE = "\r\n"
def code_scodoc_to_apo(code):
"""Conversion code jury ScoDoc en code Apogée
"""
"""Conversion code jury ScoDoc en code Apogée"""
return {
ATT: "AJAC",
ATB: "AJAC",
@ -159,7 +158,7 @@ def fix_data_encoding(
text, default_source_encoding=APO_INPUT_ENCODING, dest_encoding=APO_INPUT_ENCODING
):
"""Try to ensure that text is using dest_encoding
returns converted text, and a message describing the conversion.
returns converted text, and a message describing the conversion.
"""
message = ""
detected_encoding = guess_data_encoding(text)
@ -241,8 +240,7 @@ VOID_APO_RES = dict(N="", B="", J="", R="", M="")
class ApoEtud(dict):
"""Etudiant Apogee:
"""
"""Etudiant Apogee:"""
def __init__(
self,
@ -361,7 +359,7 @@ class ApoEtud(dict):
Autres éléments: résultats du semestre ou de l'année scolaire:
=> VRTW1: code additionnel au semestre ("code élement semestre", elt_sem_apo)
=> VRT1A: le même que le VET: ("code élement annuel", elt_annee_apo)
Attention, si le semestre couvre plusieurs étapes, indiquer les codes des éléments,
Attention, si le semestre couvre plusieurs étapes, indiquer les codes des éléments,
séparés par des virgules.
Args:
@ -369,7 +367,7 @@ class ApoEtud(dict):
sem (dict): semestre dans lequel on cherche l'élément
cur_sem (dict): semestre "courant" pour résultats annuels (VET)
autre_sem (dict): autre semestre utilisé pour calculé les résultats annuels (VET)
Returns:
dict: with N, B, J, R keys, ou None si elt non trouvé
"""
@ -701,8 +699,7 @@ class ApoData:
self.periode = periode
def setup(self, context):
"""Recherche semestres ScoDoc concernés
"""
"""Recherche semestres ScoDoc concernés"""
self.context = context
self.sems_etape = comp_apo_sems(context, self.etape_apogee, self.annee_scolaire)
self.etape_formsemestre_ids = {s["formsemestre_id"] for s in self.sems_etape}
@ -803,7 +800,7 @@ class ApoData:
Clé: id apogée, eg 'V1RT', 'V1GE2201', ...
Valeur: ApoElt, avec les attributs code, type_objet
Si les id Apogée ne sont pas uniques (ce n'est pas garanti), garde le premier
Si les id Apogée ne sont pas uniques (ce n'est pas garanti), garde le premier
"""
elts = collections.OrderedDict()
for col_id in sorted(cols.keys(), reverse=True):
@ -849,8 +846,7 @@ class ApoData:
return L
def get_etape_apogee(self):
"""Le code etape: 'V1RT', donné par le code de l'élément VET
"""
"""Le code etape: 'V1RT', donné par le code de l'élément VET"""
for elt in self.apo_elts.values():
if elt.type_objet == "VET":
return elt.code
@ -893,8 +889,7 @@ class ApoData:
f.write(self.column_titles)
def write_etuds(self, f):
"""write apo CSV etuds on f
"""
"""write apo CSV etuds on f"""
for e in self.etuds:
fs = [] # e['nip'], e['nom'], e['prenom'], e['naissance'] ]
for col_id in self.col_ids:
@ -983,8 +978,7 @@ class ApoData:
return codes_by_sem
def build_cr_table(self):
"""Table compte rendu des décisions
"""
"""Table compte rendu des décisions"""
CR = [] # tableau compte rendu des decisions
for e in self.etuds:
cr = {
@ -1015,14 +1009,14 @@ class ApoData:
def _apo_read_cols(f):
"""Lecture colonnes apo :
"""Lecture colonnes apo :
Démarre après la balise XX-APO_COLONNES-XX
et s'arrête après la balise APO_COL_VAL_FIN
Colonne Apogee: les champs sont données par la ligne
apoL_a01_code de la section XX-APO_COLONNES-XX
apoL_a01_code de la section XX-APO_COLONNES-XX
col_id est apoL_c0001, apoL_c0002, ...
:return: { col_id : { title : value } }
Example: { 'apoL_c0001' : { 'Type Objet' : 'VET', 'Code' : 'V1IN', ... }, ... }
"""
@ -1116,8 +1110,7 @@ def comp_apo_sems(context, etape_apogee, annee_scolaire):
def nar_etuds_table(context, apo_data, NAR_Etuds):
"""Liste les NAR -> excel table
"""
"""Liste les NAR -> excel table"""
code_etape = apo_data.etape_apogee
today = datetime.datetime.today().strftime("%d/%m/%y")
L = []
@ -1186,7 +1179,7 @@ def export_csv_to_apogee(
export_res_rat=True,
REQUEST=None,
):
"""Genere un fichier CSV Apogée
"""Genere un fichier CSV Apogée
à partir d'un fichier CSV Apogée vide (ou partiellement rempli)
et des résultats ScoDoc.
Si dest_zip, ajoute les fichiers générés à ce zip

View File

@ -5,7 +5,7 @@
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved.
# Copyright (c) 1999 - 2021 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

View File

@ -5,7 +5,7 @@
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved.
# Copyright (c) 1999 - 2021 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
@ -49,14 +49,12 @@ EtudsArchive = EtudsArchiver()
def can_edit_etud_archive(context, authuser):
"""True si l'utilisateur peut modifier les archives etudiantes
"""
"""True si l'utilisateur peut modifier les archives etudiantes"""
return authuser.has_permission(ScoEtudAddAnnotations, context)
def etud_list_archives_html(context, REQUEST, etudid):
"""HTML snippet listing archives
"""
"""HTML snippet listing archives"""
can_edit = can_edit_etud_archive(context, REQUEST.AUTHENTICATED_USER)
L = []
for archive_id in EtudsArchive.list_obj_archives(context, etudid):
@ -122,8 +120,7 @@ def add_archives_info_to_etud_list(context, etuds):
def etud_upload_file_form(context, REQUEST, etudid):
"""Page with a form to choose and upload a file, with a description.
"""
"""Page with a form to choose and upload a file, with a description."""
# check permission
if not can_edit_etud_archive(context, REQUEST.AUTHENTICATED_USER):
raise AccessDenied(
@ -178,8 +175,7 @@ def etud_upload_file_form(context, REQUEST, etudid):
def _store_etud_file_to_new_archive(
context, REQUEST, etudid, data, filename, description=""
):
"""Store data to new archive.
"""
"""Store data to new archive."""
filesize = len(data)
if filesize < 10 or filesize > CONFIG.ETUD_MAX_FILE_SIZE:
return 0, "Fichier image de taille invalide ! (%d)" % filesize
@ -188,8 +184,7 @@ def _store_etud_file_to_new_archive(
def etud_delete_archive(context, REQUEST, etudid, archive_name, dialog_confirmed=False):
"""Delete an archive
"""
"""Delete an archive"""
# check permission
if not can_edit_etud_archive(context, REQUEST.AUTHENTICATED_USER):
raise AccessDenied(
@ -218,8 +213,7 @@ def etud_delete_archive(context, REQUEST, etudid, archive_name, dialog_confirmed
def etud_get_archived_file(context, REQUEST, etudid, archive_name, filename):
"""Send file to client.
"""
"""Send file to client."""
return EtudsArchive.get_archived_file(
context, REQUEST, etudid, archive_name, filename
)
@ -227,8 +221,7 @@ def etud_get_archived_file(context, REQUEST, etudid, archive_name, filename):
# --- Upload d'un ensemble de fichiers (pour un groupe d'étudiants)
def etudarchive_generate_excel_sample(context, group_id=None, REQUEST=None):
"""Feuille excel pour import fichiers etudiants (utilisé pour admissions)
"""
"""Feuille excel pour import fichiers etudiants (utilisé pour admissions)"""
fmt = ImportScolars.sco_import_format()
data = ImportScolars.sco_import_generate_excel_sample(
fmt,
@ -249,8 +242,7 @@ def etudarchive_generate_excel_sample(context, group_id=None, REQUEST=None):
def etudarchive_import_files_form(context, group_id, REQUEST=None):
"""Formualaire pour importation fichiers d'un groupe
"""
"""Formualaire pour importation fichiers d'un groupe"""
H = [
context.sco_header(
REQUEST, page_title="Import de fichiers associés aux étudiants"

View File

@ -5,7 +5,7 @@
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved.
# Copyright (c) 1999 - 2021 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
@ -162,4 +162,4 @@ class Baccalaureat:
return self.type() == "G"
def is_techno(self):
return selt.type() == "T"
return self.type() == "T"

View File

@ -5,7 +5,7 @@
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved.
# Copyright (c) 1999 - 2021 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
@ -435,8 +435,15 @@ def _ue_mod_bulletin(context, etudid, formsemestre_id, ue_id, modimpls, nt, vers
if mod["mod_moy_txt"][:2] == "NA":
mod["mod_moy_txt"] = "-"
if is_malus:
mod["mod_moy_txt"] = ""
mod["mod_coef_txt"] = ""
if mod_moy > 0:
mod["mod_moy_txt"] = fmt_note(mod_moy)
mod["mod_coef_txt"] = "Malus"
elif mod_moy < 0:
mod["mod_moy_txt"] = fmt_note(-mod_moy)
mod["mod_coef_txt"] = "Bonus"
else:
mod["mod_moy_txt"] = "-"
mod["mod_coef_txt"] = "-"
else:
mod["mod_coef_txt"] = fmt_coef(modimpl["module"]["coefficient"])
if mod["mod_moy_txt"] != "NI": # ne montre pas les modules 'non inscrit'
@ -517,7 +524,7 @@ def _ue_mod_bulletin(context, etudid, formsemestre_id, ue_id, modimpls, nt, vers
if val == "NP":
e["note_txt"] = "nd"
e["note_html"] = '<span class="note_nd">nd</span>'
e["coef_txt"] = ""
e["coef_txt"] = fmt_coef(e["coefficient"])
else:
# (-0.15) s'affiche "bonus de 0.15"
if is_malus:

View File

@ -5,7 +5,7 @@
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved.
# Copyright (c) 1999 - 2021 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

View File

@ -5,7 +5,7 @@
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved.
# Copyright (c) 1999 - 2021 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

View File

@ -5,7 +5,7 @@
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved.
# Copyright (c) 1999 - 2021 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

View File

@ -5,7 +5,7 @@
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved.
# Copyright (c) 1999 - 2021 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
@ -80,8 +80,7 @@ class BulletinGeneratorLegacy(sco_bulletins_generator.BulletinGenerator):
return [self.buildTableObject(P, pdfTableStyle, colWidths)]
def bul_table_html(self):
"""Génère la table centrale du bulletin de notes: chaine HTML
"""
"""Génère la table centrale du bulletin de notes: chaine HTML"""
format = "html"
I = self.infos
authuser = self.authuser
@ -130,13 +129,10 @@ class BulletinGeneratorLegacy(sco_bulletins_generator.BulletinGenerator):
continue # saute les modules où on n'est pas inscrit
H.append('<tr class="notes_bulletin_row_mod%s">' % rowstyle)
if context.get_preference("bul_show_minmax_mod", formsemestre_id):
rang_minmax = (
'%s <span class="bul_minmax" title="[min, max] UE">[%s, %s]</span>'
% (
mod["mod_rang_txt"],
fmt_note(mod["stats"]["min"]),
fmt_note(mod["stats"]["max"]),
)
rang_minmax = '%s <span class="bul_minmax" title="[min, max] UE">[%s, %s]</span>' % (
mod["mod_rang_txt"],
fmt_note(mod["stats"]["min"]),
fmt_note(mod["stats"]["max"]),
)
else:
rang_minmax = mod["mod_rang_txt"] # vide si pas option rang
@ -386,8 +382,7 @@ sco_bulletins_generator.register_bulletin_class(BulletinGeneratorLegacy)
class BulTableStyle:
"""Construction du style de tables reportlab platypus pour les bulletins "classiques"
"""
"""Construction du style de tables reportlab platypus pour les bulletins "classiques" """
LINEWIDTH = 0.5
LINECOLOR = Color(0, 0, 0)

View File

@ -5,7 +5,7 @@
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved.
# Copyright (c) 1999 - 2021 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

View File

@ -5,7 +5,7 @@
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved.
# Copyright (c) 1999 - 2021 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
@ -57,8 +57,7 @@ import os
def form_change_bul_sig(context, side, formsemestre_id=None, REQUEST=None):
"""Change pdf signature
"""
"""Change pdf signature"""
filename = _get_sig_existing_filename(
context, side, formsemestre_id=formsemestre_id
)

View File

@ -5,7 +5,7 @@
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved.
# Copyright (c) 1999 - 2021 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
@ -48,10 +48,22 @@ Balises img: actuellement interdites.
"""
import traceback, re
import sco_utils as scu
import sco_formsemestre
from sco_pdf import *
import sco_pdf
from sco_pdf import Color, Paragraph, Spacer, Table
from sco_pdf import blue, cm, mm
from sco_pdf import SU
import sco_preferences
from notes_log import log
from sco_permissions import ScoEtudInscrit
from sco_codes_parcours import (
UE_COLORS,
UE_DEFAULT_COLOR,
UE_ELECTIVE,
UE_SPORT,
UE_STANDARD,
)
import sco_bulletins_generator
import sco_bulletins_pdf
import sco_groups
@ -271,7 +283,6 @@ class BulletinGeneratorStandard(sco_bulletins_generator.BulletinGenerator):
context = self.context
P = [] # elems pour générer table avec gen_table (liste de dicts)
formsemestre_id = I["formsemestre_id"]
sem = sco_formsemestre.get_formsemestre(context, formsemestre_id)
prefs = context.get_preferences(formsemestre_id)
# Colonnes à afficher:
@ -328,8 +339,8 @@ class BulletinGeneratorStandard(sco_bulletins_generator.BulletinGenerator):
linktmpl = (
'<span onclick="toggle_vis_ue(this);" class="toggle_ue">%s</span>&nbsp;'
)
minuslink = linktmpl % icontag("minus_img", border="0", alt="-")
pluslink = linktmpl % icontag("plus_img", border="0", alt="+")
minuslink = linktmpl % scu.icontag("minus_img", border="0", alt="-")
pluslink = linktmpl % scu.icontag("plus_img", border="0", alt="+")
# 1er ligne titres
t = {
@ -384,9 +395,7 @@ class BulletinGeneratorStandard(sco_bulletins_generator.BulletinGenerator):
P.append(t)
# Rangs dans les partitions:
partitions, partitions_etud_groups = sco_groups.get_formsemestre_groups(
context, formsemestre_id
)
partitions, _ = sco_groups.get_formsemestre_groups(context, formsemestre_id)
for partition in partitions:
if partition["bul_show_rank"]:
partition_id = partition["partition_id"]
@ -486,16 +495,16 @@ class BulletinGeneratorStandard(sco_bulletins_generator.BulletinGenerator):
t["_css_row_class"] += " notes_bulletin_row_ue_cur"
t["_titre_help"] = "(en cours, non prise en compte)"
if prefs["bul_show_minmax"]:
t["min"] = fmt_note(ue["min"])
t["max"] = fmt_note(ue["max"])
t["min"] = scu.fmt_note(ue["min"])
t["max"] = scu.fmt_note(ue["max"])
if prefs["bul_show_moypromo"]:
t["moy"] = fmt_note(ue["moy"]).replace("NA", "-")
t["moy"] = scu.fmt_note(ue["moy"]).replace("NA", "-")
# Cas particulier des UE sport (bonus)
if ue["type"] == UE_SPORT and not ue_descr:
del t["module"]
del t["coef"]
t["_pdf_style"].append(("SPAN", (colidx["note"], 0), (-1, 0)))
# t["_module_colspan"] = 3 # non car bug si aucune colonne additionnelle
# t["_module_colspan"] = 3 # non car bug si aucune colonne additionnelle
# UE électives
if ue["type"] == UE_ELECTIVE:
t["module"] += " <i>(élective)</i>"
@ -544,8 +553,7 @@ class BulletinGeneratorStandard(sco_bulletins_generator.BulletinGenerator):
rowstyle="",
hidden=False,
):
"""Liste dans la table les descriptions des modules et, si version != short, des évaluations.
"""
"""Liste dans la table les descriptions des modules et, si version != short, des évaluations."""
if ue_type == "cur": # UE courante non prise en compte (car capitalisee)
pdf_style_bg = [("BACKGROUND", (0, 0), (-1, 0), self.PDF_UE_CUR_BG)]
else:
@ -596,10 +604,10 @@ class BulletinGeneratorStandard(sco_bulletins_generator.BulletinGenerator):
"_pdf_style": pdf_style,
}
if prefs["bul_show_minmax_mod"]:
t["min"] = fmt_note(mod["stats"]["min"])
t["max"] = fmt_note(mod["stats"]["max"])
t["min"] = scu.fmt_note(mod["stats"]["min"])
t["max"] = scu.fmt_note(mod["stats"]["max"])
if prefs["bul_show_moypromo"]:
t["moy"] = fmt_note(mod["stats"]["moy"]).replace("NA", "-")
t["moy"] = scu.fmt_note(mod["stats"]["moy"]).replace("NA", "-")
P.append(t)
if self.version != "short":
@ -660,12 +668,15 @@ class BulletinGeneratorStandard(sco_bulletins_generator.BulletinGenerator):
t["note"] = "<i>" + e["note_txt"] + "</i>"
else:
t["_module_colspan"] = 2
if prefs["bul_show_minmax_eval"]:
if prefs["bul_show_minmax_eval"] or prefs["bul_show_moypromo"]:
etat = sco_evaluations.do_evaluation_etat(
self.context, e["evaluation_id"]
)
t["min"] = fmt_note(etat["mini"])
t["max"] = fmt_note(etat["maxi"])
if prefs["bul_show_minmax_eval"]:
t["min"] = scu.fmt_note(etat["mini"])
t["max"] = scu.fmt_note(etat["maxi"])
if prefs["bul_show_moypromo"]:
t["moy"] = scu.fmt_note(etat["moy"])
P.append(t)
nbeval += 1
return nbeval

View File

@ -5,7 +5,7 @@
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved.
# Copyright (c) 1999 - 2021 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
@ -305,5 +305,4 @@ sco_bulletins_generator.register_bulletin_class(BulletinGeneratorUCAC)
def bulletin_table_ucac(context, I, version=None):
"""
"""
""""""

View File

@ -5,7 +5,7 @@
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved.
# Copyright (c) 1999 - 2021 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
@ -230,6 +230,7 @@ def make_xml_formsemestre_bulletinetud(
value=mod_moy,
min=fmt_note(modstat["min"]),
max=fmt_note(modstat["max"]),
moy=fmt_note(modstat["moy"]),
)
doc._pop()
if context.get_preference("bul_show_mod_rangs", formsemestre_id):

View File

@ -5,7 +5,7 @@
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved.
# Copyright (c) 1999 - 2021 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
@ -35,6 +35,7 @@ import thread, time
# Cache data
class simpleCache:
def __init__(self):
self.cache = {}
self.inval_cache() # >
def inval_cache(self, key=None): # >
@ -56,7 +57,7 @@ class simpleCache:
class expiringCache(simpleCache):
"""A simple cache wich cache data for a most "duration" seconds.
This is used for users (which may be updated from external
This is used for users (which may be updated from external
information systems)
"""

View File

@ -5,7 +5,7 @@
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved.
# Copyright (c) 1999 - 2021 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

View File

@ -5,7 +5,7 @@
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved.
# Copyright (c) 1999 - 2021 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
@ -315,7 +315,7 @@ def do_moduleimpl_moyennes(context, nt, mod):
notes.append(0.0)
coefs.append(0.0)
coefs_mask.append(0)
if nb_notes > 0:
if nb_notes > 0 or formula_use_abs:
user_moy = compute_user_formula(
context,
sem,

View File

@ -5,7 +5,7 @@
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved.
# Copyright (c) 1999 - 2021 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

View File

@ -5,7 +5,7 @@
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved.
# Copyright (c) 1999 - 2021 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
@ -28,12 +28,14 @@
"""
Rapport (table) avec dernier semestre fréquenté et débouché de chaque étudiant
"""
from types import StringType
import safehtml
from notesdb import *
from sco_utils import *
import sco_utils as scu
import notesdb
from notes_log import log
import VERSION
from sco_exceptions import AccessDenied
from scolog import logdb
from gen_tables import GenTable
import sco_formsemestre
@ -42,8 +44,7 @@ import sco_tag_module
def report_debouche_date(context, start_year=None, format="html", REQUEST=None):
"""Rapport (table) pour les débouchés des étudiants sortis à partir de l'année indiquée.
"""
"""Rapport (table) pour les débouchés des étudiants sortis à partir de l'année indiquée."""
if not start_year:
return report_debouche_ask_date(context, REQUEST=REQUEST)
if format == "xls":
@ -54,8 +55,8 @@ def report_debouche_date(context, start_year=None, format="html", REQUEST=None):
etudids = get_etudids_with_debouche(context, start_year)
tab = table_debouche_etudids(context, etudids, keep_numeric=keep_numeric)
tab.filename = make_filename("debouche_scodoc_%s" % start_year)
tab.origin = "Généré par %s le " % VERSION.SCONAME + timedate_human_repr() + ""
tab.filename = scu.make_filename("debouche_scodoc_%s" % start_year)
tab.origin = "Généré par %s le " % VERSION.SCONAME + scu.timedate_human_repr() + ""
tab.caption = "Récapitulatif débouchés à partir du 1/1/%s." % start_year
tab.base_url = "%s?start_year=%s" % (REQUEST.URL0, start_year)
return tab.make_page(
@ -77,7 +78,7 @@ def get_etudids_with_debouche(context, start_year):
start_date = str(start_year) + "-01-01"
# Recupere tous les etudid avec un debouché renseigné et une inscription dans un semestre
# posterieur à la date de depart:
# r = SimpleDictFetch(context,
# r = notesdb.SimpleDictFetch(context,
# """SELECT DISTINCT i.etudid
# FROM notes_formsemestre_inscription i, admissions adm, notes_formsemestre s
# WHERE adm.debouche is not NULL
@ -86,7 +87,7 @@ def get_etudids_with_debouche(context, start_year):
# """,
# {'start_date' : start_date })
r = SimpleDictFetch(
r = notesdb.SimpleDictFetch(
context,
"""SELECT DISTINCT i.etudid
FROM notes_formsemestre_inscription i, notes_formsemestre s, itemsuivi it
@ -100,8 +101,7 @@ def get_etudids_with_debouche(context, start_year):
def table_debouche_etudids(context, etudids, keep_numeric=True):
"""Rapport pour ces etudiants
"""
"""Rapport pour ces etudiants"""
L = []
for etudid in etudids:
etud = context.getEtudInfo(filled=1, etudid=etudid)[0]
@ -122,7 +122,7 @@ def table_debouche_etudids(context, etudids, keep_numeric=True):
"_prenom_target": "ficheEtud?etudid=" + etud["etudid"],
"_nom_td_attrs": 'id="%s" class="etudinfo"' % (etud["etudid"]),
# 'debouche' : etud['debouche'],
"moy": fmt_note(nt.get_etud_moy_gen(etudid), keep_numeric=keep_numeric),
"moy": scu.fmt_note(nt.get_etud_moy_gen(etudid), keep_numeric=keep_numeric),
"rang": nt.get_etud_rang(etudid),
"effectif": len(nt.T),
"semestre_id": last_sem["semestre_id"],
@ -189,8 +189,7 @@ def table_debouche_etudids(context, etudids, keep_numeric=True):
def report_debouche_ask_date(context, REQUEST=None):
"""Formulaire demande date départ
"""
"""Formulaire demande date départ"""
return (
context.sco_header(REQUEST)
+ """<form method="GET">
@ -223,14 +222,17 @@ def report_debouche_ask_date(context, REQUEST=None):
# admission_edit(cnx, adm)
_itemsuiviEditor = EditableTable(
_itemsuiviEditor = notesdb.EditableTable(
"itemsuivi",
"itemsuivi_id",
("itemsuivi_id", "etudid", "item_date", "situation"),
sortkey="item_date desc",
convert_null_outputs_to_empty=True,
output_formators={"situation": safehtml.HTML2SafeHTML, "item_date": DateISOtoDMY},
input_formators={"item_date": DateDMYtoISO},
output_formators={
"situation": safehtml.HTML2SafeHTML,
"item_date": notesdb.DateISOtoDMY,
},
input_formators={"item_date": notesdb.DateDMYtoISO},
)
_itemsuivi_create = _itemsuiviEditor.create
@ -240,8 +242,7 @@ _itemsuivi_edit = _itemsuiviEditor.edit
class ItemSuiviTag(sco_tag_module.ScoTag):
"""Les tags sur les items
"""
"""Les tags sur les items"""
tag_table = "itemsuivi_tags" # table (tag_id, title)
assoc_table = "itemsuivi_tags_assoc" # table (tag_id, object_id)
@ -259,8 +260,7 @@ def itemsuivi_get(cnx, itemsuivi_id, ignore_errors=False):
def itemsuivi_suppress(context, itemsuivi_id, REQUEST=None):
"""Suppression d'un item
"""
"""Suppression d'un item"""
if not context.can_edit_suivi(REQUEST):
raise AccessDenied("Vous n'avez pas le droit d'effectuer cette opération !")
cnx = context.GetDBConnexion()
@ -285,7 +285,7 @@ def itemsuivi_create(
log("created itemsuivi %s for %s" % (itemsuivi_id, etudid))
item = itemsuivi_get(cnx, itemsuivi_id)
if format == "json":
return sendJSON(REQUEST, item)
return scu.sendJSON(REQUEST, item)
return item
@ -313,7 +313,7 @@ def itemsuivi_set_situation(context, object, value, REQUEST=None):
item = itemsuivi_get(cnx, itemsuivi_id)
item["situation"] = situation
_itemsuivi_edit(cnx, item)
return situation or IT_SITUATION_MISSING_STR
return situation or scu.IT_SITUATION_MISSING_STR
def itemsuivi_list_etud(context, etudid, format=None, REQUEST=None):
@ -323,13 +323,13 @@ def itemsuivi_list_etud(context, etudid, format=None, REQUEST=None):
for it in items:
it["tags"] = ", ".join(itemsuivi_tag_list(context, it["itemsuivi_id"]))
if format == "json":
return sendJSON(REQUEST, items)
return scu.sendJSON(REQUEST, items)
return items
def itemsuivi_tag_list(context, itemsuivi_id):
"""les noms de tags associés à cet item"""
r = SimpleDictFetch(
r = notesdb.SimpleDictFetch(
context,
"""SELECT t.title
FROM itemsuivi_tags_assoc a, itemsuivi_tags t
@ -344,17 +344,17 @@ def itemsuivi_tag_list(context, itemsuivi_id):
def itemsuivi_tag_search(context, term, REQUEST=None):
"""List all used tag names (for auto-completion)"""
# restrict charset to avoid injections
if not ALPHANUM_EXP.match(term.decode(SCO_ENCODING)):
if not scu.ALPHANUM_EXP.match(term.decode(scu.SCO_ENCODING)):
data = []
else:
r = SimpleDictFetch(
r = notesdb.SimpleDictFetch(
context,
"SELECT title FROM itemsuivi_tags WHERE title LIKE %(term)s",
{"term": term + "%"},
)
data = [x["title"] for x in r]
return sendJSON(REQUEST, data)
return scu.sendJSON(REQUEST, data)
def itemsuivi_tag_set(context, itemsuivi_id="", taglist=[], REQUEST=None):
@ -372,7 +372,7 @@ def itemsuivi_tag_set(context, itemsuivi_id="", taglist=[], REQUEST=None):
# log('itemsuivi_tag_set: itemsuivi_id=%s taglist=%s' % (itemsuivi_id, taglist))
# Sanity check:
cnx = context.GetDBConnexion()
item = itemsuivi_get(cnx, itemsuivi_id)
_ = itemsuivi_get(cnx, itemsuivi_id)
newtags = set(taglist)
oldtags = set(itemsuivi_tag_list(context, itemsuivi_id))

View File

@ -5,7 +5,7 @@
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved.
# Copyright (c) 1999 - 2021 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
@ -43,8 +43,9 @@ def index_html(context, REQUEST=None, showcodes=0, showsemtable=0):
H = []
# News:
rssicon = icontag("rssscodoc_img", title="Flux RSS", border="0")
H.append(sco_news.scolar_news_summary_html(context, rssicon=rssicon))
# 2020-12-30: abandonne l'icon rss
# rssicon = icontag("rssscodoc_img", title="Flux RSS", border="0")
H.append(sco_news.scolar_news_summary_html(context)) # , rssicon=rssicon))
# Avertissement de mise à jour:
H.append(sco_up_to_date.html_up_to_date_box(context))
@ -169,8 +170,7 @@ Chercher étape courante: <input name="etape_apo" type="text" size="8"></input>
def _sem_table(context, sems):
"""Affiche liste des semestres, utilisée pour semestres en cours
"""
"""Affiche liste des semestres, utilisée pour semestres en cours"""
tmpl = """<tr class="%(trclass)s">%(tmpcode)s
<td class="semicon">%(lockimg)s <a href="Notes/formsemestre_status?formsemestre_id=%(formsemestre_id)s#groupes">%(groupicon)s</a></td>
<td class="datesem">%(mois_debut)s</td><td class="datesem"><a title="%(session_id)s">-</a> %(mois_fin)s</td>
@ -202,8 +202,7 @@ def _sem_table(context, sems):
def _sem_table_gt(context, sems, showcodes=False):
"""Nouvelle version de la table des semestres
"""
"""Nouvelle version de la table des semestres"""
_style_sems(context, sems)
columns_ids = (
"lockimg",
@ -241,8 +240,7 @@ def _sem_table_gt(context, sems, showcodes=False):
def _style_sems(context, sems):
"""ajoute quelques attributs de présentation pour la table
"""
"""ajoute quelques attributs de présentation pour la table"""
for sem in sems:
sem["_groupicon_target"] = (
"Notes/formsemestre_status?formsemestre_id=%(formsemestre_id)s" % sem

View File

@ -5,7 +5,7 @@
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved.
# Copyright (c) 1999 - 2021 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
@ -114,8 +114,7 @@ def sco_dump_and_send_db(context, REQUEST=None):
finally:
# Drop anonymized database
_drop_ano_db(ano_db_name)
# XXX _drop_ano_db(ano_db_name)
# Remove lock
fcntl.flock(x, fcntl.LOCK_UN)
@ -153,7 +152,7 @@ def _anonymize_db(ano_db_name):
cmd = os.path.join(SCO_TOOLS_DIR, "anonymize_db.py")
log("_anonymize_db: {}".format(cmd))
try:
out = subprocess.check_output([cmd, ano_db_name])
subprocess.check_output([cmd, ano_db_name])
except subprocess.CalledProcessError as e:
log("sco_dump_and_send_db: exception in anonymisation: {}".format(e))
raise ScoValueError(

View File

@ -5,7 +5,7 @@
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved.
# Copyright (c) 1999 - 2021 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
@ -37,8 +37,7 @@ import sco_formsemestre
def formation_delete(context, formation_id=None, dialog_confirmed=False, REQUEST=None):
"""Delete a formation
"""
"""Delete a formation"""
F = context.formation_list(args={"formation_id": formation_id})
if not F:
raise ScoValueError("formation inexistante !")
@ -89,14 +88,12 @@ def formation_delete(context, formation_id=None, dialog_confirmed=False, REQUEST
def formation_create(context, REQUEST=None):
"""Creation d'une formation
"""
"""Creation d'une formation"""
return formation_edit(context, create=True, REQUEST=REQUEST)
def formation_edit(context, formation_id=None, create=False, REQUEST=None):
"""Edit or create a formation
"""
"""Edit or create a formation"""
if create:
H = [
context.sco_header(REQUEST, page_title="Création d'une formation"),

View File

@ -5,7 +5,7 @@
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved.
# Copyright (c) 1999 - 2021 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
@ -36,8 +36,7 @@ import sco_formsemestre
def matiere_create(context, ue_id=None, REQUEST=None):
"""Creation d'une matiere
"""
"""Creation d'une matiere"""
UE = context.do_ue_list(args={"ue_id": ue_id})[0]
H = [
context.sco_header(REQUEST, page_title="Création d'une matière"),

View File

@ -5,7 +5,7 @@
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved.
# Copyright (c) 1999 - 2021 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
@ -55,8 +55,7 @@ saisir et modifier les notes de ce module.
def module_create(context, matiere_id=None, REQUEST=None):
"""Creation d'un module
"""
"""Creation d'un module"""
if not matiere_id:
raise ScoValueError("invalid matiere !")
M = context.do_matiere_list(args={"matiere_id": matiere_id})[0]
@ -436,8 +435,7 @@ def module_list(context, formation_id, REQUEST=None):
def formation_add_malus_modules(context, formation_id, titre=None, REQUEST=None):
"""Création d'un module de "malus" dans chaque UE d'une formation
"""
"""Création d'un module de "malus" dans chaque UE d'une formation"""
ue_list = context.do_ue_list(args={"formation_id": formation_id})
for ue in ue_list:
@ -457,8 +455,7 @@ def formation_add_malus_modules(context, formation_id, titre=None, REQUEST=None)
def ue_add_malus_module(context, ue_id, titre=None, code=None, REQUEST=None):
"""Add a malus module in this ue
"""
"""Add a malus module in this ue"""
ue = context.do_ue_list(args={"ue_id": ue_id})[0]
if titre is None:

View File

@ -5,7 +5,7 @@
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved.
# Copyright (c) 1999 - 2021 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
@ -41,14 +41,12 @@ import sco_tag_module
def ue_create(context, formation_id=None, REQUEST=None):
"""Creation d'une UE
"""
"""Creation d'une UE"""
return ue_edit(context, create=True, formation_id=formation_id, REQUEST=REQUEST)
def ue_edit(context, ue_id=None, create=False, formation_id=None, REQUEST=None):
"""Modification ou creation d'une UE
"""
"""Modification ou creation d'une UE"""
create = int(create)
if not create:
U = context.do_ue_list(args={"ue_id": ue_id})
@ -227,7 +225,7 @@ def ue_edit(context, ue_id=None, create=False, formation_id=None, REQUEST=None):
def _add_ue_semestre_id(context, ue_list):
"""ajoute semestre_id dans les ue, en regardant le premier module de chacune.
Les UE sans modules se voient attribuer le numero UE_SEM_DEFAULT (1000000),
Les UE sans modules se voient attribuer le numero UE_SEM_DEFAULT (1000000),
qui les place à la fin de la liste.
"""
for ue in ue_list:
@ -281,7 +279,7 @@ def ue_delete(
def ue_list(context, formation_id=None, msg="", REQUEST=None):
"""Liste des matières et modules d'une formation, avec liens pour
"""Liste des matières et modules d'une formation, avec liens pour
editer (si non verrouillée).
"""
authuser = REQUEST.AUTHENTICATED_USER
@ -781,8 +779,7 @@ def edit_ue_set_code_apogee(context, id=None, value=None, REQUEST=None):
# ---- Table recap formation
def formation_table_recap(context, formation_id, format="html", REQUEST=None):
"""
"""
""""""
F = context.formation_list(args={"formation_id": formation_id})
if not F:
raise ScoValueError("invalid formation_id")

View File

@ -5,7 +5,7 @@
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved.
# Copyright (c) 1999 - 2021 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
@ -67,8 +67,7 @@ def formsemestre_get_ics_url(context, sem):
def formsemestre_load_ics(context, sem):
"""Load ics data, from our cache or, when necessary, from external provider
"""
"""Load ics data, from our cache or, when necessary, from external provider"""
# TODO: cacher le résultat
ics_url = formsemestre_get_ics_url(context, sem)
if not ics_url:
@ -86,15 +85,13 @@ def formsemestre_load_ics(context, sem):
def formsemestre_edt_groups_used(context, sem):
"""L'ensemble des groupes EDT utilisés dans l'emplois du temps publié
"""
"""L'ensemble des groupes EDT utilisés dans l'emplois du temps publié"""
cal = formsemestre_load_ics(context, sem)
return {e["X-GROUP-ID"].decode("utf8") for e in events}
def get_edt_transcodage_groups(context, formsemestre_id):
""" -> { nom_groupe_edt : nom_groupe_scodoc }
"""
"""-> { nom_groupe_edt : nom_groupe_scodoc }"""
# TODO: valider ces données au moment où on enregistre les préférences
edt2sco = {}
sco2edt = {}
@ -161,8 +158,7 @@ for e in events:
def experimental_calendar(context, group_id=None, formsemestre_id=None, REQUEST=None):
"""experimental page
"""
"""experimental page"""
return "\n".join(
[
context.sco_header(

View File

@ -5,7 +5,7 @@
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved.
# Copyright (c) 1999 - 2021 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
@ -190,8 +190,7 @@ def apo_csv_list_stored_etapes(context, annee_scolaire, sem_id=None, etapes=None
def apo_csv_delete(context, archive_id):
"""Delete archived CSV
"""
"""Delete archived CSV"""
ApoCSVArchive.delete_archive(archive_id)
@ -224,19 +223,18 @@ def apo_csv_get(context, etape_apo="", annee_scolaire="", sem_id=""):
def apo_get_sem_etapes(context, sem):
"""Etapes de ce semestre: pour l'instant, celles déclarées
Dans une future version, on pourrait aussi utiliser les étapes
d'inscription des étudiants, recupérées via le portail,
"""Etapes de ce semestre: pour l'instant, celles déclarées
Dans une future version, on pourrait aussi utiliser les étapes
d'inscription des étudiants, recupérées via le portail,
voir check_paiement_etuds().
:return: list of etape_apo (ApoEtapeVDI instances)
"""
return sem["etapes"]
def apo_csv_check_etape(context, semset, set_nips, etape_apo):
"""Check etape vs set of sems
"""
"""Check etape vs set of sems"""
# Etudiants dans la maquette CSV:
csv_data = apo_csv_get(
context, etape_apo, semset["annee_scolaire"], semset["sem_id"]
@ -254,7 +252,10 @@ def apo_csv_check_etape(context, semset, set_nips, etape_apo):
return nips_ok, apo_nips, nips_no_apo, nips_no_sco, maq_elems, sem_elems
def apo_csv_semset_check(context, semset, allow_missing_apo=False, allow_missing_csv=False): # was apo_csv_check
def apo_csv_semset_check(
context, semset, allow_missing_apo=False, allow_missing_csv=False
): # was apo_csv_check
"""
check students in stored maqs vs students in semset
Cas à détecter:

View File

@ -5,7 +5,7 @@
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved.
# Copyright (c) 1999 - 2021 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
@ -58,9 +58,7 @@ def apo_semset_maq_status(
block_export_res_sdj=True,
REQUEST=None,
):
"""Page statut / tableau de bord
"""
"""Page statut / tableau de bord"""
if not semset_id:
raise ValueError("invalid null semset_id")
semset = sco_semset.SemSet(context, semset_id=semset_id)
@ -91,7 +89,9 @@ def apo_semset_maq_status(
apo_dups,
maq_elems,
sem_elems,
) = sco_etape_apogee.apo_csv_semset_check(context, semset, allow_missing_apo, allow_missing_csv)
) = sco_etape_apogee.apo_csv_semset_check(
context, semset, allow_missing_apo, allow_missing_csv
)
if not allow_missing_decisions:
ok_for_export &= semset["jury_ok"]
@ -265,9 +265,7 @@ def apo_semset_maq_status(
)
if allow_missing_csv:
H.append("checked")
H.append(
""" >autoriser export même si étapes sans maquettes</input></div>"""
)
H.append(""" >autoriser export même si étapes sans maquettes</input></div>""")
H.append("""</form>""")
if semset and ok_for_export:
@ -295,7 +293,9 @@ def apo_semset_maq_status(
H.append(
"""<div><label><input type="checkbox" name="block_export_res_etape" value="1" %s %s>%s</input></label></div>"""
% checked(
block_export_res_etape, "export_res_etape", "résultat de l'étape (VET), sauf si diplôme"
block_export_res_etape,
"export_res_etape",
"résultat de l'étape (VET), sauf si diplôme",
)
)
H.append(
@ -424,8 +424,7 @@ def apo_semset_maq_status(
def table_apo_csv_list(context, semset, REQUEST=None):
"""Table des archives (triée par date d'archivage)
"""
"""Table des archives (triée par date d'archivage)"""
annee_scolaire = semset["annee_scolaire"]
sem_id = semset["sem_id"]
@ -481,8 +480,7 @@ def table_apo_csv_list(context, semset, REQUEST=None):
def view_apo_etuds(context, semset_id, title="", nips=[], format="html", REQUEST=None):
"""Table des étudiants Apogée par nips
"""
"""Table des étudiants Apogée par nips"""
if not semset_id:
raise ValueError("invalid null semset_id")
semset = sco_semset.SemSet(context, semset_id=semset_id)
@ -520,8 +518,7 @@ def view_apo_etuds(context, semset_id, title="", nips=[], format="html", REQUEST
def view_scodoc_etuds(
context, semset_id, title="", etudids=None, nips=None, format="html", REQUEST=None
):
"""Table des étudiants ScoDoc par nips ou etudids
"""
"""Table des étudiants ScoDoc par nips ou etudids"""
if etudids is not None:
if type(etudids) != type([]):
etudids = [etudids]
@ -636,8 +633,7 @@ def view_apo_csv_store(
def view_apo_csv_download_and_store(context, etape_apo="", semset_id="", REQUEST=None):
"""Download maquette and store it
"""
"""Download maquette and store it"""
if not semset_id:
raise ValueError("invalid null semset_id")
semset = sco_semset.SemSet(context, semset_id=semset_id)
@ -656,8 +652,7 @@ def view_apo_csv_download_and_store(context, etape_apo="", semset_id="", REQUEST
def view_apo_csv_delete(
context, etape_apo="", semset_id="", dialog_confirmed=False, REQUEST=None
):
"""Delete CSV file
"""
"""Delete CSV file"""
if not semset_id:
raise ValueError("invalid null semset_id")
semset = sco_semset.SemSet(context, semset_id=semset_id)

View File

@ -5,7 +5,7 @@
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2019 Emmanuel Viennet. All rights reserved.
# Copyright (c) 1999 - 2021 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

View File

@ -5,7 +5,7 @@
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved.
# Copyright (c) 1999 - 2021 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
@ -27,15 +27,23 @@
"""Evaluations
"""
import time
import urllib
import operator
import datetime
from notes_log import log, logCallStack
from sco_utils import *
from notesdb import *
import sco_utils as scu
from notesdb import ScoDocCursor
from sco_exceptions import AccessDenied, ScoValueError
import VERSION
from gen_tables import GenTable
from TrivialFormulator import TrivialFormulator
import sco_news
import sco_formsemestre
import sco_groups
import ZAbsences
import sco_evaluations
# --------------------------------------------------------------------
#
@ -47,7 +55,7 @@ def notes_moyenne_median_mini_maxi(notes):
notes = [
x
for x in notes
if (x != None) and (x != NOTES_NEUTRALISE) and (x != NOTES_ATTENTE)
if (x != None) and (x != scu.NOTES_NEUTRALISE) and (x != scu.NOTES_ATTENTE)
]
n = len(notes)
if not n:
@ -136,8 +144,8 @@ def do_evaluation_etat(
NotesDB = context._notes_getall(evaluation_id) # { etudid : value }
notes = [x["value"] for x in NotesDB.values()]
nb_abs = len([x for x in notes if x is None])
nb_neutre = len([x for x in notes if x == NOTES_NEUTRALISE])
nb_att = len([x for x in notes if x == NOTES_ATTENTE])
nb_neutre = len([x for x in notes if x == scu.NOTES_NEUTRALISE])
nb_att = len([x for x in notes if x == scu.NOTES_ATTENTE])
moy_num, median_num, mini_num, maxi_num = notes_moyenne_median_mini_maxi(notes)
if moy_num is None:
median, moy = "", ""
@ -145,10 +153,10 @@ def do_evaluation_etat(
mini, maxi = "", ""
mini_num, maxi_num = None, None
else:
median = fmt_note(median_num)
moy = fmt_note(moy_num)
mini = fmt_note(mini_num)
maxi = fmt_note(maxi_num)
median = scu.fmt_note(median_num)
moy = scu.fmt_note(moy_num)
mini = scu.fmt_note(mini_num)
maxi = scu.fmt_note(maxi_num)
# cherche date derniere modif note
if len(NotesDB):
t = [x["date"] for x in NotesDB.values()]
@ -159,7 +167,7 @@ def do_evaluation_etat(
E = context.do_evaluation_list(args={"evaluation_id": evaluation_id})[0]
M = context.do_moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0]
Mod = context.do_module_list(args={"module_id": M["module_id"]})[0]
is_malus = Mod["module_type"] == MODULE_MALUS # True si module de malus
is_malus = Mod["module_type"] == scu.MODULE_MALUS # True si module de malus
formsemestre_id = M["formsemestre_id"]
# Si partition_id is None, prend 'all' ou bien la premiere:
if partition_id is None:
@ -186,8 +194,8 @@ def do_evaluation_etat(
# On considere une note "manquante" lorsqu'elle n'existe pas
# ou qu'elle est en attente (ATT)
GrNbMissing = DictDefault() # group_id : nb notes manquantes
GrNotes = DictDefault(defaultvalue=[]) # group_id: liste notes valides
GrNbMissing = scu.DictDefault() # group_id : nb notes manquantes
GrNotes = scu.DictDefault(defaultvalue=[]) # group_id: liste notes valides
TotalNbMissing = 0
TotalNbAtt = 0
groups = {} # group_id : group
@ -201,14 +209,14 @@ def do_evaluation_etat(
isMissing = False
if NotesDB.has_key(i["etudid"]):
val = NotesDB[i["etudid"]]["value"]
if val == NOTES_ATTENTE:
if val == scu.NOTES_ATTENTE:
isMissing = True
TotalNbAtt += 1
if group:
GrNotes[group["group_id"]].append(val)
else:
if group:
junk = GrNotes[group["group_id"]] # create group
_ = GrNotes[group["group_id"]] # create group
isMissing = True
if isMissing:
TotalNbMissing += 1
@ -219,7 +227,7 @@ def do_evaluation_etat(
gr_incomplets.sort()
if (
(TotalNbMissing > 0)
and (E["evaluation_type"] != EVALUATION_RATTRAPAGE)
and (E["evaluation_type"] != scu.EVALUATION_RATTRAPAGE)
and not is_malus
):
complete = False
@ -244,15 +252,15 @@ def do_evaluation_etat(
"group_id": group_id,
"group_name": groups[group_id]["group_name"],
"gr_moy_num": gr_moy,
"gr_moy": fmt_note(gr_moy),
"gr_moy": scu.fmt_note(gr_moy),
"gr_median_num": gr_median,
"gr_median": fmt_note(gr_median),
"gr_mini": fmt_note(gr_mini),
"gr_maxi": fmt_note(gr_maxi),
"gr_mini": gr_mini,
"gr_maxi": gr_maxi,
"gr_median": scu.fmt_note(gr_median),
"gr_mini": scu.fmt_note(gr_mini),
"gr_maxi": scu.fmt_note(gr_maxi),
"gr_mini_num": gr_mini,
"gr_maxi_num": gr_maxi,
"gr_nb_notes": len(notes),
"gr_nb_att": len([x for x in notes if x == NOTES_ATTENTE]),
"gr_nb_att": len([x for x in notes if x == scu.NOTES_ATTENTE]),
}
)
gr_moyennes.sort(key=operator.itemgetter("group_name"))
@ -337,7 +345,7 @@ def do_evaluation_list_in_sem(context, formsemestre_id):
'visibulletin': 1} ]
"""
req = "select E.* from notes_evaluation E, notes_moduleimpl MI where MI.formsemestre_id = %(formsemestre_id)s and MI.moduleimpl_id = E.moduleimpl_id"
req = "select E.* from notes_evaluation E, notes_moduleimpl MI where MI.formsemestre_id = %(formsemestre_id)s and MI.moduleimpl_id = E.moduleimpl_id order by moduleimpl_id, numero desc, jour desc, heure_debut desc"
cnx = context.GetDBConnexion()
cursor = cnx.cursor(cursor_factory=ScoDocCursor)
cursor.execute(req, {"formsemestre_id": formsemestre_id})
@ -380,7 +388,7 @@ def _eval_etat(evals):
nb_evals_en_cours += 1
dates.append(e["etat"]["last_modif"])
dates = sort_dates(dates)
dates = scu.sort_dates(dates)
if len(dates):
last_modif = dates[-1] # date de derniere modif d'une note dans un module
@ -511,18 +519,18 @@ def evaluation_date_first_completion(context, evaluation_id):
if not etat["evalcomplete"]:
return None
E = context.do_evaluation_list(args={"evaluation_id": evaluation_id})[0]
M = context.do_moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0]
formsemestre_id = M["formsemestre_id"]
# XXX inachevé ou à revoir ?
# Il faut considerer les inscriptions au semestre
# (pour avoir l'etat et le groupe) et aussi les inscriptions
# au module (pour gerer les modules optionnels correctement)
insem = context.do_formsemestre_inscription_listinscrits(formsemestre_id)
insmod = context.do_moduleimpl_inscription_list(moduleimpl_id=E["moduleimpl_id"])
insmodset = set([x["etudid"] for x in insmod])
# E = context.do_evaluation_list(args={"evaluation_id": evaluation_id})[0]
# M = context.do_moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0]
# formsemestre_id = M["formsemestre_id"]
# insem = context.do_formsemestre_inscription_listinscrits(formsemestre_id)
# insmod = context.do_moduleimpl_inscription_list(moduleimpl_id=E["moduleimpl_id"])
# insmodset = set([x["etudid"] for x in insmod])
# retire de insem ceux qui ne sont pas inscrits au module
ins = [i for i in insem if i["etudid"] in insmodset]
# ins = [i for i in insem if i["etudid"] in insmodset]
notes = context._notes_getall(evaluation_id, filter_suppressed=False).values()
notes_log = context._notes_getall(
@ -560,8 +568,8 @@ def formsemestre_evaluations_delai_correction(
for e in evals:
M = context.do_moduleimpl_list(moduleimpl_id=e["moduleimpl_id"])[0]
Mod = context.do_module_list(args={"module_id": M["module_id"]})[0]
if (e["evaluation_type"] != EVALUATION_NORMALE) or (
Mod["module_type"] == MODULE_MALUS
if (e["evaluation_type"] != scu.EVALUATION_NORMALE) or (
Mod["module_type"] == scu.MODULE_MALUS
):
continue
e["date_first_complete"] = evaluation_date_first_completion(
@ -612,8 +620,8 @@ def formsemestre_evaluations_delai_correction(
caption="Correction des évaluations du semestre",
preferences=context.get_preferences(formsemestre_id),
base_url="%s?formsemestre_id=%s" % (REQUEST.URL0, formsemestre_id),
origin="Généré par %s le " % VERSION.SCONAME + timedate_human_repr() + "",
filename=make_filename("evaluations_delais_" + sem["titreannee"]),
origin="Généré par %s le " % VERSION.SCONAME + scu.timedate_human_repr() + "",
filename=scu.make_filename("evaluations_delais_" + sem["titreannee"]),
)
return tab.make_page(context, format=format, REQUEST=REQUEST)
@ -727,7 +735,6 @@ def evaluation_describe(context, evaluation_id="", edit_in_place=True, REQUEST=N
M = context.do_moduleimpl_list(moduleimpl_id=moduleimpl_id)[0]
Mod = context.do_module_list(args={"module_id": M["module_id"]})[0]
formsemestre_id = M["formsemestre_id"]
sem = sco_formsemestre.get_formsemestre(context, formsemestre_id)
u = context.Users.user_info(M["responsable_id"])
resp = u["prenomnom"]
nomcomplet = u["nomcomplet"]
@ -747,13 +754,13 @@ def evaluation_describe(context, evaluation_id="", edit_in_place=True, REQUEST=N
etit = E["description"] or ""
if etit:
etit = ' "' + etit + '"'
if Mod["module_type"] == MODULE_MALUS:
if Mod["module_type"] == scu.MODULE_MALUS:
etit += ' <span class="eval_malus">(points de malus)</span>'
H = [
'<span class="eval_title">Evaluation%s</span><p><b>Module : %s</b></p>'
% (etit, mod_descr)
]
if Mod["module_type"] == MODULE_MALUS:
if Mod["module_type"] == scu.MODULE_MALUS:
# Indique l'UE
ue = context.do_ue_list(args={"ue_id": Mod["ue_id"]})[0]
H.append("<p><b>UE : %(acronyme)s</b></p>" % ue)
@ -804,9 +811,9 @@ def evaluation_create_form(
moduleimpl_id = the_eval["moduleimpl_id"]
#
M = context.do_moduleimpl_withmodule_list(moduleimpl_id=moduleimpl_id)[0]
is_malus = M["module"]["module_type"] == MODULE_MALUS # True si module de malus
is_malus = M["module"]["module_type"] == scu.MODULE_MALUS # True si module de malus
formsemestre_id = M["formsemestre_id"]
min_note_max = NOTES_PRECISION # le plus petit bareme possible
min_note_max = scu.NOTES_PRECISION # le plus petit bareme possible
if not readonly:
try:
context._evaluation_check_write_access(REQUEST, moduleimpl_id=moduleimpl_id)
@ -853,17 +860,16 @@ def evaluation_create_form(
# Note maximale actuelle dans cette eval ?
etat = do_evaluation_etat(context, evaluation_id)
if etat["maxi_num"] is not None:
min_note_max = max(NOTES_PRECISION, etat["maxi_num"])
min_note_max = max(scu.NOTES_PRECISION, etat["maxi_num"])
else:
min_note_max = NOTES_PRECISION
min_note_max = scu.NOTES_PRECISION
#
if min_note_max > NOTES_PRECISION:
min_note_max_str = fmt_note(min_note_max)
if min_note_max > scu.NOTES_PRECISION:
min_note_max_str = scu.fmt_note(min_note_max)
else:
min_note_max_str = "0"
#
Mod = context.do_module_list(args={"module_id": M["module_id"]})[0]
sem = sco_formsemestre.get_formsemestre(context, M["formsemestre_id"])
#
help = """<div class="help"><p class="help">
Le coefficient d'une évaluation n'est utilisé que pour pondérer les évaluations au sein d'un module.
@ -973,7 +979,7 @@ def evaluation_create_form(
"title": "Notes de 0 à",
"explanation": "barème (note max actuelle: %s)" % min_note_max_str,
"allow_null": False,
"max_value": NOTES_MAX,
"max_value": scu.NOTES_MAX,
"min_value": min_note_max,
},
),
@ -1008,7 +1014,7 @@ def evaluation_create_form(
{
"input_type": "menu",
"title": "Modalité",
"allowed_values": (EVALUATION_NORMALE, EVALUATION_RATTRAPAGE),
"allowed_values": (scu.EVALUATION_NORMALE, scu.EVALUATION_RATTRAPAGE),
"type": "int",
"labels": ("Normale", "Rattrapage"),
},

View File

@ -5,7 +5,7 @@
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved.
# Copyright (c) 1999 - 2021 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
@ -237,8 +237,7 @@ class ScoExcelSheet:
def Excel_SimpleTable(titles=[], lines=[[]], SheetName="feuille", titlesStyles=[]):
"""Export simple type 'CSV': 1ere ligne en gras, le reste tel quel
"""
"""Export simple type 'CSV': 1ere ligne en gras, le reste tel quel"""
# XXX devrait maintenant utiliser ScoExcelSheet
wb = Workbook()
ws0 = wb.add_sheet(SheetName.decode(SCO_ENCODING))

View File

@ -5,7 +5,7 @@
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved.
# Copyright (c) 1999 - 2021 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

View File

@ -5,7 +5,7 @@
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved.
# Copyright (c) 1999 - 2021 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
@ -201,8 +201,7 @@ def _build_results_list(context, dpv_by_sem, etuds_infos):
def get_set_formsemestre_id_dates(context, start_date, end_date):
"""Ensemble des formsemestre_id entre ces dates
"""
"""Ensemble des formsemestre_id entre ces dates"""
s = SimpleDictFetch(
context,
"SELECT formsemestre_id FROM notes_formsemestre WHERE date_debut >= %(start_date)s AND date_fin <= %(end_date)s",
@ -216,7 +215,7 @@ def scodoc_table_results(
):
"""Page affichant la table des résultats
Les dates sont en dd/mm/yyyy (datepicker javascript)
types_parcours est la liste des types de parcours à afficher
types_parcours est la liste des types de parcours à afficher
(liste de chaines, eg ['100', '210'] )
"""
log("scodoc_table_results: start_date=%s" % (start_date,)) # XXX

View File

@ -5,7 +5,7 @@
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved.
# Copyright (c) 1999 - 2021 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
@ -216,7 +216,7 @@ def search_etud_in_dept(
# Was chercheEtudsInfo()
def search_etuds_infos(context, expnom=None, code_nip=None, REQUEST=None):
"""recherche les étudiants correspondants à expnom ou au code_nip
et ramene liste de mappings utilisables en DTML.
et ramene liste de mappings utilisables en DTML.
"""
may_be_nip = is_valid_code_nip(expnom)
cnx = context.GetDBConnexion()
@ -286,8 +286,7 @@ def search_etud_by_name(context, term, REQUEST=None):
def form_search_etud_in_accessible_depts(context, REQUEST):
"""Form recherche etudiants pour page accueil ScoDoc
"""
"""Form recherche etudiants pour page accueil ScoDoc"""
authuser = REQUEST.AUTHENTICATED_USER
# present form only to authenticated users
if not authuser.has_role("Authenticated"):

View File

@ -5,7 +5,7 @@
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved.
# Copyright (c) 1999 - 2021 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

View File

@ -5,7 +5,7 @@
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved.
# Copyright (c) 1999 - 2021 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

View File

@ -5,7 +5,7 @@
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved.
# Copyright (c) 1999 - 2021 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

View File

@ -5,7 +5,7 @@
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved.
# Copyright (c) 1999 - 2021 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

View File

@ -5,7 +5,7 @@
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved.
# Copyright (c) 1999 - 2021 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

View File

@ -5,7 +5,7 @@
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved.
# Copyright (c) 1999 - 2021 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

View File

@ -5,7 +5,7 @@
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved.
# Copyright (c) 1999 - 2021 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
@ -644,6 +644,12 @@ def formsemestre_description_table(
ModInscrits = context.do_moduleimpl_inscription_list(
moduleimpl_id=M["moduleimpl_id"]
)
enseignants = ", ".join(
[
context.Users.user_info(m["ens_id"], REQUEST)["nomprenom"]
for m in M["ens"]
]
)
l = {
"UE": M["ue"]["acronyme"],
"Code": M["module"]["code"],
@ -652,6 +658,8 @@ def formsemestre_description_table(
"Inscrits": len(ModInscrits),
"Responsable": context.Users.user_info(M["responsable_id"])["nomprenom"],
"_Responsable_class": "scotext",
"Enseignants": enseignants,
"_Enseignants_class": "scotext",
"Coef.": M["module"]["coefficient"],
# 'ECTS' : M['module']['ects'],
# Lien sur titre -> module
@ -689,7 +697,7 @@ def formsemestre_description_table(
columns_ids = ["UE", "Code", "Module", "Coef."]
if context.get_preference("bul_show_ects", formsemestre_id):
columns_ids += ["ects"]
columns_ids += ["Inscrits", "Responsable"]
columns_ids += ["Inscrits", "Responsable", "Enseignants"]
if with_evals:
columns_ids += [
"jour",
@ -899,6 +907,12 @@ def formsemestre_status(context, formsemestre_id=None, REQUEST=None):
<th class="resp">Responsable</th>
<th class="evals">Evaluations</th></tr>"""
)
mails_enseignants = set(
[
context.Users.user_info(ens_id, REQUEST)["email"]
for ens_id in sem["responsables"]
]
) # adr. mail des enseignants
for M in Mlist:
Mod = M["module"]
ModDescr = (
@ -915,6 +929,12 @@ def formsemestre_status(context, formsemestre_id=None, REQUEST=None):
ModInscrits = context.do_moduleimpl_inscription_list(
moduleimpl_id=M["moduleimpl_id"]
)
mails_enseignants.add(
context.Users.user_info(M["responsable_id"], REQUEST)["email"]
)
mails_enseignants |= set(
[context.Users.user_info(m["ens_id"], REQUEST)["email"] for m in M["ens"]]
)
ue = M["ue"]
if prev_ue_id != ue["ue_id"]:
prev_ue_id = ue["ue_id"]
@ -1039,5 +1059,11 @@ def formsemestre_status(context, formsemestre_id=None, REQUEST=None):
)
# --- LISTE DES ETUDIANTS
H += ['<div id="groupes">', context.make_listes_sem(sem, REQUEST), "</div>"]
# --- Lien mail enseignants:
adrlist = list(mails_enseignants - set([""]))
if adrlist:
H.append(
'<p><a class="stdlink" href="mailto:?cc=%s">Courrier aux %d enseignants du semestre</a></p>'
% (",".join(adrlist), len(adrlist))
)
return "".join(H) + context.sco_footer(REQUEST)

View File

@ -5,7 +5,7 @@
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved.
# Copyright (c) 1999 - 2021 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
@ -480,7 +480,7 @@ def formsemestre_recap_parcours_table(
show_details=False,
):
"""Tableau HTML recap parcours
Si with_links, ajoute liens pour modifier decisions (colonne de droite)
Si with_links, ajoute liens pour modifier decisions (colonne de droite)
sem_info = { formsemestre_id : txt } permet d'ajouter des informations associées à chaque semestre
with_all_columns: si faux, pas de colonne "assiduité".
"""
@ -690,8 +690,7 @@ def formsemestre_recap_parcours_table(
def form_decision_manuelle(
context, Se, formsemestre_id, etudid, desturl="", sortcol=None
):
"""Formulaire pour saisie décision manuelle
"""
"""Formulaire pour saisie décision manuelle"""
H = [
"""
<script type="text/javascript">
@ -1033,8 +1032,7 @@ def formsemestre_fix_validation_ues(context, formsemestre_id, REQUEST=None):
def formsemestre_validation_suppress_etud(context, formsemestre_id, etudid):
"""Suppression des decisions de jury pour un etudiant.
"""
"""Suppression des decisions de jury pour un etudiant."""
log("formsemestre_validation_suppress_etud( %s, %s)" % (formsemestre_id, etudid))
cnx = context.GetDBConnexion(autocommit=False)
cursor = cnx.cursor(cursor_factory=ScoDocCursor)
@ -1064,7 +1062,7 @@ def formsemestre_validation_suppress_etud(context, formsemestre_id, etudid):
def formsemestre_validate_previous_ue(context, formsemestre_id, etudid, REQUEST=None):
"""Form. saisie UE validée hors ScoDoc
"""Form. saisie UE validée hors ScoDoc
(pour étudiants arrivant avec un UE antérieurement validée).
"""
etud = context.getEtudInfo(etudid=etudid, filled=True)[0]
@ -1205,7 +1203,7 @@ def do_formsemestre_validate_previous_ue(
REQUEST=None,
):
"""Enregistre (ou modifie) validation d'UE (obtenue hors ScoDoc).
Si le coefficient est spécifié, modifie le coefficient de
Si le coefficient est spécifié, modifie le coefficient de
cette UE (utile seulement pour les semestres extérieurs).
"""
sem = sco_formsemestre.get_formsemestre(context, formsemestre_id)
@ -1265,8 +1263,7 @@ def _invalidate_etud_formation_caches(context, etudid, formation_id):
def get_etud_ue_cap_html(context, etudid, formsemestre_id, ue_id, REQUEST=None):
"""Ramene bout de HTML pour pouvoir supprimer une validation de cette UE
"""
"""Ramene bout de HTML pour pouvoir supprimer une validation de cette UE"""
valids = SimpleDictFetch(
context,
"""SELECT SFV.* FROM scolar_formsemestre_validation SFV

View File

@ -5,7 +5,7 @@
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved.
# Copyright (c) 1999 - 2021 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

View File

@ -5,7 +5,7 @@
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved.
# Copyright (c) 1999 - 2021 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

View File

@ -5,7 +5,7 @@
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved.
# Copyright (c) 1999 - 2021 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

View File

@ -5,7 +5,7 @@
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved.
# Copyright (c) 1999 - 2021 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
@ -431,10 +431,10 @@ def groups_table(
format: csv, json, xml, xls, allxls, xlsappel, moodlecsv, pdf
Si with_codes, ajoute 4 colonnes avec les codes etudid, NIP, INE et etape
"""
log(
"enter groups_table %s: %s"
% (groups_infos.members[0]["nom"], groups_infos.members[0].get("etape", "-"))
)
# log(
# "enter groups_table %s: %s"
# % (groups_infos.members[0]["nom"], groups_infos.members[0].get("etape", "-"))
# )
authuser = REQUEST.AUTHENTICATED_USER
with_codes = int(with_codes)
@ -798,6 +798,11 @@ def tab_absences_html(context, groups_infos, etat=None, REQUEST=None):
"<h3>Absences</h3>",
'<ul class="ul_abs">',
"<li>",
form_choix_saisie_semaine(
context, groups_infos, REQUEST=REQUEST
), # Ajout Le Havre
"</li>",
"<li>",
form_choix_jour_saisie_hebdo(context, groups_infos, REQUEST=REQUEST),
"</li>",
"""<li><a class="stdlink" href="Absences/EtatAbsencesGr?%s&amp;debut=%s&amp;fin=%s">Etat des absences du groupe</a></li>"""
@ -888,6 +893,37 @@ def form_choix_jour_saisie_hebdo(context, groups_infos, REQUEST=None):
return "\n".join(FA)
# Ajout Le Havre
# Formulaire saisie absences semaine
def form_choix_saisie_semaine(context, groups_infos, REQUEST=None):
authuser = REQUEST.AUTHENTICATED_USER
if not authuser.has_permission(ScoAbsChange, context):
return ""
sem = groups_infos.formsemestre
# construit l'URL "destination"
# (a laquelle on revient apres saisie absences)
query_args = cgi.parse_qs(REQUEST.QUERY_STRING)
moduleimpl_id = query_args.get("moduleimpl_id", [""])[0]
if "head_message" in query_args:
del query_args["head_message"]
destination = "%s?%s" % (REQUEST.URL, urllib.urlencode(query_args, True))
destination = destination.replace(
"%", "%%"
) # car ici utilisee dans un format string !
DateJour = time.strftime("%d/%m/%Y")
datelundi = ZAbsences.ddmmyyyy(DateJour).prev_monday()
FA = [] # formulaire avec menu saisi hebdo des absences
FA.append('<form action="Absences/SignaleAbsenceGrHebdo" method="get">')
FA.append('<input type="hidden" name="datelundi" value="%s"/>' % datelundi)
FA.append('<input type="hidden" name="moduleimpl_id" value="%s"/>' % moduleimpl_id)
FA.append('<input type="hidden" name="destination" value="%s"/>' % destination)
FA.append(groups_infos.get_form_elem())
FA.append('<input type="submit" class="button" value="Saisie à la semaine" />')
FA.append("</form>")
return "\n".join(FA)
def export_groups_as_moodle_csv(context, formsemestre_id=None, REQUEST=None):
"""Export all students/groups, in a CSV format suitable for Moodle
Each (student,group) will be listed on a separate line

View File

@ -5,7 +5,7 @@
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved.
# Copyright (c) 1999 - 2021 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

View File

@ -5,7 +5,7 @@
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved.
# Copyright (c) 1999 - 2021 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

View File

@ -5,7 +5,7 @@
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved.
# Copyright (c) 1999 - 2021 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

View File

@ -5,7 +5,7 @@
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved.
# Copyright (c) 1999 - 2021 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
@ -45,8 +45,7 @@ import sco_formsemestre
def formsemestre_table_etuds_lycees(
context, formsemestre_id, group_lycees=True, only_primo=False
):
"""Récupère liste d'etudiants avec etat et decision.
"""
"""Récupère liste d'etudiants avec etat et decision."""
sem = sco_formsemestre.get_formsemestre(context, formsemestre_id)
etuds = sco_report.tsp_etud_list(context, formsemestre_id, only_primo=only_primo)[0]
if only_primo:
@ -60,8 +59,7 @@ def formsemestre_table_etuds_lycees(
def scodoc_table_etuds_lycees(context, format="html", REQUEST=None):
"""Table avec _tous_ les étudiants des semestres non verrouillés de _tous_ les départements.
"""
"""Table avec _tous_ les étudiants des semestres non verrouillés de _tous_ les départements."""
semdepts = sco_formsemestre.scodoc_get_all_unlocked_sems(context)
etuds = []
for (sem, deptcontext) in semdepts:

View File

@ -5,7 +5,7 @@
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved.
# Copyright (c) 1999 - 2021 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
@ -41,8 +41,7 @@ import sco_codes_parcours
def list_formsemestres_modalites(context, sems):
"""Liste ordonnée des modalités présentes dans ces formsemestres
"""
"""Liste ordonnée des modalités présentes dans ces formsemestres"""
modalites = {}
for sem in sems:
if sem["modalite"] not in modalites:
@ -86,8 +85,7 @@ _modaliteEditor = EditableTable(
def do_modalite_list(context, *args, **kw):
"""Liste des modalites
"""
"""Liste des modalites"""
cnx = context.GetDBConnexion()
return _modaliteEditor.list(cnx, *args, **kw)

View File

@ -5,7 +5,7 @@
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved.
# Copyright (c) 1999 - 2021 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
@ -46,11 +46,11 @@ def moduleimpl_inscriptions_edit(
* Gestion des inscriptions
Nom TD TA TP (triable)
[x] M. XXX YYY - - -
ajouter TD A, TD B, TP 1, TP 2 ...
supprimer TD A, TD B, TP 1, TP 2 ...
* Si pas les droits: idem en readonly
"""
M = context.do_moduleimpl_list(moduleimpl_id=moduleimpl_id)[0]
@ -131,10 +131,10 @@ def moduleimpl_inscriptions_edit(
"""
% M
)
H.append('<table><tr>')
H.append("<table><tr>")
H.append(_make_menu(context, partitions, "Ajouter", "true"))
H.append(_make_menu(context, partitions, "Enlever", "false"))
H.append('</tr></table>')
H.append("</tr></table>")
H.append(
"""
<p><br/></p>
@ -217,14 +217,10 @@ def _make_menu_old_xxx(context, partitions, title="", check="true"):
return "".join(H)
def _make_menu(context, partitions, title="", check="true"):
"""Menu with list of all groups"""
items = [
{
"title" : "Tous",
"attr" : "onclick=\"group_select('', -1, %s)\"" % check
}
]
items = [{"title": "Tous", "attr": "onclick=\"group_select('', -1, %s)\"" % check}]
p_idx = 0
for partition in partitions:
if partition["partition_name"] != None:
@ -232,12 +228,18 @@ def _make_menu(context, partitions, title="", check="true"):
for group in sco_groups.get_partition_groups(context, partition):
items.append(
{
"title" : "%s %s" % (partition["partition_name"], group["group_name"]),
"attr" : "onclick=\"group_select('%s', %s, %s)\"" % (
group["group_name"], p_idx, check)
"title": "%s %s"
% (partition["partition_name"], group["group_name"]),
"attr": "onclick=\"group_select('%s', %s, %s)\""
% (group["group_name"], p_idx, check),
}
)
return '<td class="inscr_addremove_menu">' + makeMenu( title, items, base_url=context.absolute_url(), alone=True ) + "</td>"
return (
'<td class="inscr_addremove_menu">'
+ makeMenu(title, items, base_url=context.absolute_url(), alone=True)
+ "</td>"
)
def moduleimpl_inscriptions_stats(context, formsemestre_id, REQUEST=None):
"""Affiche quelques informations sur les inscriptions
@ -257,7 +259,7 @@ def moduleimpl_inscriptions_stats(context, formsemestre_id, REQUEST=None):
groupes de TD A, B et C
tous sauf groupe de TP Z (?)
tous sauf <liste d'au plus 7 noms>
"""
authuser = REQUEST.AUTHENTICATED_USER
@ -278,7 +280,7 @@ def moduleimpl_inscriptions_stats(context, formsemestre_id, REQUEST=None):
commons = [] # modules communs a tous les etuds du semestre
options = [] # modules ou seuls quelques etudiants sont inscrits
for mod in Mlist:
all, nb_inscrits, descr = descr_inscrs_module(
tous_inscrits, nb_inscrits, descr = descr_inscrs_module(
context,
sem,
mod["moduleimpl_id"],
@ -286,7 +288,7 @@ def moduleimpl_inscriptions_stats(context, formsemestre_id, REQUEST=None):
partitions,
partitions_etud_groups,
)
if all:
if tous_inscrits:
commons.append(mod)
else:
mod["descri"] = descr
@ -420,8 +422,7 @@ def moduleimpl_inscriptions_stats(context, formsemestre_id, REQUEST=None):
def descr_inscrs_module(
context, sem, moduleimpl_id, set_all, partitions, partitions_etud_groups
):
"""returns All, nb_inscrits, descr All true si tous inscrits
"""
"""returns tous_inscrits, nb_inscrits, descr"""
ins = context.do_moduleimpl_inscription_list(moduleimpl_id=moduleimpl_id)
set_m = set([x["etudid"] for x in ins]) # ens. des inscrits au module
non_inscrits = set_all - set_m
@ -523,8 +524,7 @@ def is_inscrit_ue(context, etudid, formsemestre_id, ue_id):
def do_etud_desinscrit_ue(context, etudid, formsemestre_id, ue_id, REQUEST=None):
"""Desincrit l'etudiant de tous les modules de cette UE dans ce semestre.
"""
"""Desincrit l'etudiant de tous les modules de cette UE dans ce semestre."""
cnx = context.GetDBConnexion()
cursor = cnx.cursor(cursor_factory=ScoDocCursor)
cursor.execute(
@ -558,8 +558,7 @@ def do_etud_desinscrit_ue(context, etudid, formsemestre_id, ue_id, REQUEST=None)
def do_etud_inscrit_ue(context, etudid, formsemestre_id, ue_id, REQUEST=None):
"""Incrit l'etudiant de tous les modules de cette UE dans ce semestre.
"""
"""Incrit l'etudiant de tous les modules de cette UE dans ce semestre."""
# Verifie qu'il est bien inscrit au semestre
insem = context.do_formsemestre_inscription_list(
args={"formsemestre_id": formsemestre_id, "etudid": etudid}

View File

@ -5,7 +5,7 @@
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved.
# Copyright (c) 1999 - 2021 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
@ -262,7 +262,11 @@ def moduleimpl_status(context, moduleimpl_id=None, partition_id=None, REQUEST=No
H.append(
"""Afficher les groupes de&nbsp;<select name="partition_id" onchange="document.f.submit();">"""
)
been_selected = False
for partition in partitions:
if not partition_id and not been_selected:
selected = "selected"
been_selected = True
if partition["partition_id"] == partition_id:
selected = "selected"
else:
@ -337,12 +341,11 @@ def moduleimpl_status(context, moduleimpl_id=None, partition_id=None, REQUEST=No
% etat["last_modif"].strftime("%d/%m/%Y à %Hh%M")
)
H.append('<span class="evalindex_cont">')
if has_expression:
if has_expression or True:
H.append(
"""<span class="evalindex" title="Indice dans les vecteurs (formules)">%02d</span>"""
"""<span class="evalindex" title="Indice dans les vecteurs (formules)">%2d</span>"""
% eval_index
)
# Fleches:
H.append('<span class="eval_arrows_chld">')
if eval_index != (len(ModEvals) - 1) and caneditevals:

View File

@ -5,7 +5,7 @@
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved.
# Copyright (c) 1999 - 2021 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
@ -37,7 +37,7 @@ from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.header import Header
from notesdb import *
from notesdb import * # pylint: disable=unused-wildcard-import
from notes_log import log
import scolars
from sco_utils import SCO_ENCODING, SCO_ANNONCES_WEBSITE
@ -127,7 +127,6 @@ def scolar_news_summary(context, n=5):
news = news[:n]
# mimic EditableTable.list output formatting:
for n in news:
d = n["date"]
n["date822"] = n["date"].strftime("%a, %d %b %Y %H:%M:%S %z")
# heure
n["hm"] = n["date"].strftime("%Hh%M")
@ -194,7 +193,7 @@ def scolar_news_summary_html(context, n=5, rssicon=None):
if not news:
return ""
H = ['<div class="news"><span class="newstitle">Dernières opérations']
if rssicon:
if rssicon: # 2020-12-30 plus utilisé
H.append('<a href="rssnews">' + rssicon + "</a>")
H.append('</span><ul class="newslist">')
@ -262,8 +261,10 @@ def _send_news_by_mail(context, n):
#
txt = n["text"]
if infos:
txt += "\n\nSemestre %(titremois)s\n\n" % infos["sem"]
txt += (
'\n\nSemestre <a href="Notes/formsemestre_status?formsemestre_id=%(formsemestre_id)s">%(descr_sem)s</a>)'
"""<a href="Notes/formsemestre_status?formsemestre_id=%(formsemestre_id)s">%(descr_sem)s</a>
"""
% infos
)
txt += "\n\nEffectué par: %(nomcomplet)s\n" % n["user_info"]

View File

@ -5,7 +5,7 @@
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved.
# Copyright (c) 1999 - 2021 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
@ -273,18 +273,17 @@ def ficheEtud(context, etudid=None, REQUEST=None):
if not context.canSuppressAnnotation(a["id"], REQUEST):
a["dellink"] = ""
else:
a["dellink"] = (
'<td class="annodel"><a href="doSuppressAnnotation?etudid=%s&amp;annotation_id=%s">%s</a></td>'
% (
etudid,
a["id"],
icontag(
"delete_img",
border="0",
alt="suppress",
title="Supprimer cette annotation",
),
)
a[
"dellink"
] = '<td class="annodel"><a href="doSuppressAnnotation?etudid=%s&amp;annotation_id=%s">%s</a></td>' % (
etudid,
a["id"],
icontag(
"delete_img",
border="0",
alt="suppress",
title="Supprimer cette annotation",
),
)
alist.append(
'<tr><td><span class="annodate">Le %(date)s par %(zope_authenticated_user)s : </span><span class="annoc">%(comment)s</span></td>%(dellink)s</tr>'
@ -486,8 +485,7 @@ def ficheEtud(context, etudid=None, REQUEST=None):
def menus_etud(context, REQUEST=None):
"""Menu etudiant (operations sur l'etudiant)
"""
"""Menu etudiant (operations sur l'etudiant)"""
if not REQUEST.form.has_key("etudid"):
return ""
authuser = REQUEST.AUTHENTICATED_USER

View File

@ -5,7 +5,7 @@
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved.
# Copyright (c) 1999 - 2021 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

View File

@ -5,7 +5,7 @@
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved.
# Copyright (c) 1999 - 2021 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
@ -41,7 +41,7 @@ from reportlab.platypus import Table, TableStyle, Image, KeepInFrame
from reportlab.platypus.flowables import Flowable
from reportlab.platypus.doctemplate import PageTemplate, BaseDocTemplate
from reportlab.lib.styles import getSampleStyleSheet
from reportlab.rl_config import defaultPageSize
from reportlab.rl_config import defaultPageSize # pylint: disable=no-name-in-module
from reportlab.lib.units import inch, cm, mm
from reportlab.lib.colors import pink, black, red, blue, green, magenta, red
from reportlab.lib.colors import Color

View File

@ -5,7 +5,7 @@
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved.
# Copyright (c) 1999 - 2021 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

View File

@ -5,7 +5,7 @@
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved.
# Copyright (c) 1999 - 2021 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
@ -197,16 +197,18 @@ def do_placement_selectetuds(context, REQUEST):
numbering = tf[2]["numbering"]
if columns in ("3", "4", "5", "6", "7", "8"):
gs = [("group_ids%3Alist=" + urllib.quote_plus(x)) for x in group_ids]
query = "evaluation_id=%s&amp;placement_method=%s&amp;teachers=%s&amp;building=%s&amp;room=%s&amp;columns=%s&amp;numbering=%s&amp;" % (
evaluation_id,
placement_method,
teachers,
building,
room,
columns,
numbering,
) + "&amp;".join(
gs
query = (
"evaluation_id=%s&amp;placement_method=%s&amp;teachers=%s&amp;building=%s&amp;room=%s&amp;columns=%s&amp;numbering=%s&amp;"
% (
evaluation_id,
placement_method,
teachers,
building,
room,
columns,
numbering,
)
+ "&amp;".join(gs)
)
return REQUEST.RESPONSE.redirect(REQUEST.URL1 + "/do_placement?" + query)
else:
@ -385,8 +387,7 @@ def do_placement(context, REQUEST):
def placement_eval_selectetuds(context, evaluation_id, REQUEST=None):
"""Dialogue placement etudiants: choix methode et localisation
"""
"""Dialogue placement etudiants: choix methode et localisation"""
evals = context.do_evaluation_list({"evaluation_id": evaluation_id})
if not evals:
raise ScoValueError("invalid evaluation_id")

Some files were not shown because too many files have changed in this diff Show More