diff --git a/mo/db.py b/mo/db.py index 6309a5a93483f88ee44b446d8eb3b312a498bc4d..47eb48a0b8d4acbaff68c434436f4dc1f8e15905 100644 --- a/mo/db.py +++ b/mo/db.py @@ -20,6 +20,7 @@ from sqlalchemy.sql.sqltypes import Numeric from typing import Optional, List, Tuple import mo +from mo.place_level import place_levels, PlaceLevel from mo.util_format import timedelta, time_and_timedelta # HACK: Work-around for https://github.com/dropbox/sqlalchemy-stubs/issues/114 @@ -73,7 +74,7 @@ class PlaceType(MOEnum): (name, levels) = place_type_names_and_levels[item] if level is None or level in levels: if item == enum.region and level is not None: - name += " (" + place_level_names[level] + ")" + name += " (" + place_levels[level].name + ")" out.append((item.name, name)) return out @@ -108,8 +109,8 @@ class Place(Base): return "soutěžní místo" elif self.type == PlaceType.school: return "škola" - elif self.level < len(place_level_names): - return place_level_names[self.level] + elif self.level < len(place_levels): + return place_levels[self.level].name else: return "region" @@ -119,6 +120,15 @@ class Place(Base): def can_have_child(self): return len(PlaceType.choices(level=self.level + 1)) > 0 + def get_level(self) -> PlaceLevel: + return place_levels[self.level] + + def name_locative(self): + name = self.name + if self.level == 1: + name = name.replace("ý kraj", "ém").replace("Kraj ", "") + return place_levels[self.level].in_name() + " " + name + def get_root_place(): return get_session().query(Place).filter_by(parent=None).one() @@ -238,6 +248,9 @@ class Round(Base): part = self.part_code() return f"{code}{part}" + def get_level(self) -> PlaceLevel: + return place_levels[self.level] + def has_tasks(self): return self.tasks_file diff --git a/mo/imports.py b/mo/imports.py index 03ec65e05912774c76d23662cde2c4a568b24b4b..f0eedb2c30a10d70f18f09c794192f7106895974 100644 --- a/mo/imports.py +++ b/mo/imports.py @@ -139,7 +139,7 @@ class Import: ('. Nechybí vám # na začátku?' if re.fullmatch(r'\d+', kod) else '')) if not self.check_rights(place): - return self.error(f'K místu "{kod}" nemáte práva na správu soutěže') + return self.error(f'Nemáte práva na správu soutěže {place.name_locative()}') self.place_cache[kod] = place return place @@ -274,7 +274,7 @@ class Import: elif len(pions) == 1: pion = pions[0] if pion.place != place: - return self.error(f'Již se tohoto kola účastní v jiné oblasti ({pion.place.get_code()})') + return self.error(f'Již se tohoto kola účastní v {contest.round.get_level().name_locative("jiném", "jiné", "jiném")} ({pion.place.get_code()})') else: return self.error('Již se tohoto kola účastní ve vice oblastech, což by nemělo být možné') @@ -288,11 +288,11 @@ class Import: else: if oblast is None: if not allow_none: - self.error('Je nutné uvést oblast') + self.error('Je nutné uvést ' + self.round.get_level().name) return None contest = db.get_session().query(db.Contest).filter_by(round=self.round, place=oblast).one_or_none() if contest is None: - return self.error('V uvedené oblasti toto kolo neprobíhá') + return self.error('V ' + self.round.get_level().name_locative("uvedeném", "uvedené", "uvedeném") + ' toto kolo neprobíhá') return contest @@ -599,7 +599,7 @@ class JudgeImport(Import): contest = self.obtain_contest(oblast, allow_none=True) place = contest.place if contest else self.root_place if not self.check_rights(place): - return self.error(f'K místu "{place.get_code()}" nemáte práva na správu soutěže') + return self.error(f'Nemáte práva na správu soutěže {place.name_locative()}') self.add_role(user, place, db.RoleType.opravovatel) @@ -667,7 +667,7 @@ class PointsImport(Import): if self.contest is not None: if pion.contest != self.contest: - return self.error('Soutěžící nesoutěží v této oblasti') + return self.error('Soutěžící nesoutěží v ' + self.round.get_level().name_locative('tomto', 'této', 'tomto')) rights = self.gatekeeper.rights_for_contest(pion.contest) if not rights.can_edit_points(): diff --git a/mo/place_level.py b/mo/place_level.py new file mode 100644 index 0000000000000000000000000000000000000000..8ecc2ab4c58d1526b698426f7be98544e2c002f5 --- /dev/null +++ b/mo/place_level.py @@ -0,0 +1,41 @@ +from dataclasses import dataclass + + +@dataclass +class PlaceLevel: + level: int + genus: str + name: str + name_gen: str + name_acc: str + name_loc: str + in_prep: str + + def name_genitive(self, m="", f="", n="") -> str: + w = {'m': m, 'f': f, 'n': n}[self.genus] + return (w + ' ' if w is not '' else '') + self.name_gen + + def name_accusative(self, m="", f="", n="") -> str: + w = {'m': m, 'f': f, 'n': n}[self.genus] + return (w + ' ' if w is not '' else '') + self.name_acc + + def name_locative(self, m="", f="", n="") -> str: + w = {'m': m, 'f': f, 'n': n}[self.genus] + return (w + ' ' if w is not '' else '') + self.name_loc + + def in_name(self) -> str: + return self.in_prep + ' ' + self.name_loc + + +place_levels = [ + PlaceLevel(level=0, genus='m', in_prep='ve', + name='stát', name_gen='státu', name_acc='stát', name_loc='státě'), + PlaceLevel(level=1, genus='m', in_prep='v', + name='kraj', name_gen='kraje', name_acc='kraj', name_loc='kraji'), + PlaceLevel(level=2, genus='m', in_prep='v', + name='okres', name_gen='okresu', name_acc='okres', name_loc='okrese'), + PlaceLevel(level=3, genus='f', in_prep='v', + name='obec', name_gen='obce', name_acc='obec', name_loc='obci'), + PlaceLevel(level=4, genus='f', in_prep='ve', + name='škola', name_gen='školy', name_acc='školu', name_loc='škole'), +] diff --git a/mo/web/org_contest.py b/mo/web/org_contest.py index f9bdb12ca0b40f1696aa7399f6a881aa6413d81f..144d1787dd91ac46310e973e07ebacc6953c0486 100644 --- a/mo/web/org_contest.py +++ b/mo/web/org_contest.py @@ -129,16 +129,16 @@ class ParticipantsActionForm(FlaskForm): elif self.set_contest.data: contest_place = db.get_place_by_code(self.contest_place.data) if not contest_place: - flash("Nepovedlo se najít zadanou soutěžní oblast", 'danger') + flash("Nepovedlo se najít "+round.get_level().name_accusative("zadaný", "zadanou", "zadané"), 'danger') return False # Contest hledáme vždy v master kole, abychom náhodou nepřesunuli účastníky do soutěže v podkole contest = sess.query(db.Contest).filter_by(round_id=round.master_round_id, place_id=contest_place.place_id).one_or_none() if not contest: - flash(f"Nepovedlo se najít soutěž v kole {round.round_code_short()} v oblasti {contest_place.name}", 'danger') + flash(f"Nepovedlo se najít soutěž v kole {round.round_code_short()} {contest_place.name_locative()}", 'danger') return False rr = g.gatekeeper.rights_for_contest(contest) if not rr.have_right(Right.manage_contest): - flash(f"Nemáte právo ke správě soutěže v kole {round.round_code_short()} v oblasti {contest_place.name}, nelze do ní přesunout účastníky", 'danger') + flash(f"Nemáte právo ke správě soutěže v kole {round.round_code_short()} {contest_place.name_locative()}, nelze do ní přesunout účastníky", 'danger') return False elif self.remove_participation.data: pass @@ -161,7 +161,7 @@ class ParticipantsActionForm(FlaskForm): rr = g.gatekeeper.rights_for_contest(pion.contest) if not rr.have_right(Right.manage_contest): flash( - f"Nemáte právo ke správě soutěže v kole {round.round_code_short()} v oblasti {pion.contest.place.name} " + f"Nemáte právo ke správě soutěže v kole {round.round_code_short()} {pion.contest.place.name_locative()} " + f"(účastník {u.full_name()}). Žádná akce nebyla provedena.", 'danger' ) return False @@ -223,7 +223,7 @@ class ParticipantsActionForm(FlaskForm): elif self.set_contest.data: flash( inflect_number(count, 'účastník přesunut', 'účastníci přesunuti', 'účastníků přesunuto') - + f' do soutěže v oblasti {contest_place.name}', + + f' do soutěže {contest_place.name_locative()}', 'success' ) elif self.remove_participation.data: @@ -471,7 +471,7 @@ def org_contest_list(id: int, site_id: Optional[int] = None): else: # (count, query) = filter.apply_limits(query, pagesize=50) count = db.get_count(query) - table = make_contestant_table(query, add_checkbox=can_edit) + table = make_contestant_table(query, master_contest.round, add_checkbox=can_edit) return render_template( 'org_contest_list.html', @@ -480,7 +480,7 @@ def org_contest_list(id: int, site_id: Optional[int] = None): filter=filter, count=count, action_form=action_form, ) else: - table = make_contestant_table(query, is_export=True) + table = make_contestant_table(query, master_contest.round, is_export=True) return table.send_as(format) @@ -535,7 +535,7 @@ def get_contestants_query( return query -def make_contestant_table(query: Query, add_checkbox: bool = False, add_contest_column: bool = False, is_export: bool = False): +def make_contestant_table(query: Query, round: db.Round, add_checkbox: bool = False, add_contest_column: bool = False, is_export: bool = False): ctants = query.all() rows: List[Row] = [] @@ -572,7 +572,7 @@ def make_contestant_table(query: Query, add_checkbox: bool = False, add_contest_ if add_checkbox: cols = [Column(key='checkbox', name=' ', title=' ')] + cols if add_contest_column: - cols.append(Column(key='region_code', name='kod_oblasti', title='Oblast')) + cols.append(Column(key='region_code', name='kod_oblasti', title=round.get_level().name.title())) if is_export: cols.append(Column(key='user_id', name='user_id')) diff --git a/mo/web/org_round.py b/mo/web/org_round.py index 446c29d315c0ea297c48ec0a6abc9bcb5264969e..a2abf38e79bf30f5caf53af83213875fa5882e08 100644 --- a/mo/web/org_round.py +++ b/mo/web/org_round.py @@ -110,7 +110,7 @@ def add_contest(round: db.Round, form: AddContestForm) -> bool: return False if place.level != round.level: - flash(f'{place.type_name().title()} {place.name} není {db.place_level_names[round.level]}', 'danger') + flash(f'{place.type_name().title()} {place.name} není {round.level().name}', 'danger') return False sess = db.get_session() @@ -128,7 +128,7 @@ def add_contest(round: db.Round, form: AddContestForm) -> bool: contest = db.Contest(round=round.master, place=place, state=state) rr = g.gatekeeper.rights_for_contest(contest) if not rr.have_right(Right.add_contest): - flash('Vaše role nedovoluje vytvořit soutěž v oblasti {place.type_name()} {place.name}', 'danger') + flash(f'Vaše role nedovoluje vytvořit soutěž {place.name_locative()}', 'danger') return False sess.add(contest) @@ -164,7 +164,7 @@ def add_contest(round: db.Round, form: AddContestForm) -> bool: app.logger.info(f"Soutěž #{subcontest.contest_id} založena: {db.row2dict(subcontest)}") sess.commit() - flash(f'Soutěž v oblasti {place.type_name()} {place.name} založena', 'success') + flash(f'Založena soutěž {place.name_locative()}', 'success') return True @@ -213,6 +213,8 @@ def org_round(id: int): return redirect(url_for('org_round', id=id)) form_add_contest = AddContestForm() + form_add_contest.place_code.label.text = ( + "Nová soutěž " + round.get_level().in_name()) if add_contest(round, form_add_contest): return redirect(url_for('org_round', id=id)) @@ -383,7 +385,7 @@ def org_round_list(id: int): else: (count, query) = filter.apply_limits(query, pagesize=50) # count = db.get_count(query) - table = make_contestant_table(query, add_contest_column=True, add_checkbox=True) + table = make_contestant_table(query, round, add_contest_column=True, add_checkbox=True) return render_template( 'org_round_list.html', @@ -392,7 +394,7 @@ def org_round_list(id: int): filter=filter, count=count, action_form=action_form, ) else: - table = make_contestant_table(query, is_export=True) + table = make_contestant_table(query, round, is_export=True) return table.send_as(format) diff --git a/mo/web/org_score.py b/mo/web/org_score.py index 02c5e1cd5b25495be2ec2f02f074ee2123365920..62932ab90a2714e90624d266ab31a5be971d14e4 100644 --- a/mo/web/org_score.py +++ b/mo/web/org_score.py @@ -128,7 +128,7 @@ def org_score(round_id: Optional[int] = None, contest_id: Optional[int] = None): if is_export: columns.append(Column(key='email', name='email')) if not contest_id: - columns.append(Column(key='contest', name='oblast', title='Soutěžní oblast')) + columns.append(Column(key='contest', name='oblast', title=round.get_level().name.title())) if is_export: columns.append(Column(key='pion_place', name='soutezni_misto')) columns.append(Column(key='school', name='skola', title='Škola')) diff --git a/mo/web/templates/org_contest_advance.html b/mo/web/templates/org_contest_advance.html index 3bea35e52ec7000651dd1ad460b0cb18cec97a70..2094c11ac51a31f771947e5a2dc0c862ffba0127 100644 --- a/mo/web/templates/org_contest_advance.html +++ b/mo/web/templates/org_contest_advance.html @@ -21,7 +21,7 @@ <table class='data'> <thead> - <tr><th>Oblast<th>Postoupilo<th>Nepostoupilo<th> + <tr><th>{{ prev_round.get_level().name|capitalize }}<th>Postoupilo<th>Nepostoupilo<th> <tbody> {% for c in prev_contests %} <tr> diff --git a/mo/web/templates/org_contest_list.html b/mo/web/templates/org_contest_list.html index 3e28dd3a6a3a88db5a9035c7e25b08c7b3287841..a52f292d2b0ee28f7671d8c25229c66dcef4d19d 100644 --- a/mo/web/templates/org_contest_list.html +++ b/mo/web/templates/org_contest_list.html @@ -3,7 +3,7 @@ {% set round = contest.round %} {% block title %} -Seznam účastníků {% if site %}soutěžního místa {{ site.name }}{% else %}oblasti {{ contest.place.name }}{% endif %} +Seznam účastníků {% if site %}v soutěžním místě {{ site.name }}{% else %}{{ contest.place.name_locative() }}{% endif %} {% endblock %} {% block breadcrumbs %} {{ contest_breadcrumbs(round=round, contest=contest, site=site, action="Seznam účastníků") }} diff --git a/mo/web/templates/org_contest_solutions.html b/mo/web/templates/org_contest_solutions.html index 99f04c1e347b98721d57dcc77746a0fdbf6997cf..7bda4f7c3466e544f126108be2883da21fed8a1c 100644 --- a/mo/web/templates/org_contest_solutions.html +++ b/mo/web/templates/org_contest_solutions.html @@ -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 %} diff --git a/mo/web/templates/org_contest_task.html b/mo/web/templates/org_contest_task.html index d327cf1275f8e35df504e021fea770f229d2c7ce..9e8c65c00adbdfc977971e546976447ac78193b9 100644 --- a/mo/web/templates/org_contest_task.html +++ b/mo/web/templates/org_contest_task.html @@ -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> diff --git a/mo/web/templates/org_contest_user.html b/mo/web/templates/org_contest_user.html index c0420489eaa4faa094e9cdcb0039a012d132603c..c6e8e4708b63e80fed734f3da89a82db61b0e151 100644 --- a/mo/web/templates/org_contest_user.html +++ b/mo/web/templates/org_contest_user.html @@ -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> diff --git a/mo/web/templates/org_generic_batch_download.html b/mo/web/templates/org_generic_batch_download.html index 394d33de1f1e9fe8ccaa74b17f4d8a8b3639a6b5..b2248bed84cdc41a3a6973c9f2a5c180ceaa577f 100644 --- a/mo/web/templates/org_generic_batch_download.html +++ b/mo/web/templates/org_generic_batch_download.html @@ -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 %} diff --git a/mo/web/templates/org_generic_import.html b/mo/web/templates/org_generic_import.html index ebe4616a3b4ec546ed6704446e8efc829e73adec..295a450726147a5b15b08460303250a439a23526 100644 --- a/mo/web/templates/org_generic_import.html +++ b/mo/web/templates/org_generic_import.html @@ -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") }} diff --git a/mo/web/templates/org_round.html b/mo/web/templates/org_round.html index 17637aeaae19e82a285d533061e2d7b8c4dec380..5cf1afca8e54a3f85352babde3709708ba6eed24 100644 --- a/mo/web/templates/org_round.html +++ b/mo/web/templates/org_round.html @@ -92,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> @@ -114,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 %} diff --git a/mo/web/templates/org_score.html b/mo/web/templates/org_score.html index 8220ad36f33b6e58fd839288f2ad6dd5c915b3f7..8e0b6e98dc2997afa4f9b8c26d2e3e0d98ebe1df 100644 --- a/mo/web/templates/org_score.html +++ b/mo/web/templates/org_score.html @@ -1,7 +1,7 @@ {% 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") }} diff --git a/mo/web/templates/parts/org_participants_table_actions.html b/mo/web/templates/parts/org_participants_table_actions.html index 1e27b5f97e2ea0c4b696bf50d0bcc7385e72521e..e7097f7b439b5933a2254896a1420d42b1dcd022 100644 --- a/mo/web/templates/parts/org_participants_table_actions.html +++ b/mo/web/templates/parts/org_participants_table_actions.html @@ -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 %}