Compare commits

...

28 Commits

Author SHA1 Message Date
b2e5fccec1 Ajouter 'toto' 2020-12-30 00:49:51 +01:00
1f4a32a485 Calcul formule dans modules sans note (pour malus) [contrib. JMP] 2020-12-29 23:16:40 +01:00
97f69ebc7d Bulletins: ajuste l affichage de la ligne module pour les modules malus (contrib. JMP) 2020-12-29 18:53:03 +01:00
d6bf8efdee TEST: set default sem resp. 2020-12-29 17:32:26 +01:00
8bf2255d82 typo 2020-12-28 23:47:36 +01:00
4847a2d12b black 2020-12-28 22:59:59 +01:00
978d4e64af ajout bouton "saisie à la semaine" dans le tab "Absences et feuilles" 2020-12-28 22:59:02 +01:00
7ad2a10894 shellcheck 2020-12-28 22:09:20 +01:00
dea24d0257 TESTS: scenario standard capitalisation 2020-12-28 21:49:53 +01:00
e7ccdee992 TESTS: scenario standard capitalisation 2020-12-28 21:42:22 +01:00
62a813494c Fix: bulletins 2020-12-28 14:43:12 +01:00
274d39a3ec Test basic: modules, sems, notes 2020-12-27 13:45:24 +01:00
0864917a5f Fix: moduleimpl_status ordre des partitions 2020-12-26 00:39:45 +01:00
ea388ea494 New testing tools 2020-12-26 00:11:55 +01:00
6af4c6a2fd Fix: Affichage moyenne promo par évaluation sur bulletin 2020-12-25 12:10:37 +01:00
2b5a470516 itre form saisie + pylint 2020-12-24 14:56:01 +01:00
7efe8cd194 Fix: partition par defaut sur tableau bord module 2020-12-24 11:47:13 +01:00
de6d0233ac Lien mail enseignants sur tableau bord semestre 2020-12-24 01:29:50 +01:00
fcbbc877bc Liste enseignants dans table description semestre (DS, Le Havre) 2020-12-24 01:00:41 +01:00
2f94c41821 documente debouche comme obsolete 2020-12-24 00:19:35 +01:00
d386f82146 pylint 2020-12-24 00:07:17 +01:00
ec006442c4 pylint 2020-12-23 23:49:11 +01:00
cf091d2c1a Fix: removed debug message raising an exception 2020-12-23 23:48:11 +01:00
d6e7bbc713 Merge branch 'master' of https://scodoc.org/git/viennet/ScoDoc 2020-12-23 22:28:26 +01:00
7794fff4a5 Fix: export excel des evaluations en attente 2020-12-23 22:27:21 +01:00
65575e3562 Merge branch 'master' of https://scodoc.org/git/viennet/ScoDoc 2020-12-23 17:22:14 +01:00
3b75dcfa13 Fix mode 2020-12-23 17:21:59 +01:00
9b5f766500 Fix: export Excel n'affiche pas certains DS 2020-12-23 17:19:22 +01:00
155 changed files with 1196 additions and 367 deletions

View File

