Calendrier évaluations: fix #875

This commit is contained in:
Emmanuel Viennet 2024-04-15 17:53:02 +02:00
parent f2ce16f161
commit ad7b48e110
4 changed files with 102 additions and 213 deletions

View File

@ -230,41 +230,41 @@ def next_iso_day(date):
def YearTable( def YearTable(
year, year,
events=[], events_by_day: dict[str, list[dict]],
firstmonth=9, firstmonth=9,
lastmonth=7, lastmonth=7,
halfday=0,
dayattributes="", dayattributes="",
pad_width=8,
): ):
# Code simplifié en 2024: utilisé seulement pour calendrier évaluations
"""Generate a calendar table """Generate a calendar table
events = list of tuples (date, text, color, href [,halfday]) events = list of tuples (date, text, color, href [,halfday])
where date is a string in ISO format (yyyy-mm-dd) where date is a string in ISO format (yyyy-mm-dd)
halfday is boolean (true: morning, false: afternoon) halfday is boolean (true: morning, false: afternoon)
text = text to put in calendar (must be short, 1-5 cars) (optional) text = text to put in calendar (must be short, 1-5 cars) (optional)
if halfday, generate 2 cells per day (morning, afternoon)
""" """
T = [ T = [
'<table id="maincalendar" class="maincalendar" border="3" cellpadding="1" cellspacing="1" frame="box">' """<table id="maincalendar" class="maincalendar"
border="3" cellpadding="1" cellspacing="1" frame="box">"""
] ]
T.append("<tr>") T.append("<tr>")
month = firstmonth month = firstmonth
while 1: while True:
T.append('<td valign="top">') T.append('<td valign="top">')
T.append(MonthTableHead(month)) T.append(_month_table_head(month))
T.append( T.append(
MonthTableBody( _month_table_body(
month, month,
year, year,
events, events_by_day,
halfday,
dayattributes, dayattributes,
is_work_saturday(), is_work_saturday(),
pad_width=pad_width,
) )
) )
T.append(MonthTableTail()) T.append(
T.append("</td>") """
</table>
</td>"""
)
if month == lastmonth: if month == lastmonth:
break break
month = month + 1 month = month + 1
@ -322,29 +322,32 @@ WEEKDAYCOLOR = GRAY1
WEEKENDCOLOR = GREEN3 WEEKENDCOLOR = GREEN3
def MonthTableHead(month): def _month_table_head(month):
color = WHITE color = WHITE
return """<table class="monthcalendar" border="0" cellpadding="0" cellspacing="0" frame="box"> return f"""<table class="monthcalendar" border="0" cellpadding="0" cellspacing="0" frame="box">
<tr bgcolor="%s"><td class="calcol" colspan="2" align="center">%s</td></tr>\n""" % ( <tr bgcolor="{color}">
color, <td class="calcol" colspan="2" align="center">{MONTHNAMES_ABREV[month - 1]}</td>
MONTHNAMES_ABREV[month - 1], </tr>\n"""
)
def MonthTableTail(): def _month_table_body(
return "</table>\n" month,
year,
events_by_day: dict[str, list[dict]],
def MonthTableBody( trattributes="",
month, year, events=[], halfday=0, trattributes="", work_saturday=False, pad_width=8 work_saturday=False,
): ) -> str:
"""
events : [event]
event = [ yyyy-mm-dd, legend, href, color, descr ] XXX
"""
firstday, nbdays = calendar.monthrange(year, month) firstday, nbdays = calendar.monthrange(year, month)
localtime = time.localtime() localtime = time.localtime()
current_weeknum = time.strftime("%U", localtime) current_weeknum = time.strftime("%U", localtime)
current_year = localtime[0] current_year = localtime[0]
T = [] rows = []
# cherche date du lundi de la 1ere semaine de ce mois # cherche date du lundi de la 1ere semaine de ce mois
monday = ddmmyyyy("1/%d/%d" % (month, year)) monday = ddmmyyyy(f"1/{month}/{year}")
while monday.weekday != 0: while monday.weekday != 0:
monday = monday.prev() monday = monday.prev()
@ -353,7 +356,6 @@ def MonthTableBody(
else: else:
weekend = ("S", "D") weekend = ("S", "D")
if not halfday:
for d in range(1, nbdays + 1): for d in range(1, nbdays + 1):
weeknum = time.strftime( weeknum = time.strftime(
"%U", time.strptime("%d/%d/%d" % (d, month, year), scu.DATE_FMT) "%U", time.strptime("%d/%d/%d" % (d, month, year), scu.DATE_FMT)
@ -367,144 +369,38 @@ def MonthTableBody(
bgcolor = WEEKDAYCOLOR bgcolor = WEEKDAYCOLOR
weekclass = "wk" + str(monday).replace("/", "_") weekclass = "wk" + str(monday).replace("/", "_")
attrs = trattributes attrs = trattributes
# events this day ?
events = events_by_day.get(f"{year}-{month:02}-{d:02}", [])
color = None color = None
legend = "" ev_txts = []
href = ""
descr = ""
# event this day ?
# each event is a tuple (date, text, color, href)
# where date is a string in ISO format (yyyy-mm-dd)
for ev in events: for ev in events:
ev_year = int(ev[0][:4]) color = ev.get("color")
ev_month = int(ev[0][5:7]) href = ev.get("href", "")
ev_day = int(ev[0][8:10]) description = ev.get("description", "")
if year == ev_year and month == ev_month and ev_day == d: if href:
if ev[1]: href = f'href="{href}"'
legend = ev[1] if description:
if ev[2]: description = f"""title="{html.escape(description, quote=True)}" """
color = ev[2] if href or description:
if ev[3]: ev_txts.append(f"""<a {href} {description}>{ev.get("title", "")}</a>""")
href = ev[3] else:
if len(ev) > 4 and ev[4]: ev_txts.append(ev.get("title", "&nbsp;"))
descr = ev[4]
# #
cc = [] cc = []
if color is not None: if color is not None:
cc.append('<td bgcolor="%s" class="calcell">' % color) cc.append(f'<td bgcolor="{color}" class="calcell">')
else: else:
cc.append('<td class="calcell">') cc.append('<td class="calcell">')
if href: cc.append(f"{', '.join(ev_txts)}</td>")
href = 'href="%s"' % href cells = "".join(cc)
if descr:
descr = 'title="%s"' % html.escape(descr, quote=True)
if href or descr:
cc.append("<a %s %s>" % (href, descr))
if legend or d == 1:
if pad_width is not None:
n = pad_width - len(legend) # pad to 8 cars
if n > 0:
legend = (
"&nbsp;" * (n // 2) + legend + "&nbsp;" * ((n + 1) // 2)
)
else:
legend = "&nbsp;" # empty cell
cc.append(legend)
if href or descr:
cc.append("</a>")
cc.append("</td>")
cell = "".join(cc)
if day == "D": if day == "D":
monday = monday.next_day(7) monday = monday.next_day(7)
if ( if weeknum == current_weeknum and current_year == year and weekclass != "wkend":
weeknum == current_weeknum
and current_year == year
and weekclass != "wkend"
):
weekclass += " currentweek" weekclass += " currentweek"
T.append( rows.append(
'<tr bgcolor="%s" class="%s" %s><td class="calday">%d%s</td>%s</tr>' f"""<tr bgcolor="{bgcolor}" class="{weekclass}" {attrs}>
% (bgcolor, weekclass, attrs, d, day, cell) <td class="calday">{d}{day}</td>{cells}</tr>"""
) )
else:
# Calendar with 2 cells / day
for d in range(1, nbdays + 1):
weeknum = time.strftime(
"%U", time.strptime("%d/%d/%d" % (d, month, year), scu.DATE_FMT)
)
day = DAYNAMES_ABREV[(firstday + d - 1) % 7]
if day in weekend:
bgcolor = WEEKENDCOLOR
weekclass = "wkend"
attrs = ""
else:
bgcolor = WEEKDAYCOLOR
weekclass = "wk" + str(monday).replace("/", "_")
attrs = trattributes
if (
weeknum == current_weeknum
and current_year == year
and weekclass != "wkend"
):
weeknum += " currentweek"
if day == "D": return "\n".join(rows)
monday = monday.next_day(7)
T.append(
'<tr bgcolor="%s" class="wk%s" %s><td class="calday">%d%s</td>'
% (bgcolor, weekclass, attrs, d, day)
)
cc = []
for morning in (True, False):
color = None
legend = ""
href = ""
descr = ""
for ev in events:
ev_year = int(ev[0][:4])
ev_month = int(ev[0][5:7])
ev_day = int(ev[0][8:10])
if ev[4] is not None:
ev_half = int(ev[4])
else:
ev_half = 0
if (
year == ev_year
and month == ev_month
and ev_day == d
and morning == ev_half
):
if ev[1]:
legend = ev[1]
if ev[2]:
color = ev[2]
if ev[3]:
href = ev[3]
if len(ev) > 5 and ev[5]:
descr = ev[5]
#
if color is not None:
cc.append('<td bgcolor="%s" class="calcell">' % (color))
else:
cc.append('<td class="calcell">')
if href:
href = 'href="%s"' % href
if descr:
descr = 'title="%s"' % html.escape(descr, quote=True)
if href or descr:
cc.append("<a %s %s>" % (href, descr))
if legend or d == 1:
n = 3 - len(legend) # pad to 3 cars
if n > 0:
legend = (
"&nbsp;" * (n // 2) + legend + "&nbsp;" * ((n + 1) // 2)
)
else:
legend = "&nbsp;&nbsp;&nbsp;" # empty cell
cc.append(legend)
if href or descr:
cc.append("</a>")
cc.append("</td>\n")
T.append("".join(cc) + "</tr>")
return "\n".join(T)

View File

@ -360,6 +360,7 @@ def do_evaluation_etat_in_mod(nt, modimpl: ModuleImpl):
return etat return etat
# View
def formsemestre_evaluations_cal(formsemestre_id): def formsemestre_evaluations_cal(formsemestre_id):
"""Page avec calendrier de toutes les evaluations de ce semestre""" """Page avec calendrier de toutes les evaluations de ce semestre"""
formsemestre = FormSemestre.get_formsemestre(formsemestre_id) formsemestre = FormSemestre.get_formsemestre(formsemestre_id)
@ -373,22 +374,17 @@ def formsemestre_evaluations_cal(formsemestre_id):
color_futur = "#70E0FF" color_futur = "#70E0FF"
year = formsemestre.annee_scolaire() year = formsemestre.annee_scolaire()
events = {} # (day, halfday) : event events_by_day = collections.defaultdict(list) # date_iso : event
for e in evaluations: for e in evaluations:
if e.date_debut is None: if e.date_debut is None:
continue # éval. sans date continue # éval. sans date
txt = e.moduleimpl.module.code or e.moduleimpl.module.abbrev or "éval."
if e.date_debut == e.date_fin: if e.date_debut == e.date_fin:
heure_debut_txt, heure_fin_txt = "?", "?" heure_debut_txt, heure_fin_txt = "", ""
else: else:
heure_debut_txt = ( heure_debut_txt = (
e.date_debut.strftime(scu.TIME_FMT) if e.date_debut else "?" e.date_debut.strftime(scu.TIME_FMT) if e.date_debut else ""
) )
heure_fin_txt = e.date_fin.strftime(scu.TIME_FMT) if e.date_fin else "?" heure_fin_txt = e.date_fin.strftime(scu.TIME_FMT) if e.date_fin else ""
description = f"""{
e.moduleimpl.module.titre
}, de {heure_debut_txt} à {heure_fin_txt}"""
# Etat (notes completes) de l'évaluation: # Etat (notes completes) de l'évaluation:
modimpl_result = nt.modimpls_results[e.moduleimpl.id] modimpl_result = nt.modimpls_results[e.moduleimpl.id]
@ -398,28 +394,27 @@ def formsemestre_evaluations_cal(formsemestre_id):
color = color_incomplete color = color_incomplete
if e.date_debut > datetime.datetime.now(scu.TIME_ZONE): if e.date_debut > datetime.datetime.now(scu.TIME_ZONE):
color = color_futur color = color_futur
href = url_for( day = e.date_debut.date().isoformat() # yyyy-mm-dd
event = {
"color": color,
"date_iso": day,
"title": e.moduleimpl.module.code or e.moduleimpl.module.abbrev or "éval.",
"description": f"""{e.description or e.moduleimpl.module.titre_str()}"""
+ (
f""" de {heure_debut_txt} à {heure_fin_txt}"""
if heure_debut_txt
else ""
),
"href": url_for(
"notes.moduleimpl_status", "notes.moduleimpl_status",
scodoc_dept=g.scodoc_dept, scodoc_dept=g.scodoc_dept,
moduleimpl_id=e.moduleimpl_id, moduleimpl_id=e.moduleimpl_id,
) ),
day = e.date_debut.date().isoformat() # yyyy-mm-dd "modimpl": e.moduleimpl,
event = events.get(day) }
if not event: events_by_day[day].append(event)
events[day] = [day, txt, color, href, description, e.moduleimpl]
else:
if event[-1].id != e.moduleimpl.id:
# plusieurs evals de modules differents a la meme date
event[1] += ", " + txt
event[4] += ", " + description
if color == color_incomplete:
event[2] = color_incomplete
if color == color_futur:
event[2] = color_futur
cal_html = sco_cal.YearTable( cal_html = sco_cal.YearTable(year, events_by_day=events_by_day)
year, events=list(events.values()), halfday=False, pad_width=None
)
return f""" return f"""
{ {

View File

@ -51,8 +51,6 @@ Calendrier de l'assiduité
<div class="dayline"> <div class="dayline">
<div class="dayline-title"> <div class="dayline-title">
<span>Assiduité du</span>
<br>
<span>{{jour.get_date()}}</span> <span>{{jour.get_date()}}</span>
{{jour.generate_minitimeline() | safe}} {{jour.generate_minitimeline() | safe}}
</div> </div>
@ -158,7 +156,7 @@ Calendrier de l'assiduité
.calendrier { .calendrier {
display: flex; display: flex;
justify-content: start; justify-content: center;
overflow-x: scroll; overflow-x: scroll;
border: 1px solid #444; border: 1px solid #444;
border-radius: 12px; border-radius: 12px;

View File

@ -1,7 +1,7 @@
# -*- mode: python -*- # -*- mode: python -*-
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
SCOVERSION = "9.6.962" SCOVERSION = "9.6.963"
SCONAME = "ScoDoc" SCONAME = "ScoDoc"