Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision
  • devel
  • fo
  • fo-base
  • honza/add-contestant
  • honza/kolo-vs-soutez
  • honza/mr6
  • honza/mr7
  • honza/mra
  • honza/mrd
  • honza/mrf
  • honza/submit-images
  • jh-stress-test-wip
  • jirka/typing
  • jk/issue-196
  • jk/issue-96
  • master
  • mj/submit-images
  • shorten-schools
18 results

Target

Select target project
  • mo-p/osmo
1 result
Select Git revision
  • devel
  • fo
  • fo-base
  • honza/add-contestant
  • honza/kolo-vs-soutez
  • honza/mr6
  • honza/mr7
  • honza/mra
  • honza/mrd
  • honza/mrf
  • honza/submit-images
  • jh-stress-test-wip
  • jirka/typing
  • jk/issue-196
  • jk/issue-96
  • master
  • mj/submit-images
  • shorten-schools
18 results
Show changes
Showing
with 91 additions and 38 deletions
......@@ -4,16 +4,16 @@
{% set site_id = site.place_id if site else None %}
{% block title %}
{{ "Založení řešení" if edit_form else "Tabulka řešení" }} {% if site %}soutěžního místa {{ site.name }}{% else %}oblasti {{ contest.place.name }}{% endif %}
{{ "Založení řešení" if edit_form else "Tabulka řešení" }} {% if site %}soutěžního místa {{ site.name }}{% else %}{{ contest.place.name_locative() }}{% endif %}
{% endblock %}
{% block breadcrumbs %}
{{ contest_breadcrumbs(round=round, contest=contest, site=site, action="Založení řešení" if edit_form else "Tabulka řešení") }}
{% endblock %}
{% block pretitle %}
{% if round.state in [RoundState.grading, RoundState.closed] %}
{% if contest.state in [RoundState.grading, RoundState.closed] %}
<div class="btn-group pull-right">
<a class="btn btn-default" href="{{ url_for('org_score', contest_id=contest.contest_id) }}">Výsledky oblasti</a>
<a class="btn btn-default" href="{{ url_for('org_score', contest_id=contest.contest_id) }}">Výsledky {{ round.get_level().name_genitive() }}</a>
<a class="btn btn-default" href="{{ url_for('org_score', round_id=round.round_id) }}">Výsledky kola</a>
</div>
{% endif %}
......@@ -93,7 +93,7 @@ konkrétní úlohu. Symbol <span class="icon">🗐</span> značí, že existuje
{% endif %}
<td class="sol">
{% if sol.points is not none %}
{{ sol.points }}
{{ sol.points|decimal }}
{% if sum_points.append(sol.points) %}{% endif %}
{% else %}
<span class="unknown">?</span>
......@@ -111,7 +111,7 @@ konkrétní úlohu. Symbol <span class="icon">🗐</span> značí, že existuje
{% endif %}
<a class="btn btn-xs btn-link icon" title="Detail řešení" href="{{ url_for('org_submit_list', contest_id=contest.contest_id, user_id=u.user_id, task_id=task.task_id, site_id=site_id) }}">🔍</a>
{% endfor %}
<th>{{ sum_points|sum }}</th>
<th>{{ sum_points|sum|decimal }}</th>
</tr>
{% endfor %}
<tfoot>
......
......@@ -15,8 +15,8 @@
{% block pretitle %}
<div class="btn-group pull-right">
<a class="btn btn-default" href="{{ url_for('org_contest_solutions', id=ct_id, site_id=site_id) }}">Všechny úlohy</a>
{% if round.state in [RoundState.grading, RoundState.closed] %}
<a class="btn btn-default" href="{{ url_for('org_score', contest_id=ct_id) }}">Výsledky oblasti</a>
{% if contest.state in [RoundState.grading, RoundState.closed] %}
<a class="btn btn-default" href="{{ url_for('org_score', contest_id=ct_id) }}">Výsledky {{ round.get_level().name_genitive() }}</a>
<a class="btn btn-default" href="{{ url_for('org_score', round_id=round.round_id) }}">Výsledky kola</a>
{% endif %}
</div>
......
......@@ -18,7 +18,7 @@
<a class="btn btn-default" href="{{ url_for('org_round_list', id=round.round_id) }}">Účastníci</a>
<a class="btn btn-default" href="{{ url_for('org_score', round_id=round.round_id) }}">Výsledky</a>
</div>
<br>Soutěžní oblast:
<br>{{ round.get_level().name|capitalize }}:
<div class="btn-group">
<a class="btn btn-default" href="{{ url_for('org_contest_solutions', id=ct_id) }}">Tabulka řešení</a>
<a class="btn btn-default" href="{{ url_for('org_contest_list', id=ct_id) }}">Účastníci</a>
......@@ -40,7 +40,7 @@
<thead>
<tr><th colspan='2'>Účast v kole
</thead>
<tr><td>Soutěžní oblast:<td><a href='{{ url_for('org_contest', id=ct_id) }}'>{{ contest.place.name }}</a>
<tr><td>{{ round.get_level().name|capitalize }}:<td><a href='{{ url_for('org_contest', id=ct_id) }}'>{{ contest.place.name }}</a>
<tr><td>Soutěžní místo:<td><a href='{{ url_for('org_contest', id=ct_id, site_id=sc.pion.place_id) }}'>{{ sc.pion.place.name }}</a>
<tr><td>Stav účasti:<td>{{ sc.pion.state.friendly_name() }}
</table>
......
{% extends "base.html" %}
{% import "bootstrap/wtf.html" as wtf %}
{% block title %}Zadání kola {{ round.round_code() }}{% endblock %}
{% block breadcrumbs %}
{{ contest_breadcrumbs(round=round, action="Zadáni") }}
{% endblock %}
{% block body %}
{{ wtf.quick_form(form, form_type='horizontal', button_map={'upload': 'primary', 'delete': 'danger'}) }}
{% endblock %}
......@@ -15,7 +15,7 @@
<p>Zde si můžete stáhnout všechna řešení této úlohy
{% if contest %}
z dané oblasti.
{{ contest.place.name_locative() }}.
{% else %}
ze všech oblastí tohoto kola.
{% endif %}
......
......@@ -31,6 +31,7 @@
<p>Zde si můžete stáhnout bodovací formulář v zadaném formátu a pak ho nahrát zpět
s vyplněnými body. "<code>?</code>" místo bodů značí dosud neobodované řešení,
"<code>X</code>" značí řešení neodevzdané.
{% if round.points_step < 1 %}Při zadávání desetinných bodů můžete použít desetinnou tečku i čárku.{% endif %}
{{ wtf.quick_form(form, form_type='simple', button_map={'submit': 'primary'}) }}
......
......@@ -2,7 +2,7 @@
{% import "bootstrap/wtf.html" as wtf %}
{% block title %}
Import dat do {% if contest %}soutěžní oblasti {{ contest.place.name }}{% else %}kola {{ round.round_code() }}{% endif %}
Import dat do {% if contest %}soutěže {{ contest.place.name_locative() }}{% else %}kola {{ round.round_code() }}{% endif %}
{% endblock %}
{% block breadcrumbs %}
{{ contest_breadcrumbs(round=round, contest=contest, action="Import dat") }}
......
......@@ -11,6 +11,7 @@
<a href='{{ url_for('org_export_skoly', format='en_csv') }}'>CSV s čárkami</a>,
<a href='{{ url_for('org_export_skoly', format='cs_csv') }}'>CSV se středníky</a>,
<a href='{{ url_for('org_export_skoly', format='tsv') }}'>TSV</a>
<li><a href='https://docs.google.com/document/d/1XXk7Od-ZKtfmfNa-9FpFjUqmy0Ekzf2-2q3EpSWyn1w/edit?usp=sharing'>Návod na tvorbu PDF</a>
</ul>
<h3>Rychlé hledání</h3>
......
......@@ -16,7 +16,7 @@
<tr><td>Kategorie<td>{{ round.category }}
<tr><td>Pořadí<td>{{ round.seq }}
{% if round.part > 0 %}<tr><td>Část<td>{{ round.part_code() }}{% endif %}
<tr><td>Oblast<td>{{ level_names[round.level] }}
<tr><td>Oblast<td>{{ round.get_level().name }}
<tr><td>Vaše role<td>{% if g.user.is_admin %}správce{% elif roles %}{{ roles|join(", ") }}{% else %}–{% endif %}
{% if group_rounds|length > 1 %}
<tr><td>Skupina kol:<td>
......@@ -37,7 +37,7 @@
</table>
<table class=data style="float: left;">
<thead>
<tr><th colspan=2>Termíny
<tr><th colspan=2>Termíny a zadání
</thead>
<tr><td>Účastníci vidí zadání od<td>{{ round.ct_tasks_start|time_and_timedelta }}
<tr><td>Účastníci odevzdávají do<td>{{ round.ct_submit_end|time_and_timedelta }}
......@@ -45,7 +45,9 @@
<tr><td>Dozor odevzdává do<td>{{ round.pr_submit_end|time_and_timedelta }}
<tr><td>Zadání<td>
{% if round.tasks_file %}
{% if can_view_statement %}
{% if not statement_exists %}
<span class=error>soubor neexistuje</span>
{% elif can_view_statement %}
<a href='{{ url_for('org_task_statement', id=round.round_id) }}'>stáhnout</a>
{% else %}
není dostupné
......@@ -59,8 +61,9 @@
{% endif %}
</thead>
<tr><td>Výsledková listina<td>{{ round.master.score_mode.friendly_name() }}
<tr><td>Hranice bodů pro vítěze<td>{{ round.master.score_winner_limit|none_value(Markup('<i>nenastaveno</i>')) }}
<tr><td>Hranice bodů pro úspěšné řešitele<td>{{ round.master.score_successful_limit|none_value(Markup('<i>nenastaveno</i>')) }}
<tr><td>Hranice bodů pro vítěze<td>{{ round.master.score_winner_limit|decimal|none_value(Markup('<i>nenastaveno</i>')) }}
<tr><td>Hranice bodů pro úspěšné řešitele<td>{{ round.master.score_successful_limit|decimal|none_value(Markup('<i>nenastaveno</i>')) }}
<tr><td>Přesnost bodování<td>{{ round.master.points_step_name() }}
</table>
<div style="clear: both;"></div>
......@@ -73,7 +76,8 @@
<a class="btn btn-default" href='{{ url_for('org_round_import', id=round.round_id) }}'>Importovat data</a>
{% endif %}
{% if can_manage_round %}
<a class="btn btn-default" href='{{ url_for('org_round_edit', id=round.round_id) }}'>Editovat nastavení a termíny</a>
<a class="btn btn-default" href='{{ url_for('org_round_edit', id=round.round_id) }}'>Nastavení a termíny</a>
<a class="btn btn-default" href='{{ url_for('org_edit_statement', id=round.round_id) }}'>Zadání</a>
{% endif %}
{% if round.has_messages %}
<a class="btn btn-default" href='{{ url_for('org_round_messages', id=round.round_id) }}'>Zprávičky</a>
......@@ -88,7 +92,7 @@
<table class=data>
<thead>
<tr>
<th>Soutěžní oblast
<th>{{ round.get_level().name|capitalize }}
<th>Stav
<th>Počet účastníků
</tr>
......@@ -110,7 +114,7 @@
</tfoot>
</table>
{% else %}
<p>Zatím nebyly založeny v žádné oblasti.
<p>Zatím nebyly založeny žádné soutěže.
{% endif %}
{% if can_add_contest %}
......@@ -139,7 +143,7 @@
<td>{{ task.code }}
<td>{{ task.name }}
<td>{{ task.sol_count }}
<td>{{ task.max_points|none_value('–') }}
<td>{{ task.max_points|decimal|none_value('–') }}
{% if can_manage_round %}
<td><div class="btn-group">
<a class="btn btn-xs btn-primary" href="{{ url_for('org_round_task_edit', id=round.round_id, task_id=task.task_id) }}">Editovat</a>
......
......@@ -18,7 +18,7 @@
<td>{{ r.year }}
<td>{{ r.category }}
<td>{{ r.seq }}{{ r.part_code() }}
<td>{{ level_names[r.level] }}
<td>{{ r.get_level().name }}
<td>{{ r.name }}
<td class='rstate-{{r.state.name}}'>{{ r.state.friendly_name() }}
{% endfor %}
......
{% extends "base.html" %}
{% block title %}
{% if contest %}Výsledky oblasti {{ contest.place.name }}{% else %}Výsledky kola {{ round.round_code() }}{% endif %}
{{ round.round_code() }}: Výsledky pro {{ round.name|lower }} kategorie {{ round.category }}{% if contest %} {{ contest.place.name_locative() }}{% endif %}
{% endblock %}
{% block breadcrumbs %}
{{ contest_breadcrumbs(round=round, contest=contest, action="Výsledky oblasti" if contest else "Výsledky kola") }}
......@@ -55,10 +55,10 @@ Rozkliknutím bodů se lze dostat na detail daného řešení.</p>
{% if master.score_winner_limit is not none or master.score_successful_limit is not none %}
<p>
{% if master.score_winner_limit is not none %}
<b>Vítězi</b> se stávají účastníci s alespoň <b>{{ master.score_winner_limit|inflected("bodem", "body", "body") }}</b>.
<b>Vítězi</b> se stávají účastníci s alespoň <b>{{ master.score_winner_limit|decimal }} {{ master.score_winner_limit|inflected_by("bodem", "body", "body") }}</b>.
{% endif %}
{% if master.score_successful_limit is not none %}
<b>Úspěšnými řešiteli</b> se stávají účastníci s alespoň <b>{{ master.score_successful_limit|inflected("bodem", "body", "body") }}</b>.
<b>Úspěšnými řešiteli</b> se stávají účastníci s alespoň <b>{{ master.score_successful_limit|decimal }} {{ master.score_successful_limit|inflected_by("bodem", "body", "body") }}</b>.
{% endif %}
{% endif %}
......
{% extends "base.html" %}
{% import "bootstrap/wtf.html" as wtf %}
{% block title %}Řešení {{ sc.task.code }} {{ sc.task.name }} &ndash; {{ sc.user.full_name() }}{% endblock %}
{% block title %}{{ sc.user.full_name() }} – řešení úlohy {{ sc.task.code }} {{ sc.task.name }}{% endblock %}
{% block breadcrumbs %}
{{ contest_breadcrumbs(round=sc.round, contest=sc.contest, site=sc.site, task=sc.task, user=sc.user, action="Detail řešení") }}
{% endblock %}
......@@ -13,8 +13,8 @@
<tr><th>Úloha<td><a href='{{ url_for('org_contest_task', contest_id=sc.contest.contest_id, site_id=site_id, task_id=sc.task.task_id) }}'>{{ sc.task.code }} {{ sc.task.name }}</a>
{% if solution %}
<tr><th>Body<td>
{% if solution.points is not none %}{{solution.points}}{% else %}<span class="unknown">?</span>{% endif %}
{% if sc.task.max_points is not none %}<span class="hint"> / {{ sc.task.max_points }}</span>{% endif %}
{{ solution.points|decimal|none_value(Markup('<span class="unknown">?</span>')) }}
{% if sc.task.max_points is not none %}<span class="hint"> / {{ sc.task.max_points|decimal }}</span>{% endif %}
<tr title="Viditelná účastníkovi po uzavření kola">
<th>Poznámka k řešení:<td style="white-space: pre-line;">{{ solution.note|or_dash }}</td>
<tr title="Viditelná jen organizátorům">
......@@ -153,7 +153,7 @@ Existuje více než jedna verze oprav, finální je podbarvená.
{% for p in points_history %}
<tr {% if loop.index == 1 %} class='sol-active'{% endif %}>
<td>{{ p.points_at|timeformat }}
<td>{% if p.points is not none %}{{ p.points }}{% else %}–{% endif %}
<td>{{ p.points|decimal|none_value('–') }}
<td>{{ p.user|user_link }}
</tr>
{% endfor %}
......
......@@ -23,8 +23,8 @@
</label>
</div><div class="col-sm-5 radio">
<label>
<input id="action_on-1" name="action_on" type="radio" value="checked" required{% if action_form.action_on.data == 'all' %} checked{% endif %}>
označených účastnících
<input id="action_on-1" name="action_on" type="radio" value="checked" required{% if action_form.action_on.data == 'checked' %} checked{% endif %}>
pouze zaškrtnutých účastnících
</label>
</div>
</div>
......@@ -39,12 +39,16 @@
<div class="col-sm-4">{{ wtf.form_field(action_form.set_participation_place, form_type='inline', class='btn btn-primary') }}</div>
</div>
<div class="form-group">
<label class="control-label col-sm-2" for="contest_place">Soutěžní oblast</label>
<label class="control-label col-sm-2" for="contest_place">
{{ round.get_level().name|capitalize }}
</label>
<div class="col-sm-6">
{{ wtf.form_field(action_form.contest_place, form_type='inline', placeholder='Kód místa') }}
<p class="help-block">Musí existovat soutěž v dané oblasti pro stejné kolo.</p>
<p class="help-block">
{{ round.get_level().name_locative("V tomto", "V této", "V tomto") }} musí existovat soutěž pro {{ round.name|lower }} kategorie {{ round.category }}.
</p>
</div>
<div class="col-sm-4">{{ wtf.form_field(action_form.set_contest, form_type='inline', class='btn btn-primary') }}</div>
<div class="col-sm-4">{{ wtf.form_field(action_form.set_contest, form_type='inline', class='btn btn-primary', value='Přesunout do ' + round.get_level().name_genitive('jiného', 'jiné', 'jiného')) }}</div>
</div>
<div class="form-group">
<label class="control-label col-sm-2" for="contest_place">Smazání účasti</label>
......@@ -55,6 +59,6 @@
</form>
{% else %}
<p>
<i>Nemáte právo k editaci účastníků v této oblasti.</i>
<i>Nemáte právo k editaci účastníků v {{ round.get_level().name_locative("v tomto", "v této", "v tomto") }}.</i>
</p>
{% endif %}
......@@ -86,10 +86,15 @@ finální (ve výchozím stavu poslední nahrané).{% elif sc.allow_upload_solut
{% if sol.org_note %} <span class="icon" title="Interní poznámka: {{ sol.org_note }}">🗩</span>{% endif %}
<td>
{% if points_form %}
<input type="number" min=0 {% if task.max_points is not none %}max={{ task.max_points }}{% endif %} class="form-control" name="points_{{u.user_id}}" value="{{ request_form.get("points_{}".format(u.user_id)) or sol.points }}" size="4" tabindex={{ tabindex.value }} autofocus>
{% set tabindex.value = tabindex.value + 1%}
<input
type="number" class="form-control" name="points_{{u.user_id}}"
min=0 {% if task.max_points is not none %}max={{ task.max_points }}{% endif %}
step="{{ round.points_step }}"
value="{{ request_form.get("points_{}".format(u.user_id))|none_value(sol.points|decimal) }}"
size="4" tabindex={{ tabindex.value }} autofocus
>
{% else %}
{% if sol.points is not none %}{{ sol.points}}{% else %}<span class="unknown">?</span>{% endif %}
{{ sol.points|decimal|none_value(Markup('<span class="unknown">?</span>')) }}
{% endif %}
{% else %}
<td colspan="4" class="text-center">
......
......@@ -18,7 +18,7 @@ from mo.web import app
import mo.web.util
@app.route('/user')
@app.route('/user/')
def user_index():
pcrs = load_pcrs()
if getattr(config, 'AUTO_REGISTER_TEST', False) and not any(round.category == 'T' for pion, contest, round in pcrs):
......
import decimal
from flask import Response, send_file, url_for
from flask_wtf import FlaskForm
import os
......@@ -7,6 +8,7 @@ import unicodedata
import werkzeug.exceptions
import werkzeug.utils
import wtforms
from wtforms.fields.html5 import DecimalField
import mo.db as db
import mo.jobs
......@@ -40,6 +42,13 @@ class PagerForm(FlaskForm):
return (count, query)
def task_statement_exists(round: db.Round) -> bool:
if round.tasks_file is None:
return False
file = os.path.join(mo.util.data_dir('statements'), round.tasks_file)
return os.path.isfile(file)
def send_task_statement(round: db.Round) -> Response:
assert round.tasks_file is not None
file = os.path.join(mo.util.data_dir('statements'), round.tasks_file)
......@@ -119,3 +128,20 @@ def send_job_result(job: db.Job) -> Response:
else:
logger.error(f'Soubor {file} je výsledkem jobu, ale ve FS neexistuje')
raise werkzeug.exceptions.NotFound()
class MODecimalField(DecimalField):
"""Upravený DecimalField, který formátuje číslo podle jeho skutečného počtu
desetinných míst a zadané `places` používá jen jako maximální počet desetinných míst."""
def _value(self):
if self.data is not None:
# Spočítání počtu desetinných míst, zbytek necháme na původní implementaci
max_places = self.places
self.places = 0
d = decimal.Decimal(1)
while self.data % d != 0 and self.places < max_places:
self.places += 1
d /= 10
return super(MODecimalField, self)._value()