Skip to content
Snippets Groups Projects

Zakládání řešení zadáváním bodů

Merged Jiří Setnička requested to merge jirka/create-sol-points into devel
All threads resolved!
1 file
+ 2
2
Compare changes
  • Side-by-side
  • Inline
+ 138
27
@@ -353,6 +353,7 @@ def org_contest(id: int, site_id: Optional[int] = None):
can_manage=rr.have_right(Right.manage_contest),
can_upload=rr.can_upload_feedback(round),
can_edit_points=rr.can_edit_points(round),
can_create_solutions=rr.can_upload_feedback(round) or rr.can_upload_solutions(round),
can_view_statement=rr.can_view_statement(round),
tasks=tasks, places_counts=places_counts,
)
@@ -556,6 +557,7 @@ class SolutionContext:
allow_view: bool
allow_upload_solutions: bool
allow_upload_feedback: bool
allow_create_solutions: bool
allow_edit_points: bool
@@ -608,6 +610,8 @@ def get_solution_context(contest_id: int, user_id: Optional[int], task_id: Optio
if not allow_view:
raise werkzeug.exceptions.Forbidden()
allow_upload_solutions = rr.can_upload_solutions(round)
allow_upload_feedback = rr.can_upload_feedback(round)
return SolutionContext(
contest=contest,
round=round,
@@ -617,8 +621,9 @@ def get_solution_context(contest_id: int, user_id: Optional[int], task_id: Optio
site=site,
# XXX: Potřebujeme tohle všechno? Nechceme spíš vracet rr a nechat každého, ať na něm volá metody?
allow_view=allow_view,
allow_upload_solutions=rr.can_upload_solutions(round),
allow_upload_feedback=rr.can_upload_feedback(round),
allow_upload_solutions=allow_upload_solutions,
allow_upload_feedback=allow_upload_feedback,
allow_create_solutions=allow_upload_solutions or allow_upload_feedback,
allow_edit_points=rr.can_edit_points(round),
)
@@ -633,6 +638,7 @@ class SubmitForm(FlaskForm):
file_note = wtforms.TextAreaField("Poznámka k souboru")
submit_sol = wtforms.SubmitField('Uložit a nahrát soubor jako řešení')
submit_fb = wtforms.SubmitField('Uložit a nahrát soubor jako opravu')
delete = wtforms.SubmitField('Smazat řešení')
class SetFinalForm(FlaskForm):
@@ -704,6 +710,24 @@ def org_submit_list(contest_id: int, user_id: int, task_id: int, site_id: Option
form = SubmitForm(obj=sol)
if form.validate_on_submit():
if sol and form.delete.data:
if sol.final_submit or sol.final_feedback:
flash('Nelze smazat řešení, ke kterému již byl odevzdán soubor', 'danger')
else:
flash('Řešení smazáno', 'success')
sess.delete(sol)
mo.util.log(
type=db.LogType.participant,
what=sc.user.user_id,
details={
'action': 'solution-removed',
'task': task_id,
},
)
sess.commit()
app.logger.info(f"Řešení úlohy {sc.task.code} od účastníka {sc.user.user_id} smazáno")
return redirect(self_url)
points = form.points.data
# Checks
if sol and sc.allow_edit_points and points and points < 0:
@@ -713,6 +737,21 @@ def org_submit_list(contest_id: int, user_id: int, task_id: int, site_id: Option
flash('Schází soubor k nahrání, žádné změny nebyly uloženy', 'danger')
return redirect(self_url)
if not sol and (sc.allow_edit_points or sc.allow_upload_solutions or sc.allow_upload_feedback):
flash('Řešení založeno', 'success')
sol = db.Solution(task=sc.task, user=sc.user)
sess.add(sol)
mo.util.log(
type=db.LogType.participant,
what=sc.user.user_id,
details={
'action': 'solution-created',
'task': task_id,
},
)
sess.commit()
app.logger.info(f"Řešení úlohy {sc.task.code} od účastníka {sc.user.user_id} založeno")
# Edit sol and points
if sol and sc.allow_edit_points:
# Sol edit
@@ -768,16 +807,6 @@ def org_submit_list(contest_id: int, user_id: int, task_id: int, site_id: Option
return redirect(self_url)
sess.add(paper)
# FIXME: Bylo by hezké použít INSERT ... ON CONFLICT UPDATE
# (SQLAlchemy to umí, ale ne přes ORM, jen core rozhraním)
sol = (sess.query(db.Solution)
.filter_by(user_id=user_id, task_id=task_id)
.with_for_update()
.one_or_none())
if sol is None:
sol = db.Solution(task=sc.task, user=sc.user)
sess.add(sol)
if type == db.PaperType.solution:
sol.final_submit_obj = paper
else:
@@ -874,20 +903,24 @@ class TaskPointsForm(FlaskForm):
submit = wtforms.SubmitField("Uložit body")
class TaskCreateForm(FlaskForm):
submit = wtforms.SubmitField("Založit označená řešení")
@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>/')
@app.route('/org/contest/c/<int:contest_id>/task/<int:task_id>/points', methods=('GET', 'POST'), endpoint="org_contest_task_points")
@app.route('/org/contest/c/<int:contest_id>/task/<int:task_id>/create', methods=('GET', 'POST'), endpoint="org_contest_task_create")
@app.route('/org/contest/c/<int:contest_id>/site/<int:site_id>/task/<int:task_id>/create', methods=('GET', 'POST'), endpoint="org_contest_task_create")
def org_contest_task(contest_id: int, task_id: int, site_id: Optional[int] = None):
sc = get_solution_context(contest_id, None, task_id, site_id)
edit_points = request.endpoint == "org_contest_task_points"
if edit_points:
assert site_id is None
if not sc.allow_edit_points:
action_create = request.endpoint == "org_contest_task_create"
action_points = request.endpoint == "org_contest_task_points"
if action_create and not sc.allow_create_solutions:
raise werkzeug.exceptions.Forbidden()
if action_points and not sc.allow_edit_points:
raise werkzeug.exceptions.Forbidden()
if sc.round.state != db.RoundState.grading:
flash("Kolo není ve stavu opravování, nelze zadávat body", "warning")
return redirect(url_for('org_contest_task', contest_id=contest_id, task_id=task_id))
sess = db.get_session()
@@ -896,8 +929,41 @@ def org_contest_task(contest_id: int, task_id: int, site_id: Optional[int] = Non
rows.sort(key=lambda r: r[0].user.sort_key())
points_form: Optional[TaskPointsForm] = None
create_form: Optional[TaskCreateForm] = None
if action_create:
create_form = TaskCreateForm()
if create_form.validate_on_submit():
new_sol_count = 0
for pion, sol in rows:
if sol:
continue # již existuje
if not request.form.get(f"create_sol_{pion.user_id}"):
continue # nikdo nežádá o vytvoření
sol = db.Solution(task=sc.task, user=pion.user)
sess.add(sol)
mo.util.log(
type=db.LogType.participant,
what=pion.user_id,
details={
'action': 'solution-created',
'task': task_id,
},
)
app.logger.info(f"Řešení úlohy {sc.task.code} od účastníka {pion.user_id} založeno")
new_sol_count += 1
if new_sol_count > 0:
sess.commit()
flash(inflect_by_number(new_sol_count, "Založeno", "Založena", "Založeno") + ' '
+ inflect_number(new_sol_count, "nové řešení", "nová řešení", "nových řešení"),
"success")
else:
flash("Žádné změny k uložení", "info")
return redirect(url_for('org_contest_task', contest_id=contest_id, task_id=task_id, site_id=site_id))
if edit_points:
if action_points:
points_form = TaskPointsForm()
if points_form.validate_on_submit():
count = 0
@@ -906,10 +972,13 @@ def org_contest_task(contest_id: int, task_id: int, site_id: Optional[int] = Non
if sol is None:
continue
points = request.form.get(f"points_{sol.user_id}", type=int)
if points and points < 0:
if points is None:
continue
if points < 0:
flash('Nelze zadat záporné body', 'danger')
ok = False
break
if points != sol.points:
# Save points
sol.points = points
@@ -924,7 +993,7 @@ def org_contest_task(contest_id: int, task_id: int, site_id: Optional[int] = Non
if ok:
if count > 0:
sess.commit()
flash("Změněny body u " + inflect_number(count, "řešitele", "řešitelů", "řešitelů"), "success")
flash("Změněny body u " + inflect_number(count, "řešení", "řešení", "řešení"), "success")
else:
flash("Žádné změny k uložení", "info")
return redirect(url_for('org_contest_task', contest_id=contest_id, task_id=task_id))
@@ -943,17 +1012,26 @@ def org_contest_task(contest_id: int, task_id: int, site_id: Optional[int] = Non
"org_contest_task.html",
sc=sc, rows=rows, paper_counts=paper_counts,
paper_link=lambda u, p: mo.web.util.org_paper_link(sc.contest, sc.site, u, p),
can_upload=sc.allow_upload_feedback,
points_form=points_form, request_form=request.form,
points_form=points_form, create_form=create_form, request_form=request.form,
)
class ContestSolutionsEditForm(FlaskForm):
submit = wtforms.SubmitField("Založit označená řešení")
@app.route('/org/contest/c/<int:id>/solutions', methods=('GET', 'POST'))
@app.route('/org/contest/c/<int:id>/site/<int:site_id>/solutions', methods=('GET', 'POST'))
@app.route('/org/contest/c/<int:id>/solutions/edit', methods=('GET', 'POST'), endpoint="org_contest_solutions_edit")
@app.route('/org/contest/c/<int:id>/site/<int:site_id>/solutions/edit', methods=('GET', 'POST'), endpoint="org_contest_solutions_edit")
def org_contest_solutions(id: int, site_id: Optional[int] = None):
sc = get_solution_context(id, None, None, site_id)
sess = db.get_session()
edit_action = request.endpoint == "org_contest_solutions_edit"
if edit_action and not sc.allow_create_solutions:
raise werkzeug.exceptions.Forbidden()
pions_subq = sess.query(db.Participation.user_id).filter_by(contest=sc.contest)
if sc.site:
pions_subq = pions_subq.filter_by(place=sc.site)
@@ -996,13 +1074,46 @@ def org_contest_solutions(id: int, site_id: Optional[int] = None):
for s in sols:
task_sols[s.task_id][s.user_id] = s
edit_form: Optional[ContestSolutionsEditForm] = None
if edit_action:
edit_form = ContestSolutionsEditForm()
if edit_form.validate_on_submit():
new_sol_count = 0
for task in tasks:
for pion in pions:
if pion.user_id in task_sols[task.task_id]:
continue # již existuje
if not request.form.get(f"create_sol_{task.task_id}_{pion.user_id}"):
continue # nikdo nežádá o vytvoření
sol = db.Solution(task=task, user=pion.user)
sess.add(sol)
mo.util.log(
type=db.LogType.participant,
what=pion.user_id,
details={
'action': 'solution-created',
'task': task.task_id,
},
)
app.logger.info(f"Řešení úlohy {task.code} od účastníka {pion.user_id} založeno")
new_sol_count += 1
if new_sol_count > 0:
sess.commit()
flash(inflect_by_number(new_sol_count, "Založeno", "Založena", "Založeno") + ' '
+ inflect_number(new_sol_count, "nové řešení", "nová řešení", "nových řešení"),
"success")
else:
flash("Žádné změny k uložení", "info")
return redirect(url_for('org_contest_solutions', id=id, site_id=site_id))
return render_template(
'org_contest_solutions.html',
contest=sc.contest, site=sc.site, sc=sc,
pions=pions, tasks=tasks, tasks_sols=task_sols, paper_counts=paper_counts,
can_upload=sc.allow_upload_feedback,
can_edit_points=sc.allow_edit_points,
paper_link=lambda u, p: mo.web.util.org_paper_link(sc.contest, sc.site, u, p),
edit_form=edit_form,
)
Loading