Skip to content
Snippets Groups Projects

Tabulky řešení

All threads resolved!
Merged
Jiří Setničkarequested to merge
jirka/solutions-table into devel
All threads resolved!

Files

+ 163
81
@@ -5,6 +5,7 @@ import flask_wtf.file
import locale
import os
import secrets
from sqlalchemy import func
from sqlalchemy.orm import joinedload
from sqlalchemy.orm.query import Query
from typing import List, Tuple, Optional, Sequence, Dict
@@ -197,15 +198,69 @@ def get_contest_rr(id: int, right_needed: Optional[Right] = None) -> Tuple[db.Co
return contest, rr
def get_contest_site_rr(id: int, site_id: Optional[int], right_needed: Optional[Right] = None) -> Tuple[db.Contest, db.Place, Rights]:
if site_id is None:
contest, rr = get_contest_rr(id, right_needed)
return contest, None, rr
contest = get_contest(id)
site = db.get_session().query(db.Place).get(site_id)
if not site:
raise werkzeug.exceptions.NotFound()
rr = Rights(g.user)
rr.get_for_contest_site(contest, site)
if not (right_needed is None or rr.have_right(right_needed)):
raise werkzeug.exceptions.Forbidden()
return contest, site, rr
@app.route('/org/contest/c/<int:id>')
def org_contest(id: int):
contest, rr = get_contest_rr(id, None)
@app.route('/org/contest/c/<int:id>/site/<int:site_id>/')
def org_contest(id: int, site_id: Optional[int] = None):
sess = db.get_session()
contest, site, rr = get_contest_site_rr(id, site_id, None)
sol_counts_q = (
sess.query(db.Solution.task_id, func.count(db.Solution.task_id))
.filter(db.Solution.task_id.in_(
sess.query(db.Task.task_id).filter_by(round=contest.round)
))
)
if site:
sol_counts_q = sol_counts_q.filter(db.Solution.user_id.in_(
sess.query(db.Participation.user_id).filter_by(place=site)
))
sol_counts = {}
for task_id, count in sol_counts_q.group_by(db.Solution.task_id).all():
sol_counts[task_id] = count
tasks = sess.query(db.Task).filter_by(round=contest.round).all()
tasks.sort(key=lambda t: t.code)
for task in tasks:
task.sol_count = sol_counts[task.task_id] if task.task_id in sol_counts else 0
count = None
places_counts = None
if site_id:
count = sess.query(db.Participation).filter_by(place_id=site_id).count()
else:
places_counts = (
sess.query(db.Place, func.count('*'))
.select_from(db.Participation).join(db.Place)
.group_by(db.Place)
.filter(db.Participation.contest_id == id).all()
)
return render_template(
'org_contest.html',
contest=contest,
contest=contest, site=site,
rights=sorted(rr.current_rights, key=lambda r: r. name),
can_manage=rr.have_right(Right.manage_contest),
tasks=tasks, places_counts=places_counts, count=count,
)
@@ -250,8 +305,9 @@ def org_contest_import_template():
@app.route('/org/contest/c/<int:id>/ucastnici', methods=('GET', 'POST'))
def org_contest_list(id: int):
contest, rr = get_contest_rr(id)
@app.route('/org/contest/c/<int:id>/site/<int:site_id>/ucastnici', methods=('GET', 'POST'))
def org_contest_list(id: int, site_id: Optional[int] = None):
contest, site, rr = get_contest_site_rr(id, site_id)
can_edit = rr.have_right(Right.manage_contest)
format = request.args.get('format', "")
@@ -261,7 +317,7 @@ def org_contest_list(id: int):
round=contest.round, contest=contest,
school=db.get_place_by_code(filter.school.data),
# contest_place=db.get_place_by_code(filter.contest_place.data),
participation_place=db.get_place_by_code(filter.participation_place.data),
participation_place=site if site else db.get_place_by_code(filter.participation_place.data),
participation_state=None if filter.participation_state.data == '*' else filter.participation_state.data
)
@@ -279,7 +335,7 @@ def org_contest_list(id: int):
table = make_contestant_table(query, add_checkbox=can_edit)
return render_template(
'org_contest_list.html',
contest=contest,
contest=contest, site=site,
table=table,
filter=filter, count=count, action_form=action_form,
)
@@ -371,126 +427,111 @@ def make_contestant_table(query: Query, add_checkbox: bool = False, add_contest_
@app.route('/org/contest/c/<int:id>/reseni')
def org_contest_solutions(id: int):
# FIXME: Práva?
# FIXME: Hlavička stránky podle Jirkova předělání
contest, rr = get_contest_rr(id, Right.manage_contest)
format = request.args.get('format', "")
@app.route('/org/contest/c/<int:id>/site/<int:site_id>/reseni')
def org_contest_solutions(id: int, site_id: Optional[int] = None):
contest, site, rr = get_contest_site_rr(id, site_id, Right.manage_contest)
sess = db.get_session()
pions_subq = sess.query(db.Participation.user_id).filter_by(contest=contest)
if site:
pions_subq = pions_subq.filter_by(place=site)
pions_subq = pions_subq.subquery()
pions = (sess.query(db.Participation)
.filter_by(contest=contest)
.filter(db.Participation.user_id.in_(pions_subq))
.options(joinedload(db.Participation.user))
.all())
tasks_subq = sess.query(db.Task.task_id).filter_by(round=contest.round).subquery()
tasks = (sess.query(db.Task)
.filter_by(round=contest.round)
.order_by(db.Task.code)
.all())
pions_subq = (sess.query(db.Participation.user_id)
.filter_by(contest=contest)
.subquery())
sols = (sess.query(db.Solution)
.filter(db.Solution.user_id.in_(pions_subq))
.options(joinedload(db.Solution.last_submit_obj),
joinedload(db.Solution.last_feedback_obj))
.all())
print('XXX pions:', pions) # FIXME
print('XXX tasks:', tasks)
print('XXX sols:', sols)
cols = [ Column(key='name', name='jmeno', title='Jméno') ]
sols = sess.query(db.Solution).filter(
db.Solution.user_id.in_(pions_subq),
db.Solution.task_id.in_(tasks_subq)
).options(
joinedload(db.Solution.last_submit_obj),
joinedload(db.Solution.last_feedback_obj)
).all()
task_sols: Dict[int, Dict[int, db.Solution]] = {}
for t in tasks:
cols.append(Column(key=f't-{t.task_id}', name=t.code))
task_sols[t.task_id] = {}
for s in sols:
task_sols[s.task_id][s.user_id] = s
rows = []
for pion in pions:
user = pion.user
r = {
'name': user.full_name(),
}
for t in tasks:
s = task_sols[t.task_id].get(user.user_id, None)
if s is not None:
cell = '*'
else:
cell = ""
r[f't-{t.task_id}'] = cell
rows.append(r)
print('XXX cols:', cols) # FIXME
print('XXX rows:', rows)
def paper_link(paper: db.Paper) -> str:
return url_for('org_submit_paper',
contest_id=contest.contest_id,
paper_id=paper.paper_id,
site_id=site_id,
filename=mo.web.util.task_paper_filename(paper))
table = Table(
columns=cols,
rows=rows,
filename='reseni',
return render_template(
'org_contest_solutions.html',
contest=contest, site=site,
pions=pions, tasks=tasks, tasks_sols=task_sols,
paper_link=paper_link,
)
if format == "":
return render_template(
'org_contest_solutions.html',
contest=contest,
table=table,
)
else:
return table.send_as(format)
@dataclass
class SolutionContext:
contest: db.Contest
round: db.Round
pion: db.Participation
user: db.User
user: Optional[db.User]
task: db.Task
site: Optional[db.Place]
allow_view: bool
allow_upload_solutions: bool
allow_upload_feedback: bool
def get_solution_context(contest_id: int, user_id: int, task_id: int, site_id: Optional[int]) -> SolutionContext:
def get_solution_context(contest_id: int, user_id: Optional[int], task_id: int, site_id: Optional[int]) -> SolutionContext:
sess = db.get_session()
# Nejprve zjistíme, zda existuje soutěž
contest = get_contest(contest_id)
round = contest.round
# Zkontrolujeme, zda se účastník opravdu účastní soutěže
pion = (sess.query(db.Participation)
.filter_by(user_id=user_id, contest_id=contest_id)
.options(joinedload(db.Participation.place),
joinedload(db.Participation.user))
.one_or_none())
if not pion:
raise werkzeug.exceptions.NotFound()
# A zda soutěží na zadaném soutěžním místě, je-li určeno
if site_id is not None and site_id != pion.site_id:
raise werkzeug.exceptions.NotFound()
# Najdeme úlohu a ověříme, že je součástí soutěže
task = sess.query(db.Task).get(task_id)
if not task or task.round != round:
raise werkzeug.exceptions.NotFound()
# Pokud je uvedeno soutěžní místo, hledáme práva k němu, jinak k soutěži
if site_id is not None:
site = pion.place
site = None
user = None
if user_id is not None:
# Zkontrolujeme, zda se účastník opravdu účastní soutěže
pion = (sess.query(db.Participation)
.filter_by(user_id=user_id, contest_id=contest_id)
.options(joinedload(db.Participation.place),
joinedload(db.Participation.user))
.one_or_none())
if not pion:
raise werkzeug.exceptions.NotFound()
user = pion.user
# A zda soutěží na zadaném soutěžním místě, je-li určeno
if site_id is not None and site_id != pion.place_id:
raise werkzeug.exceptions.NotFound()
# Pokud je uvedeno soutěžní místo, hledáme práva k němu, jinak k soutěži
if site_id is not None:
site = pion.place
else:
site = contest.place
pion = None
if site_id is not None:
site = sess.query(db.Place).get(site_id)
if not site:
raise werkzeug.exceptions.NotFound()
rr = Rights(g.user)
rr.get_for_contest_site(contest, site)
rr.get_for_contest_site(contest, site or contest.place)
# Kdo má právo na jaké operace
allow_upload_solutions = (rr.have_right(Right.manage_contest)
@@ -507,8 +548,9 @@ def get_solution_context(contest_id: int, user_id: int, task_id: int, site_id: O
contest=contest,
round=round,
pion=pion,
user=pion.user,
user=user,
task=task,
site=site,
allow_view=allow_view,
allow_upload_solutions=allow_upload_solutions,
allow_upload_feedback=allow_upload_feedback,
@@ -672,3 +714,43 @@ def org_proctor_import_template():
@app.route('/doc/import-dozor')
def org_proctor_import_help():
return render_template('doc_import_proctor.html')
def get_solutions_query(
task: db.Task,
for_contest: Optional[db.Contest] = None,
for_site: Optional[db.Place] = None) -> Query:
sess = db.get_session()
pions_filter = sess.query(db.Participation.user_id)
if for_contest:
pions_filter = pions_filter.filter_by(contest=for_contest)
if for_site:
pions_filter = pions_filter.filter_by(place=for_site)
solutions = sess.query(db.Solution).filter_by(task=task)
if for_contest or for_site:
solutions = solutions.filter(db.Solution.user_id.in_(pions_filter))
return solutions
@app.route('/org/contest/c/<int:contest_id>/task/<int:task_id>/')
@app.route('/org/contest/c/<int:contest_id>/site/<int:site_id>/task/<int:task_id>/')
def org_contest_task_submits(contest_id: int, task_id: int, site_id: Optional[int] = None):
sc = get_solution_context(contest_id, None, task_id, site_id)
q = get_solutions_query(sc.task, for_contest=sc.contest, for_site=sc.site)
solutions: List[db.Solution] = q.all()
def paper_link(paper: db.Paper) -> str:
return url_for('org_submit_paper',
contest_id=sc.contest.contest_id,
paper_id=paper.paper_id,
site_id=site_id,
filename=mo.web.util.task_paper_filename(paper))
return render_template(
"org_contest_task.html",
sc=sc, solutions=solutions,
paper_link=paper_link,
)
Loading