Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Наработки для маркированной трассы со штрафными кругами #421

Merged
merged 9 commits into from
Dec 20, 2023
33 changes: 33 additions & 0 deletions languages/ru_RU/LC_MESSAGES/sportorg.po
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,9 @@ msgstr "Финиш"
msgid "Penalty"
msgstr "Штраф"

msgid "Penalty laps"
msgstr "Штрафные круги"

msgid "Penalty legs"
msgstr "Штраф, круги"

Expand Down Expand Up @@ -648,18 +651,45 @@ msgstr "Штраф за каждую просроченную минуту"
msgid "no penalty"
msgstr "штраф не начисляется"

msgid "No penalty"
msgstr "Штраф не начисляется"

msgid "penalty time"
msgstr "штрафное время"

msgid "Penalty calculation mode: penalty time"
msgstr "Режим начисления штрафа: штрафное время"

msgid "penalty laps"
msgstr "штрафные круги"

msgid "Penalty calculation mode: penalty laps"
msgstr "Режим начисления штрафа: штрафные круги"

msgid "counting lap"
msgstr "оценочный круг"

msgid "Operating mode: evaluation point"
msgstr "Режим работы: оценочный круг"

msgid "Print number of penalty laps instead of splits"
msgstr "При считывании печатается распечатка"

msgid "when competitor reads out his card"
msgstr "с количеством штрафных кругов"

msgid "lap station"
msgstr "станция на штрафном круге"

msgid "Station number on the penalty lap"
msgstr "Номер станции на штрафном круге"

msgid "Competitor must punch at station"
msgstr "Спортсмен должен отметиться на станции"

msgid "every time he/she passes a penalty lap"
msgstr "при каждом прохождении штрафного круга"

msgid "scores off"
msgstr "очки не рассчитываются"

Expand Down Expand Up @@ -1515,6 +1545,9 @@ msgstr "СОШЕЛ"
msgid "Disqualified"
msgstr "ДИСКВ."

msgid "laps"
msgstr "кр."

msgid "contain"
msgstr "содержит"

Expand Down
1 change: 1 addition & 0 deletions sportorg/gui/dialogs/result_edit.py
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,7 @@ def apply_changes_impl(self):
try:
ResultChecker.checking(result)
sergeikobelev marked this conversation as resolved.
Show resolved Hide resolved
ResultChecker.calculate_penalty(result)
ResultChecker.checking(result)
if result.person and result.person.group:
GroupSplits(race(), result.person.group).generate(True)
except ResultCheckerException as e:
Expand Down
62 changes: 58 additions & 4 deletions sportorg/gui/dialogs/timekeeping_properties.py
Original file line number Diff line number Diff line change
Expand Up @@ -183,24 +183,52 @@ def init_ui(self):

self.result_proc_tab.setLayout(self.result_proc_layout)

# marked route settings
# marked route penalty calculation settings
self.marked_route_tab = QWidget()
self.mr_layout = QFormLayout()
self.mr_off_radio = QRadioButton(translate('no penalty'))
self.mr_off_radio.setToolTip(translate('No penalty'))
self.mr_off_radio.toggled.connect(self.penalty_calculation_mode)
self.mr_layout.addRow(self.mr_off_radio)
self.mr_time_radio = QRadioButton(translate('penalty time'))
self.mr_time_radio.setToolTip(
translate('Penalty calculation mode: penalty time')
)
self.mr_time_radio.toggled.connect(self.penalty_calculation_mode)
self.mr_time_edit = AdvTimeEdit(display_format=self.time_format)
self.mr_layout.addRow(self.mr_time_radio, self.mr_time_edit)
self.mr_laps_radio = QRadioButton(translate('penalty laps'))
self.mr_laps_radio.setToolTip(
translate('Penalty calculation mode: penalty laps')
)
self.mr_laps_radio.toggled.connect(self.penalty_calculation_mode)
self.mr_layout.addRow(self.mr_laps_radio)
self.mr_counting_lap_check = QCheckBox(translate('counting lap'))
self.mr_counting_lap_check.setToolTip(
translate('Operating mode: evaluation point')
+ '\n'
+ translate('Print number of penalty laps instead of splits')
+ '\n'
+ translate('when competitor reads out his card')
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Лучше использовать f строки вместо оператора +

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Также, кмк

Suggested change
+ translate('when competitor reads out his card')
+ translate('when a competitor reads out his card')

)
self.mr_counting_lap_check.stateChanged.connect(self.penalty_calculation_mode)
self.mr_layout.addRow(self.mr_counting_lap_check)
self.mr_lap_station_check = QCheckBox(translate('lap station'))
self.mr_lap_station_check.setToolTip(
translate('Station number on the penalty lap')
+ '\n'
+ translate('Competitor must punch at station')
+ '\n'
+ translate('every time he/she passes a penalty lap')
)
self.mr_lap_station_check.stateChanged.connect(self.penalty_calculation_mode)
self.mr_lap_station_edit = AdvSpinBox(max_width=50)
self.mr_layout.addRow(self.mr_lap_station_check, self.mr_lap_station_edit)
self.mr_dont_dqs_check = QCheckBox(translate("Don't disqualify"))
self.mr_dont_dqs_check.setToolTip(translate("Don't disqualify"))
self.mr_layout.addRow(self.mr_dont_dqs_check)
self.mr_max_penalty_by_cp = QCheckBox(translate('Max penalty = quantity of cp'))
self.mr_max_penalty_by_cp.setToolTip(translate('Max penalty = quantity of cp'))
self.mr_layout.addRow(self.mr_max_penalty_by_cp)
self.marked_route_tab.setLayout(self.mr_layout)