@ -45,17 +45,19 @@ L'API de plus bas niveau est en gros:
"""
import urllib
import datetime
import jaxml
# ---------------
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
@ -356,7 +358,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 +382,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 +404,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 +425,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 +452,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 +489,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 +536,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 +567,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 +590,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 +600,6 @@ class ZAbsences(
vars(),
)
Abs = cursor.dictfetchall()
# log('ListeAbsDate: abs=%s' % Abs)
# remove duplicates
A = {} # { (jour, matin) : abs }
for a in Abs:
@ -625,7 +626,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 +633,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 +654,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 +680,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 +704,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 +732,6 @@ class ZAbsences(
if desc:
return "(%s) %s" % (desc, module)
return desc
if module:
return module
return ""
@ -745,7 +744,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 +768,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 +848,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 +885,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 +932,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
@ -963,10 +961,10 @@ class ZAbsences(
)[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"
@ -1044,9 +1042,7 @@ class ZAbsences(
% {"menu_module": menu_module, "url": base_url, "sel": sel}
)
H += self._gen_form_saisie_groupe(
etuds, self.day_names(), datessem, destination, None, moduleimpl_id
)
H += self._gen_form_saisie_groupe(etuds, datessem, destination, moduleimpl_id)
H.append(self.sco_footer(REQUEST))
return "\n".join(H)
@ -1063,8 +1059,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
@ -1083,16 +1078,13 @@ class ZAbsences(
]
if not moduleimpl_id:
moduleimp_id = None
base_url_noweeks = (
"SignaleAbsenceGrSemestre?datedebut=%s&datefin=%s&%s&destination=%s"
% (
moduleimpl_id = None
base_url_noweeks = "SignaleAbsenceGrSemestre?datedebut=%s&datefin=%s&%s&destination=%s" % (
datedebut,
datefin,
groups_infos.groups_query_args,
urllib.quote(destination),
)
)
base_url = (
base_url_noweeks + "&nbweeks=%s" % nbweeks
) # sans le moduleimpl_id
@ -1139,7 +1131,6 @@ class ZAbsences(
if moduleimpl_id:
url_link_semaines += "&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 +1138,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>"
)
@ -1217,15 +1208,18 @@ class ZAbsences(
% {"menu_module": menu_module, "url": base_url, "sel": sel}
)
H += self._gen_form_saisie_groupe(
etuds, colnames, dates, destination, dayname, moduleimpl_id
)
H += self._gen_form_saisie_groupe(etuds, dates, destination, moduleimpl_id)
H.append(self.sco_footer(REQUEST))
return "\n".join(H)
def _gen_form_saisie_groupe(
self, etuds, colnames, dates, destination="", dayname="", moduleimpl_id=None
):
def _gen_form_saisie_groupe(self, etuds, dates, destination="", moduleimpl_id=None):
"""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">
@ -1252,25 +1246,29 @@ class ZAbsences(
"""
% 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:
@ -1373,14 +1371,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.*
@ -1499,10 +1496,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 +1599,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 +1628,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 +1728,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 +1753,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)
@ -1884,11 +1874,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 +1888,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 +1925,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:
@ -2107,8 +2094,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 +2113,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 +2197,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)
@ -2443,8 +2429,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

@ -1617,7 +1617,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

@ -284,6 +284,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)
@ -291,10 +292,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

@ -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

@ -13,27 +13,45 @@
source config.sh
source utils.sh
check_uid_root $0
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 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
if [ -e $cfg_pathname ]
if [ -e "$cfg_pathname" ]
then
echo 'Erreur: Il existe deja une configuration pour "'$DEPT'"'
echo 'Erreur: Il existe deja une configuration pour "'"$DEPT"'"'
exit 1
fi
@ -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 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

@ -18,46 +18,64 @@ 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 DEPT
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
if [ -e $cfg_pathname ]
if [ -e "$cfg_pathname" ]
then
# arret de ScoDoc
scodocctl stop
# suppression de la base postgres
db_name=$(cat $cfg_pathname | sed '/^dbname=*/!d; s///;q')
db_name=$(cat "$cfg_pathname" | sed '/^dbname=*/!d; s///;q')
echo "suppression de la base postgres $db_name"
su -c "dropdb $db_name" $POSTGRES_SUPERUSER || terminate "ne peux supprimer base de donnees $db_name"
su -c "dropdb $db_name" "$POSTGRES_SUPERUSER" || terminate "ne peux supprimer base de donnees $db_name"
# suppression du fichier de config
/bin/rm -f $cfg_pathname || terminate "ne peux supprimer $cfg_pathname"
/bin/rm -f "$cfg_pathname" || terminate "ne peux supprimer $cfg_pathname"
# relance ScoDoc
if [ "$interactive" = 1 ]
then
echo -n "Demarrer le serveur ScoDoc ? (y/n) [n]"
read ans
read -r ans
if [ "$(norm_ans "$ans")" = 'Y' ]
then
scodocctl start
fi
fi
exit 0
else
echo 'Erreur: pas de configuration trouvee pour "'$DEPT'"'
echo 'Erreur: pas de configuration trouvee pour "'"$DEPT"'"'
exit 1
fi

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

View File

@ -15,7 +15,7 @@ SCODOC_DIR="${INSTANCE_DIR}/Products/ScoDoc"
SCODOC_VAR_DIR="${INSTANCE_DIR}/var/scodoc"
source utils.sh
check_uid_root $0
check_uid_root "$0"
# Safety check
echo "Ce script va remplacer les donnees de votre installation ScoDoc par celles"
@ -28,7 +28,7 @@ echo
echo "TOUTES LES BASES POSTGRESQL SERONT EFFACEES !!!"
echo
echo -n "Voulez vous poursuivre cette operation ? (y/n) [n]"
read ans
read -r ans
if [ ! "$(norm_ans "$ans")" = 'Y' ]
then
echo "Annulation"
@ -57,7 +57,7 @@ then
echo "Opening tgz archive..."
tmp=$(mktemp -d)
chmod a+rx "$tmp"
cd "$tmp"
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
cd ${SCODOC_DIR:?}/config || terminate "no config directory"
./upgrade.sh
#

View File

@ -35,7 +35,7 @@ INSTANCE_DIR=/opt/scodoc
SCODOC_DIR="$INSTANCE_DIR/Products/ScoDoc"
source utils.sh
check_uid_root $0
check_uid_root "$0"
echo "Stopping ScoDoc..."
scodocctl stop
@ -44,9 +44,9 @@ 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
echo "Error dumping postgresql database\nPlease check that SQL server is running\nAborting."
printf "Error dumping postgresql database\nPlease check that SQL server is running\nAborting."
exit 1
fi
chown root "$DEST"
@ -57,7 +57,7 @@ cp -rp "$INSTANCE_DIR/var" "$DEST"
# Depts db config (now in .../var)
shopt -s nullglob
if [ ! -z "$(echo ${SCODOC_DIR}/config/depts/*.cfg)" ]
if [ -n "$(echo ${SCODOC_DIR}/config/depts/*.cfg)" ]
then
echo "Copying legacy depts configs..."
cp -rp "$SCODOC_DIR/config/depts" "$DEST"
@ -86,6 +86,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

@ -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

@ -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

@ -34,7 +34,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

@ -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):
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

@ -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,8 +123,7 @@ 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"]
@ -162,7 +160,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>""",
"""
@ -207,8 +208,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 +274,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 +289,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>""",
"""
@ -329,8 +331,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 +379,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 +395,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,8 +467,7 @@ 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)
@ -569,8 +571,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 +622,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

@ -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'

View File

@ -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,10 +495,10 @@ 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"]
@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -764,12 +764,17 @@ def _list_notes_evals(context, evals, etudid):
"""
L = []
for e in evals:
if e["etat"]["evalcomplete"]:
if (
e["etat"]["evalcomplete"]
or e["etat"]["evalattente"]
or e["publish_incomplete"]
):
NotesDB = context._notes_getall(e["evaluation_id"])
if NotesDB.has_key(etudid):
val = NotesDB[etudid]["value"]
else:
val = None
# Note manquante mais prise en compte immédiate: affiche ATT
val = NOTES_ATTENTE
val_fmt = fmt_note(val, keep_numeric=True)
L.append(val_fmt)
return L
@ -778,9 +783,15 @@ def _list_notes_evals(context, evals, etudid):
def _list_notes_evals_titles(context, codemodule, evals):
"""Liste des titres des evals completes"""
L = []
eval_index = len(evals) - 1
for e in evals:
if e["etat"]["evalcomplete"]:
L.append(codemodule + "-" + DateISOtoDMY(e["jour"]))
if (
e["etat"]["evalcomplete"]
or e["etat"]["evalattente"]
or e["publish_incomplete"]
):
L.append(codemodule + "-" + str(eval_index) + "-" + e["jour"].isoformat())
eval_index -= 1
return L
@ -788,7 +799,11 @@ def _list_notes_evals_stats(context, evals, key):
"""Liste des stats (moy, ou rien!) des evals completes"""
L = []
for e in evals:
if e["etat"]["evalcomplete"]:
if (
e["etat"]["evalcomplete"]
or e["etat"]["evalattente"]
or e["publish_incomplete"]
):
if key == "moy":
val = e["etat"]["moy_num"]
L.append(fmt_note(val, keep_numeric=True))

View File

@ -28,6 +28,8 @@
"""Synchronisation des listes d'étudiants avec liste portail (Apogée)
"""
import time
import pprint
from sco_utils import ScoEtudInscrit, annee_scolaire_debut, log, ScoValueError
from notesdb import ScoDocCursor
@ -42,8 +44,6 @@ import sco_formsemestre_inscriptions
import sco_formsemestre_status
from sco_news import NEWS_INSCR, NEWS_NOTE, NEWS_FORM, NEWS_SEM, NEWS_MISC
import time
# Clés utilisées pour la synchro
EKEY_APO = "nip"
EKEY_SCO = "code_nip"
@ -695,7 +695,7 @@ def do_import_etud_admission(
"codelycee": get_opt_str(etud, "lycee"),
"boursier": get_opt_str(etud, "bourse"),
}
log("do_import_etud_admission: etud=%s" % etud)
log("do_import_etud_admission: etud=%s" % pprint.pformat(etud))
al = scolars.admission_list(cnx, args={"etudid": etudid})
if not al:
scolars.admission_create(cnx, args) # -> adm_id

View File

@ -25,7 +25,7 @@
#
##############################################################################
""" Acces donnees etudiants
""" Accès donnees etudiants
"""
from sco_utils import *
@ -35,7 +35,7 @@ from TrivialFormulator import TrivialFormulator
import safehtml
from scolog import logdb
from notes_table import *
import sco_news
# XXXXXXXXX HACK: zope 2.7.7 bug turaround ?
import locale
@ -618,6 +618,59 @@ def make_etud_args(etudid=None, code_nip=None, REQUEST=None, raise_exc=True):
return args
def create_etud(context, cnx, args={}, REQUEST=None):
"""Creation d'un étudiant. génère aussi évenement et "news".
Args:
args: dict avec les attributs de l'étudiant
Returns:
etud, l'étudiant créé.
"""
# creation d'un etudiant
etudid = etudident_create(cnx, args, context=context, REQUEST=REQUEST)
# crée une adresse vide (chaque etudiant doit etre dans la table "adresse" !)
_ = adresse_create(
cnx,
{
"etudid": etudid,
"typeadresse": "domicile",
"description": "(creation individuelle)",
},
)
# event
scolar_events_create(
cnx,
args={
"etudid": etudid,
"event_date": time.strftime("%d/%m/%Y"),
"formsemestre_id": None,
"event_type": "CREATION",
},
)
# log
logdb(
REQUEST,
cnx,
method="etudident_edit_form",
etudid=etudid,
msg="creation initiale",
)
etud = scolars.etudident_list(cnx, {"etudid": etudid})[0]
context.fillEtudsInfo([etud])
etud["url"] = "ficheEtud?etudid=%(etudid)s" % etud
sco_news.add(
context,
REQUEST,
typ=sco_news.NEWS_INSCR,
object=None, # pas d'object pour ne montrer qu'un etudiant
text='Nouvel étudiant <a href="%(url)s">%(nomprenom)s</a>' % etud,
url=etud["url"],
)
return etud
# ---------- "EVENTS"
_scolar_eventsEditor = EditableTable(
"scolar_events",

1
scotests/__init__.py Normal file
View File

@ -0,0 +1 @@
#

View File

Before

Width:  |  Height:  |  Size: 9.8 KiB

After

Width:  |  Height:  |  Size: 9.8 KiB

View File

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

View File

Before

Width:  |  Height:  |  Size: 8.2 KiB

After

Width:  |  Height:  |  Size: 8.2 KiB

View File

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

View File

Before

Width:  |  Height:  |  Size: 8.4 KiB

After

Width:  |  Height:  |  Size: 8.4 KiB

View File

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

Before

Width:  |  Height:  |  Size: 9.1 KiB

After

Width:  |  Height:  |  Size: 9.1 KiB

View File

Before

Width:  |  Height:  |  Size: 8.7 KiB

After

Width:  |  Height:  |  Size: 8.7 KiB

View File

Before

Width:  |  Height:  |  Size: 8.0 KiB

After

Width:  |  Height:  |  Size: 8.0 KiB

View File

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

View File

Before

Width:  |  Height:  |  Size: 8.5 KiB

After

Width:  |  Height:  |  Size: 8.5 KiB

View File

Before

Width:  |  Height:  |  Size: 8.4 KiB

After

Width:  |  Height:  |  Size: 8.4 KiB

View File

Before

Width:  |  Height:  |  Size: 9.8 KiB

After

Width:  |  Height:  |  Size: 9.8 KiB

View File

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

View File

Before

Width:  |  Height:  |  Size: 8.0 KiB

After

Width:  |  Height:  |  Size: 8.0 KiB

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

Before

Width:  |  Height:  |  Size: 9.9 KiB

After

Width:  |  Height:  |  Size: 9.9 KiB

View File

Before

Width:  |  Height:  |  Size: 8.9 KiB

After

Width:  |  Height:  |  Size: 8.9 KiB

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

Before

Width:  |  Height:  |  Size: 9.6 KiB

After

Width:  |  Height:  |  Size: 9.6 KiB

View File

Before

Width:  |  Height:  |  Size: 8.7 KiB

After

Width:  |  Height:  |  Size: 8.7 KiB

View File

Before

Width:  |  Height:  |  Size: 6.6 KiB

After

Width:  |  Height:  |  Size: 6.6 KiB

View File

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

Before

Width:  |  Height:  |  Size: 6.9 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

View File

Before

Width:  |  Height:  |  Size: 7.0 KiB

After

Width:  |  Height:  |  Size: 7.0 KiB

View File

Before

Width:  |  Height:  |  Size: 8.3 KiB

After

Width:  |  Height:  |  Size: 8.3 KiB

View File

Before

Width:  |  Height:  |  Size: 8.9 KiB

After

Width:  |  Height:  |  Size: 8.9 KiB

View File

Before

Width:  |  Height:  |  Size: 7.0 KiB

After

Width:  |  Height:  |  Size: 7.0 KiB

View File

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

Before

Width:  |  Height:  |  Size: 7.4 KiB

After

Width:  |  Height:  |  Size: 7.4 KiB

View File

Before

Width:  |  Height:  |  Size: 9.8 KiB

After

Width:  |  Height:  |  Size: 9.8 KiB

View File

Before

Width:  |  Height:  |  Size: 6.2 KiB

After

Width:  |  Height:  |  Size: 6.2 KiB

View File

Before

Width:  |  Height:  |  Size: 9.7 KiB

After

Width:  |  Height:  |  Size: 9.7 KiB

View File

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

View File

Before

Width:  |  Height:  |  Size: 8.6 KiB

After

Width:  |  Height:  |  Size: 8.6 KiB

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

Before

Width:  |  Height:  |  Size: 9.3 KiB

After

Width:  |  Height:  |  Size: 9.3 KiB

View File

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

View File

Before

Width:  |  Height:  |  Size: 8.8 KiB

After

Width:  |  Height:  |  Size: 8.8 KiB

View File

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

View File

Before

Width:  |  Height:  |  Size: 6.8 KiB

After

Width:  |  Height:  |  Size: 6.8 KiB

View File

Before

Width:  |  Height:  |  Size: 8.7 KiB

After

Width:  |  Height:  |  Size: 8.7 KiB

View File

Before

Width:  |  Height:  |  Size: 9.3 KiB

After

Width:  |  Height:  |  Size: 9.3 KiB

View File

Before

Width:  |  Height:  |  Size: 9.0 KiB

After

Width:  |  Height:  |  Size: 9.0 KiB

View File

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

View File

Before

Width:  |  Height:  |  Size: 7.5 KiB

After

Width:  |  Height:  |  Size: 7.5 KiB

View File

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

Before

Width:  |  Height:  |  Size: 9.3 KiB

After

Width:  |  Height:  |  Size: 9.3 KiB

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

View File

Before

Width:  |  Height:  |  Size: 8.2 KiB

After

Width:  |  Height:  |  Size: 8.2 KiB

View File

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

View File

Before

Width:  |  Height:  |  Size: 8.2 KiB

After

Width:  |  Height:  |  Size: 8.2 KiB

View File

Before

Width:  |  Height:  |  Size: 8.8 KiB

After

Width:  |  Height:  |  Size: 8.8 KiB

View File

Before

Width:  |  Height:  |  Size: 8.7 KiB

After

Width:  |  Height:  |  Size: 8.7 KiB

View File

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

Before

Width:  |  Height:  |  Size: 8.9 KiB

After

Width:  |  Height:  |  Size: 8.9 KiB

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