From 7c24a7ec71afb469aa63c634a70007822ff69b1f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ji=C5=99=C3=AD=20Setni=C4=8Dka?= <setnicka@seznam.cz>
Date: Sun, 7 Mar 2021 20:30:27 +0100
Subject: [PATCH] =?UTF-8?q?V=C3=BDsledkov=C3=A1=20listina=20upravena=20pro?=
 =?UTF-8?q?=20d=C4=9Blen=C3=A1=20kola?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Pro dělená kola výsledkovka agreguje všechny úlohy z kol ve skupině.

Při zobrazení na webu je potřeba správně odkazovat na patřičné contesty:
* Proklik na účastníky zůstává ve stejném kole
* Proklik na záhlaví úlohy může vést i do jiného kola skupiny
* Proklik na submit může vést i do jiného kola skupiny

Issue #178
---
 mo/score.py         | 16 +++++++++-------
 mo/web/org_score.py | 23 ++++++++++++++++++-----
 2 files changed, 27 insertions(+), 12 deletions(-)

diff --git a/mo/score.py b/mo/score.py
index b7f4ed5d..aba2ffe7 100644
--- a/mo/score.py
+++ b/mo/score.py
@@ -105,12 +105,12 @@ class Score:
         self.contest = contest
         self.part_states = part_states
 
-        # Příprava subquery na účastníky
+        # Příprava subquery na účastníky (contest_subq obsahuje master_contest_id)
         sess = db.get_session()
         if contest:
-            contest_subq = [contest.contest_id]
+            contest_subq = [contest.master_contest_id]
         else:
-            contest_subq = sess.query(db.Contest.contest_id).filter_by(round=round)
+            contest_subq = sess.query(db.Contest.master_contest_id).filter_by(round=round)
 
         # Načtení účastníků
         data: List[Tuple[db.User, db.Participation, db.Participant]] = (
@@ -164,7 +164,9 @@ class Score:
         num_participants = db.get_count(user_id_subq)
 
         # Načtení úloh
-        tasks: List[db.Task] = sess.query(db.Task).filter_by(round=round).all()
+        tasks: List[db.Task] = sess.query(db.Task).filter(db.Task.round_id.in_(
+            sess.query(db.Round.round_id).filter_by(master_round_id=round.master_round_id)
+        )).all()
         for task in tasks:
             self._tasks[step][task.task_id] = ScoreTask(task)
             self._tasks[step][task.task_id].num_solutions = num_participants
@@ -202,7 +204,7 @@ class Score:
         # Zkusíme nalézt kolo o `step` kroků zpět
         prev_round = sess.query(db.Round).filter_by(
             year=self.round.year, category=self.round.category, seq=self.round.seq - step
-        ).one_or_none()
+        ).filter(db.Round.master_round_id == db.Round.round_id).one_or_none()
         if prev_round is None:
             return False
         self._prev_rounds[step] = prev_round
@@ -211,13 +213,13 @@ class Score:
             # Pokud tvoříme výsledkovku pro contest, tak nás zajímají jen řešení
             # z podoblastí contestu spadajícího pod hlavní
             desc_cte = db.place_descendant_cte(self.contest.place, max_level=prev_round.level)
-            contest_subq = sess.query(db.Contest.contest_id).filter(
+            contest_subq = sess.query(db.Contest.master_contest_id).filter(
                 db.Contest.round == prev_round,
                 db.Contest.place_id.in_(select([desc_cte]))
             )
         else:
             # Pokud vytváříme výsledkovku pro celé kolo, bereme vše
-            contest_subq = sess.query(db.Contest.contest_id).filter_by(round=prev_round)
+            contest_subq = sess.query(db.Contest.master_contest_id).filter_by(round=prev_round)
 
         self._load_tasks_and_sols(step, prev_round, contest_subq)
         return True
diff --git a/mo/web/org_score.py b/mo/web/org_score.py
index 4925de52..34043704 100644
--- a/mo/web/org_score.py
+++ b/mo/web/org_score.py
@@ -1,6 +1,6 @@
 from flask import render_template, request, g
 from flask.helpers import url_for
-from typing import Optional
+from typing import List, Optional
 import werkzeug.exceptions
 
 import mo
@@ -102,6 +102,16 @@ def org_score(round_id: Optional[int] = None, contest_id: Optional[int] = None):
     results = score.get_sorted_results()
     messages = score.get_messages()
 
+    # Pro tvorbu odkazů na správné contesty ve výsledkovkách dělených kol
+    all_subcontests: List[db.Contest] = sess.query(db.Contest).filter(
+        db.Contest.round_id.in_(
+            sess.query(db.Round.round_id).filter_by(master_round_id=round.master_round_id)
+        )
+    ).all()
+    subcontest_id_map = {}
+    for subcontest in all_subcontests:
+        subcontest_id_map[(subcontest.round_id, subcontest.master_contest_id)] = subcontest.contest_id
+
     # Construct columns
     is_export = (format != "")
     columns = []
@@ -123,13 +133,14 @@ def org_score(round_id: Optional[int] = None, contest_id: Optional[int] = None):
     for task in tasks:
         title = task.code
         if contest_id:
+            local_ct_id = subcontest_id_map[(task.round_id, contest.master_contest_id)]
             title = '<a href="{}">{}</a>'.format(
-                url_for('org_contest_task', contest_id=contest_id, task_id=task.task_id),
+                url_for('org_contest_task', contest_id=local_ct_id, task_id=task.task_id),
                 task.code
             )
             if rr.can_edit_points(round):
                 title += ' <a href="{}" title="Editovat body" class="icon">✎</a>'.format(
-                    url_for('org_contest_task_points', contest_id=contest_id, task_id=task.task_id),
+                    url_for('org_contest_task_points', contest_id=local_ct_id, task_id=task.task_id),
                 )
         columns.append(Column(key=f'task_{task.task_id}', name=task.code, title=title))
     columns.append(Column(key='total_points', name='celkove_body', title='Celkové body'))
@@ -140,13 +151,14 @@ def org_score(round_id: Optional[int] = None, contest_id: Optional[int] = None):
     for result in results:
         user, pant, pion = result.user, result.pant, result.pion
         school = pant.school_place
+        local_pion_ct_id = subcontest_id_map[(round.round_id, pion.contest_id)]
         row = Row(keys={
             'order':        OrderCell(result.order.place, result.order.span, result.order.continuation),
             'winner':       'ano' if result.winner else '',
             'successful':   'ano' if result.successful else '',
             'user':         user,
             'email':        user.email,
-            'participant':  cell_pion_link(user, pion.contest_id, user.full_name()),
+            'participant':  cell_pion_link(user, local_pion_ct_id, user.full_name()),
             'contest':      CellLink(pion.contest.place.name, url_for('org_contest', id=pion.contest_id)),
             'pion_place':   pion.place.name,
             'school':       CellLink(school.name, url_for('org_place', id=school.place_id)),
@@ -157,8 +169,9 @@ def org_score(round_id: Optional[int] = None, contest_id: Optional[int] = None):
         })
         sols = result.get_sols_map()
         for task in tasks:
+            local_sol_ct_id = subcontest_id_map[(task.round_id, pion.contest_id)]
             row.keys[f'task_{task.task_id}'] = SolPointsCell(
-                contest_id=pion.contest_id, user=user, sol=sols.get(task.task_id)
+                contest_id=local_sol_ct_id, user=user, sol=sols.get(task.task_id)
             )
         if result.winner:
             row.html_attr = {"class": "winner", "title": "Vítěz"}
-- 
GitLab