Skip to content
Snippets Groups Projects
Commit e43a0028 authored by Jiří Setnička's avatar Jiří Setnička Committed by Jan Prachař
Browse files

Nastavení ručního zjednoznačnění pořadí u výsledkovek

Požaduje právo manage_round/manage_contest.

Issue #210
parent a3699387
No related branches found
No related tags found
No related merge requests found
from decimal import Decimal from decimal import Decimal
from flask import render_template, request, g from flask import render_template, request
from flask.helpers import url_for from flask.helpers import flash, url_for
from typing import List, Optional, Union from typing import List, Optional, Union
from sqlalchemy.orm import joinedload from flask_wtf.form import FlaskForm
import werkzeug.exceptions import werkzeug.exceptions
from werkzeug.utils import redirect
import wtforms
import mo import mo
import mo.db as db import mo.db as db
...@@ -11,8 +13,8 @@ from mo.rights import Right ...@@ -11,8 +13,8 @@ from mo.rights import Right
from mo.score import Score from mo.score import Score
from mo.web import app from mo.web import app
from mo.web.org_contest import get_context from mo.web.org_contest import get_context
from mo.web.table import Cell, CellLink, Column, Row, Table, cell_pion_link from mo.web.table import Cell, CellInput, CellLink, Column, Row, Table, cell_pion_link
from mo.util_format import format_decimal from mo.util_format import format_decimal, inflect_number
class OrderCell(Cell): class OrderCell(Cell):
...@@ -86,8 +88,14 @@ class SolPointsCell(Cell): ...@@ -86,8 +88,14 @@ class SolPointsCell(Cell):
return td+f'<a href="{url}" title="Zobrazit detail řešení">{points}</a>' return td+f'<a href="{url}" title="Zobrazit detail řešení">{points}</a>'
class ScoreEditForm(FlaskForm):
submit = wtforms.SubmitField("Uložit zjednoznačnění")
@app.route('/org/contest/r/<int:round_id>/score') @app.route('/org/contest/r/<int:round_id>/score')
@app.route('/org/contest/r/<int:round_id>/score/edit', methods=('GET', 'POST'), endpoint="org_score_edit")
@app.route('/org/contest/c/<int:ct_id>/score') @app.route('/org/contest/c/<int:ct_id>/score')
@app.route('/org/contest/c/<int:ct_id>/score/edit', methods=('GET', 'POST'), endpoint="org_score_edit")
@app.route('/public/kolo/<int:round_id>/vysledky', endpoint="public_score") @app.route('/public/kolo/<int:round_id>/vysledky', endpoint="public_score")
@app.route('/public/soutez/<int:ct_id>/vysledky', endpoint="public_score") @app.route('/public/soutez/<int:ct_id>/vysledky', endpoint="public_score")
def org_score(round_id: Optional[int] = None, ct_id: Optional[int] = None): def org_score(round_id: Optional[int] = None, ct_id: Optional[int] = None):
...@@ -106,12 +114,51 @@ def org_score(round_id: Optional[int] = None, ct_id: Optional[int] = None): ...@@ -106,12 +114,51 @@ def org_score(round_id: Optional[int] = None, ct_id: Optional[int] = None):
if state != db.RoundState.closed and not ctx.rights.have_right(Right.view_contestants): if state != db.RoundState.closed and not ctx.rights.have_right(Right.view_contestants):
raise werkzeug.exceptions.Forbidden() raise werkzeug.exceptions.Forbidden()
is_edit = request.endpoint == 'org_score_edit'
if is_edit and not ctx.rights.have_right(Right.manage_contest):
raise werkzeug.exceptions.Forbidden()
score = Score(round.master, contest) score = Score(round.master, contest)
tasks = score.get_tasks() tasks = score.get_tasks()
tasks.sort(key=mo.util.sortby_task_code) tasks.sort(key=mo.util.sortby_task_code)
results = score.get_sorted_results() results = score.get_sorted_results()
messages = score.get_messages() messages = score.get_messages()
edit_form: Optional[ScoreEditForm] = None
if is_edit:
edit_form = ScoreEditForm()
if edit_form.validate_on_submit():
count = 0
for result in results:
try:
score_suborder = int(request.form.get(f"suborder_{result.user.user_id}"))
except ValueError:
score_suborder = None
if score_suborder != result.pion.score_suborder:
app.logger.info(f"Změněno zjednoznačnění u soutěžícího #{result.user.user_id} v soutěži #{result.pion.contest_id}: {result.pion.score_suborder}->{score_suborder}")
mo.util.log(
type=db.LogType.participant,
what=result.user.user_id,
details={
'action': 'pion-changed-suborder',
'contest_id': result.pion.contest_id,
'old_score_suborder': result.pion.score_suborder,
'new_score_suborder': score_suborder
},
)
result.pion.score_suborder = score_suborder
count += 1
if count > 0:
sess.commit()
flash('Změněno zjednoznačnění u ' + inflect_number(count, 'soutěžícího', 'soutěžících', 'soutěžících'), 'success')
else:
flash('Žádné změny k uložení', 'info')
return redirect(ctx.url_for('org_score'))
# Pro tvorbu odkazů na správné contesty ve výsledkovkách dělených kol # 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( all_subcontests: List[db.Contest] = sess.query(db.Contest).filter(
db.Contest.round_id.in_( db.Contest.round_id.in_(
...@@ -155,6 +202,8 @@ def org_score(round_id: Optional[int] = None, ct_id: Optional[int] = None): ...@@ -155,6 +202,8 @@ def org_score(round_id: Optional[int] = None, ct_id: Optional[int] = None):
) )
columns.append(Column(key=f'task_{task.task_id}', name=task.code, title=title)) 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')) columns.append(Column(key='total_points', name='celkove_body', title='Celkové body'))
if is_edit:
columns.append(Column(key='suborder', name='zjednoznacneni_poradi', title='Pořadí na sdíleném místě'))
# columns.append(Column(key='order_key', name='order_key', title='Třídící klíč')) # columns.append(Column(key='order_key', name='order_key', title='Třídící klíč'))
# Construct rows # Construct rows
...@@ -190,6 +239,7 @@ def org_score(round_id: Optional[int] = None, ct_id: Optional[int] = None): ...@@ -190,6 +239,7 @@ def org_score(round_id: Optional[int] = None, ct_id: Optional[int] = None):
'total_points': SolTotalPointsCell(result.get_total_points()), 'total_points': SolTotalPointsCell(result.get_total_points()),
'birth_year': pant.birth_year, 'birth_year': pant.birth_year,
'order_key': result._order_key, 'order_key': result._order_key,
'suborder': CellInput(f"suborder_{user.user_id}", pion.score_suborder, "number", attrs={"size": 6}),
}) })
sols = result.get_sols_map() sols = result.get_sols_map()
...@@ -212,6 +262,7 @@ def org_score(round_id: Optional[int] = None, ct_id: Optional[int] = None): ...@@ -212,6 +262,7 @@ def org_score(round_id: Optional[int] = None, ct_id: Optional[int] = None):
columns=columns, columns=columns,
rows=table_rows, rows=table_rows,
filename=filename, filename=filename,
show_downlink=not is_edit,
) )
group_rounds = round.get_group_rounds(True) group_rounds = round.get_group_rounds(True)
...@@ -226,6 +277,7 @@ def org_score(round_id: Optional[int] = None, ct_id: Optional[int] = None): ...@@ -226,6 +277,7 @@ def org_score(round_id: Optional[int] = None, ct_id: Optional[int] = None):
group_rounds=group_rounds, group_rounds=group_rounds,
can_view_submits=ctx.rights.can_view_submits(), can_view_submits=ctx.rights.can_view_submits(),
public=public, public=public,
edit_form=edit_form,
) )
else: else:
return table.send_as(format) return table.send_as(format)
{% extends "base.html" %} {% extends "base.html" %}
{% import "bootstrap/wtf.html" as wtf %}
{% block title %} {% block title %}
Výsledky {{ round.name|round_genitive|lower }}{% if public %} {{ round.year }}. ročníku FO{% endif %} kategorie {{ round.category }}{% if contest %} {{ contest.place.name_locative() }}{% endif %} Výsledky {{ round.name|round_genitive|lower }}{% if public %} {{ round.year }}. ročníku FO{% endif %} kategorie {{ round.category }}{% if contest %} {{ contest.place.name_locative() }}{% endif %}
...@@ -40,8 +41,19 @@ Rozkliknutím bodů se lze dostat na detail daného řešení. ...@@ -40,8 +41,19 @@ Rozkliknutím bodů se lze dostat na detail daného řešení.
{% endif %} {% endif %}
{% endif %} {% endif %}
{% if edit_form %}
<p><strong>Zjednoznačnění pořadí:</strong> U soutěžících na sdílených pozicích vyplňte číslo do políčka na konci řádku. Třídí se vzestupně od nejmenšího, prázdné políčko se považuje za nulu.</p>
<form method="POST" class="form form-horizontal" action="">
{{ edit_form.csrf_token }}
{% endif %}
{{ table.to_html() }} {{ table.to_html() }}
{% if edit_form %}
{{ wtf.form_field(edit_form.submit, class="btn btn-primary pull-right") }}
</form>
{% endif %}
{% if not public %} {% if not public %}
{% for (type, msg) in messages %} {% for (type, msg) in messages %}
...@@ -60,9 +72,14 @@ Diskvalifikovaní, odmítnuvší a nepřítomní účastníci jsou skryti, stejn ...@@ -60,9 +72,14 @@ Diskvalifikovaní, odmítnuvší a nepřítomní účastníci jsou skryti, stejn
Jsou v ní započítány body ze všech úloh těchto kol.</p> Jsou v ní započítány body ze všech úloh těchto kol.</p>
{% endif %} {% endif %}
<p>
{% if contest and contest.state == RoundState.closed or round.state == RoundState.closed %} {% if contest and contest.state == RoundState.closed or round.state == RoundState.closed %}
<p><a class="btn btn-default" target="_blank" href="{% if contest %}{{ url_for('public_score', ct_id=contest.contest_id) }}{% else %}{{ url_for('public_score', round_id=round.round_id) }}{% endif %}">Veřejná výsledková listina</a> <a class="btn btn-default" target="_blank" href="{% if contest %}{{ url_for('public_score', ct_id=contest.contest_id) }}{% else %}{{ url_for('public_score', round_id=round.round_id) }}{% endif %}">Veřejná výsledková listina</a>
{% endif %}
{% if ctx.rights.have_right(Right.manage_contest) %}
<a class="btn btn-default" href="{{ ctx.url_for('org_score_edit') }}">Zjednoznačnit pořadí</a>
{% endif %} {% endif %}
</p>
{% endif %} {% endif %}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment