Skip to content

Commit c358e80

Browse files
authored
[scoreboard] Remove auto-generated scoreboard deadline labels and simplify their handling (#799)
1 parent 133ab0a commit c358e80

3 files changed

Lines changed: 55 additions & 193 deletions

File tree

scoreboard/README.md

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ Generates `output_directory/index.html` with the scoreboard.
1818

1919
- `data/points-info.yml` - Task points, deadlines, penalties
2020
- `data/plagiarism.yml` - Flagged submissions
21-
- `data/deadlines.yml` - Optional display deadlines and day offsets
21+
- `data/deadlines.yml` - Optional deadline display labels
2222

2323
## Testing
2424

@@ -36,22 +36,20 @@ HTML table with columns: S (solution), A (acceleration), E (efficiency), D (dead
3636

3737
### Deadlines display
3838

39-
- Threads deadlines are auto-distributed across the Spring window: 1 Feb → 15 May.
40-
- Processes deadlines are auto-distributed across the Autumn window: 15 Oct → 14 Dec.
39+
- Deadline labels are shown only when explicitly configured in `data/deadlines.yml`.
40+
- Missing entries are hidden.
4141
- Due time is 23:59 MSK on the shown date.
42-
- File `data/deadlines.yml` can shift dates per item by integer day offsets (default 0). Example:
42+
- Example:
4343

4444
```yaml
4545
threads:
46-
seq: 0 # no shift
47-
omp: -2 # 2 days earlier
48-
tbb: 3 # 3 days later
49-
stl: 0
50-
all: 0
46+
seq: "22 Feb"
47+
omp: "14 Mar"
48+
tbb: "4 Apr"
49+
stl: "24 Apr"
50+
all: "15 May"
5151
processes:
52-
task_1: 0
53-
task_2: 5
54-
task_3: -1
52+
task_1: "4 Nov"
53+
task_2: "24 Nov"
54+
task_3: "14 Dec"
5555
```
56-
57-
- If you put a non-integer string instead of a number, it is used as-is as the label (e.g., `"10 Nov"`).

scoreboard/data/deadlines.yml

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
11
threads:
2-
# Put integer to shift auto date by N days (negative allowed). Default 0.
3-
seq: 0
4-
omp: 0
5-
tbb: 0
6-
stl: 0
7-
all: 0
2+
# seq: "22 Feb"
3+
# omp: "14 Mar"
4+
# tbb: "4 Apr"
5+
# stl: "24 Apr"
6+
# all: "15 May"
87

98
processes:
10-
# Use integer offsets for tasks; default 0.
11-
task_1: 0
12-
task_2: 0
13-
task_3: 0
9+
# task_1: "4 Nov"
10+
# task_2: "24 Nov"
11+
# task_3: "14 Dec"

scoreboard/main.py

Lines changed: 35 additions & 169 deletions
Original file line numberDiff line numberDiff line change
@@ -736,7 +736,7 @@ def main():
736736

737737
env = Environment(loader=FileSystemLoader(Path(__file__).parent / "templates"))
738738

739-
# Load optional display deadlines from deadlines.yml and/or auto-compute evenly
739+
# Load optional display deadline labels from deadlines.yml.
740740
deadlines_display_threads: dict[str, str] | None = None
741741
deadlines_display_processes: dict[str, str] | None = None
742742
try:
@@ -749,80 +749,32 @@ def main():
749749
except Exception:
750750
pass
751751

752-
# Helper: compute evenly spaced dates for current semester (MSK)
753-
import calendar
754-
from datetime import date, timedelta
755-
756-
def _abbr(day: date) -> str:
757-
return f"{day.day} {calendar.month_abbr[day.month]}"
758-
759-
def _spring_bounds(today: date) -> tuple[date, date]:
760-
"""Return [1 Feb .. 15 May] window for the appropriate year.
761-
If today is past 15 May, use next year's spring; otherwise this year's.
762-
"""
763-
y = today.year
764-
start = date(y, 2, 1)
765-
end = date(y, 5, 15)
766-
if today > end:
767-
y += 1
768-
start = date(y, 2, 1)
769-
end = date(y, 5, 15)
770-
return start, end
771-
772-
def _autumn_bounds(today: date) -> tuple[date, date]:
773-
"""Return [15 Oct .. 14 Dec] window for the appropriate year.
774-
If today is past 14 Dec, use next year's autumn; otherwise this year's.
775-
"""
776-
y = today.year
777-
start = date(y, 10, 15)
778-
end = date(y, 12, 14)
779-
if today > end:
780-
y += 1
781-
start = date(y, 10, 15)
782-
end = date(y, 12, 14)
783-
return start, end
784-
785-
def _evenly_spaced_dates(n: int, start: date, end: date) -> list[date]:
786-
"""
787-
Return n deadlines evenly spaced across the window (start..end],
788-
i.e., strictly after the start date, with the last at end.
789-
Positions are at fractions (i+1)/n of the total span.
790-
"""
791-
if n <= 1:
792-
return [end]
793-
total = (end - start).days
794-
if total < 0:
795-
start, end = end, start
796-
total = -total
797-
res = []
798-
for i in range(n):
799-
off = int(round((i + 1) * total / n))
800-
if off <= 0:
801-
off = 1
802-
if off > total:
803-
off = total
804-
res.append(start + timedelta(days=off))
805-
return res
806-
807-
def _compute_display_deadlines_threads(order: list[str]) -> dict[str, date]:
808-
# Threads = Spring semester (prefer MSK; fallback to local time)
809-
try:
810-
today = _now_msk().date()
811-
except Exception:
812-
today = datetime.now().date()
813-
s, e = _spring_bounds(today)
814-
ds = _evenly_spaced_dates(len(order), s, e)
815-
return {t: d for t, d in zip(order, ds)}
816-
817-
def _compute_display_deadlines_processes(n_items: int) -> list[date]:
818-
# Processes = Autumn semester (prefer MSK; fallback to local time)
819-
try:
820-
today = _now_msk().date()
821-
except Exception:
822-
today = datetime.now().date()
823-
s, e = _autumn_bounds(today)
824-
ds = _evenly_spaced_dates(n_items, s, e)
825-
return ds
752+
def _deadline_label(value) -> str:
753+
if value is None or isinstance(value, (bool, int, float)):
754+
return ""
755+
return str(value)
756+
757+
def _thread_deadline_labels(order: list[str]) -> dict[str, str]:
758+
if not deadlines_display_threads:
759+
return {}
760+
labels = {}
761+
for task_type in order:
762+
label = _deadline_label(deadlines_display_threads.get(task_type))
763+
if label:
764+
labels[task_type] = label
765+
return labels
766+
767+
def _process_deadline_labels(task_numbers: list[int]) -> list[str]:
768+
if not deadlines_display_processes:
769+
return []
770+
labels = []
771+
for task_number in task_numbers:
772+
label = _deadline_label(
773+
deadlines_display_processes.get(f"task_{task_number}")
774+
or deadlines_display_processes.get(f"mpi_task_{task_number}")
775+
)
776+
labels.append(label)
777+
return labels if any(labels) else []
826778

827779
# Locate perf CSVs from CI or local runs (threads and processes)
828780
candidates_threads = [
@@ -1239,30 +1191,9 @@ def _build_process_rows(processes_dirs: list[str]):
12391191
generated_msk = _now_msk().strftime("%Y-%m-%d %H:%M:%S")
12401192
table_template = env.get_template("index.html.j2")
12411193
threads_vmax = int((cfg.get("threads", {}) or {}).get("variants_max", 1))
1242-
# Build display deadlines (use file values if present, fill missing with auto)
1194+
# Build display deadlines from explicit file values.
12431195
threads_order = task_types_threads
1244-
auto_threads_dl = _compute_display_deadlines_threads(threads_order)
1245-
dl_threads_out = {}
1246-
for t in threads_order:
1247-
base_date = auto_threads_dl.get(t)
1248-
# Default = 0 shift
1249-
shift_days = 0
1250-
label = None
1251-
if deadlines_display_threads and t in deadlines_display_threads:
1252-
val = deadlines_display_threads.get(t)
1253-
if isinstance(val, int):
1254-
shift_days = val
1255-
else:
1256-
# try int-like string, else treat as explicit label
1257-
try:
1258-
shift_days = int(str(val).strip())
1259-
except Exception:
1260-
label = str(val)
1261-
if label is None and isinstance(base_date, date):
1262-
vdate = base_date + timedelta(days=shift_days)
1263-
dl_threads_out[t] = _abbr(vdate)
1264-
else:
1265-
dl_threads_out[t] = label or ""
1196+
dl_threads_out = _thread_deadline_labels(threads_order)
12661197

12671198
threads_html = table_template.render(
12681199
task_types=task_types_threads,
@@ -1275,31 +1206,8 @@ def _build_process_rows(processes_dirs: list[str]):
12751206
# Use dedicated template for processes table layout
12761207
processes_template = env.get_template("processes.html.j2")
12771208
proc_vmaxes = [_find_process_variants_max(cfg, n) for n in expected_numbers]
1278-
# Build display deadlines for processes in task order (1..3)
1279-
auto_proc_dl = _compute_display_deadlines_processes(len(expected_numbers))
1280-
proc_deadlines_list: list[str] = []
1281-
for i, n in enumerate(expected_numbers):
1282-
base_date = auto_proc_dl[i]
1283-
shift_days = 0
1284-
label = None
1285-
if deadlines_display_processes:
1286-
key = f"task_{n}"
1287-
val = deadlines_display_processes.get(
1288-
key
1289-
) or deadlines_display_processes.get(f"mpi_task_{n}")
1290-
if val is not None:
1291-
if isinstance(val, int):
1292-
shift_days = val
1293-
else:
1294-
try:
1295-
shift_days = int(str(val).strip())
1296-
except Exception:
1297-
label = str(val)
1298-
if label is None and isinstance(base_date, date):
1299-
vdate = base_date + timedelta(days=shift_days)
1300-
proc_deadlines_list.append(_abbr(vdate))
1301-
else:
1302-
proc_deadlines_list.append(label or "")
1209+
# Build display deadlines for processes in task order (1..3).
1210+
proc_deadlines_list = _process_deadline_labels(expected_numbers)
13031211

13041212
processes_html = processes_template.render(
13051213
top_task_names=proc_top_headers,
@@ -1357,27 +1265,8 @@ def _slugify(text: str) -> str:
13571265
eff_num_proc,
13581266
deadlines_cfg,
13591267
)
1360-
# Rebuild deadline labels for this page
1361-
auto_threads_dl_g = _compute_display_deadlines_threads(threads_order)
1362-
dl_threads_out_g = {}
1363-
for t in threads_order:
1364-
base_date = auto_threads_dl_g.get(t)
1365-
shift_days = 0
1366-
label = None
1367-
if deadlines_display_threads and t in deadlines_display_threads:
1368-
val = deadlines_display_threads.get(t)
1369-
if isinstance(val, int):
1370-
shift_days = val
1371-
else:
1372-
try:
1373-
shift_days = int(str(val).strip())
1374-
except Exception:
1375-
label = str(val)
1376-
if label is None and isinstance(base_date, date):
1377-
vdate = base_date + timedelta(days=shift_days)
1378-
dl_threads_out_g[t] = _abbr(vdate)
1379-
else:
1380-
dl_threads_out_g[t] = label or ""
1268+
# Rebuild deadline labels for this page.
1269+
dl_threads_out_g = _thread_deadline_labels(threads_order)
13811270

13821271
html_g = table_template.render(
13831272
task_types=task_types_threads,
@@ -1427,31 +1316,8 @@ def _id_key(stud: dict) -> str:
14271316
rows_g = _build_process_rows(filtered_dirs)
14281317

14291318
proc_vmaxes_g = [_find_process_variants_max(cfg, n) for n in [1, 2, 3]]
1430-
# Build display deadlines for processes group page
1431-
auto_proc_dl_g = _compute_display_deadlines_processes(3)
1432-
proc_deadlines_list_g: list[str] = []
1433-
for i, n in enumerate([1, 2, 3]):
1434-
base_date = auto_proc_dl_g[i]
1435-
shift_days = 0
1436-
label = None
1437-
if deadlines_display_processes:
1438-
key = f"task_{n}"
1439-
val = deadlines_display_processes.get(
1440-
key
1441-
) or deadlines_display_processes.get(f"mpi_task_{n}")
1442-
if val is not None:
1443-
if isinstance(val, int):
1444-
shift_days = val
1445-
else:
1446-
try:
1447-
shift_days = int(str(val).strip())
1448-
except Exception:
1449-
label = str(val)
1450-
if label is None and isinstance(base_date, date):
1451-
vdate = base_date + timedelta(days=shift_days)
1452-
proc_deadlines_list_g.append(_abbr(vdate))
1453-
else:
1454-
proc_deadlines_list_g.append(label or "")
1319+
# Build display deadlines for processes group page.
1320+
proc_deadlines_list_g = _process_deadline_labels([1, 2, 3])
14551321

14561322
html_g = processes_template.render(
14571323
top_task_names=proc_top_headers_g,

0 commit comments

Comments
 (0)