Expand Down Expand Up @@ -305,6 +333,32 @@ def on_assignment_mode(self):
self.chip_reading_box.setDisabled(mode)
self.chip_duplicate_box.setDisabled(mode)

def penalty_calculation_mode(self):
self.mr_time_edit.setDisabled(not self.mr_time_radio.isChecked())
self.mr_counting_lap_check.setDisabled(
not (
self.mr_laps_radio.isChecked()
and not self.mr_lap_station_check.isChecked()
sergeikobelev marked this conversation as resolved.
Show resolved Hide resolved
)
)
self.mr_lap_station_check.setDisabled(
not (
self.mr_laps_radio.isChecked()
and not self.mr_counting_lap_check.isChecked()
)
)
self.mr_lap_station_edit.setDisabled(
not (
self.mr_laps_radio.isChecked() and self.mr_lap_station_check.isChecked()
)
)
self.mr_dont_dqs_check.setDisabled(
not (self.mr_laps_radio.isChecked() or self.mr_time_radio.isChecked())
)
self.mr_max_penalty_by_cp.setDisabled(
not (self.mr_laps_radio.isChecked() or self.mr_time_radio.isChecked())
)

def set_values_from_model(self):
cur_race = race()
zero_time = cur_race.get_setting('system_zero_time', (8, 0, 0))
Expand Down Expand Up @@ -417,9 +471,9 @@ def set_values_from_model(self):
mr_penalty_time = OTime(
msec=obj.get_setting('marked_route_penalty_time', 60000)
)
mr_if_counting_lap = obj.get_setting('marked_route_if_counting_lap', True)
mr_if_counting_lap = obj.get_setting('marked_route_if_counting_lap', False)
mr_if_station_check = obj.get_setting('marked_route_if_station_check', False)
mr_station_code = obj.get_setting('marked_route_station_code', 80)
mr_station_code = obj.get_setting('marked_route_penalty_lap_station_code', 80)
mr_if_dont_dsq_check = obj.get_setting('marked_route_dont_dsq', False)
mr_if_max_penalty_by_cp = obj.get_setting(
'marked_route_max_penalty_by_cp', False
Expand Down Expand Up @@ -596,7 +650,7 @@ def apply_changes_impl(self):
obj.set_setting('marked_route_penalty_time', mr_penalty_time)
obj.set_setting('marked_route_if_counting_lap', mr_if_counting_lap)
obj.set_setting('marked_route_if_station_check', mr_if_station_check)
obj.set_setting('marked_route_station_code', mr_station_code)
obj.set_setting('marked_route_penalty_lap_station_code', mr_station_code)
sergeikobelev marked this conversation as resolved.
Show resolved Hide resolved
sergeikobelev marked this conversation as resolved.
Show resolved Hide resolved
obj.set_setting('marked_route_dont_dsq', mr_if_dont_dsq)
obj.set_setting('marked_route_max_penalty_by_cp', mr_if_max_penalty_by_cp)

Expand Down
2 changes: 2 additions & 0 deletions sportorg/gui/menu/actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -593,7 +593,9 @@ def execute(self):
logging.debug('Penalty calculation start')
for result in race().results:
if result.person:
ResultChecker.checking(result)
ResultChecker.calculate_penalty(result)
ResultChecker.checking(result)
logging.debug('Penalty calculation finish')
ResultCalculation(race()).process_results()
self.app.refresh()
Expand Down
14 changes: 14 additions & 0 deletions sportorg/libs/template/template.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,19 @@ def date(value, fmt=None):
return dateutil.parser.parse(value).strftime(fmt)


def plural(value, fmt=None):
Copy link
Collaborator

@daxartio daxartio Dec 10, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

это что-то зависимая от языка. Я бы исключал такие функции в главном коде, в самих шаблонах еще можно

if value is None:
return ''
if 5 <= abs(value) <= 19:
return 'ов'
elif abs(value) % 10 == 1:
return ''
elif abs(value) % 10 in (2, 3, 4):
return 'а'
else:
return 'ов'


def finalize(thing):
return thing if thing else ''

Expand All @@ -50,6 +63,7 @@ def get_text_from_template(searchpath: str, path: str, **kwargs):
env.filters['tohhmmss'] = to_hhmmss
env.filters['date'] = date
env.policies['json.dumps_kwargs']['ensure_ascii'] = False
env.filters['plural'] = plural
template = env.get_template(path)

return template.render(**kwargs)
1 change: 1 addition & 0 deletions sportorg/models/memory.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ class ResultStatus(_TitleType):
DID_NOT_ENTER = 14
CANCELLED = 15
RESTORED = 16
MISS_PENALTY_LAP = 17


class Organization(Model):
Expand Down
63 changes: 57 additions & 6 deletions sportorg/models/result/result_checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,15 @@ def checking(cls, result):
ResultStatus.OK,
ResultStatus.MISSING_PUNCH,
ResultStatus.OVERTIME,
ResultStatus.MISS_PENALTY_LAP,
]:
result.status = ResultStatus.OK
if not o.check_result(result):
result.status = ResultStatus.MISSING_PUNCH
result.status_comment = 'п.п.3.13.12.2'

elif not cls.check_penalty_laps(result):
result.status = ResultStatus.MISS_PENALTY_LAP
elif result.person.group and result.person.group.max_time.to_msec():
if result.get_result_otime() > result.person.group.max_time:
if race().get_setting('result_processing_mode', 'time') == 'time':
Expand All @@ -72,7 +75,7 @@ def check_all(cls):
ResultChecker.calculate_penalty(result)

@staticmethod
def calculate_penalty(result):
def calculate_penalty(result: Result):
mode = race().get_setting('marked_route_mode', 'off')
if mode == 'off':
return
Expand All @@ -89,16 +92,19 @@ def calculate_penalty(result):
return

controls = course.controls
splits = result.splits

if mode == 'laps' and race().get_setting('marked_route_if_station_check'):
lap_station = race().get_setting('marked_route_penalty_lap_station_code')
splits, _ = ResultChecker.detach_penalty_laps2(splits, lap_station)

if race().get_setting('marked_route_dont_dsq', False):
# free order, don't penalty for extra cp
penalty = ResultChecker.penalty_calculation_free_order(
result.splits, controls
)
penalty = ResultChecker.penalty_calculation_free_order(splits, controls)
else:
# marked route with penalty
penalty = ResultChecker.penalty_calculation(
result.splits, controls, check_existence=True
splits, controls, check_existence=True
)

if race().get_setting('marked_route_max_penalty_by_cp', False):
Expand Down Expand Up @@ -167,9 +173,12 @@ def penalty_calculation(splits, controls, check_existence=False):
// returns 1 if check_existence=True
```
"""

user_array = [i.code for i in splits]
origin_array = [i.get_number_code() for i in controls]
res = 0

# может дать 0 штрафа при мусоре в чипе
sergeikobelev marked this conversation as resolved.
Show resolved Hide resolved
if check_existence and len(user_array) < len(origin_array):
# add 1 penalty score for missing points
res = len(origin_array) - len(user_array)
Expand Down Expand Up @@ -232,6 +241,48 @@ def penalty_calculation_free_order(splits, controls):

return res

@staticmethod
def detach_penalty_laps(splits, lap_station):
if not splits:
return [], []
for idx, punch in enumerate(reversed(splits)):
if int(punch.code) != lap_station:
break
else:
idx = len(splits)
idx = len(splits) - idx
return splits[:idx], splits[idx:]

@staticmethod
def detach_penalty_laps2(splits, lap_station):
"""Walkaround: извлекает отметки на штрафной станции.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Надо удалить коммент. Или написать на английском

Наивный метод, надо учитывать, что штрафные КП должны относиться
к пункту оценки, а не появляться из неочищенного чипа"""
sergeikobelev marked this conversation as resolved.
Show resolved Hide resolved
if not splits:
return [], []
regular = [punch for punch in splits if int(punch.code) != lap_station]
penalty = [punch for punch in splits if int(punch.code) == lap_station]
return regular, penalty

@staticmethod
def check_penalty_laps(result):
assert isinstance(result, Result)

mode = race().get_setting('marked_route_mode', 'off')
check_laps = race().get_setting('marked_route_if_station_check')

if mode == 'laps' and check_laps:
lap_station = race().get_setting('marked_route_penalty_lap_station_code')
_, penalty_laps = ResultChecker.detach_penalty_laps2(
result.splits, lap_station
)
num_penalty_laps = len(penalty_laps)

if num_penalty_laps < result.penalty_laps:
return False

return True

@staticmethod
def get_control_score(code):
obj = race()
Expand Down Expand Up @@ -281,7 +332,7 @@ def marked_route_check_penalty_laps(result: Result):

mr_if_counting_lap = obj.get_setting('marked_route_if_counting_lap', False)
mr_if_station_check = obj.get_setting('marked_route_if_station_check', False)
mr_station_code = obj.get_setting('marked_route_station_code', 0)
mr_station_code = obj.get_setting('marked_route_penalty_lap_station_code', 0)

if mr_if_station_check and int(mr_station_code) > 0:
count_laps = 0
Expand Down
Loading