feat: support penalty config
All checks were successful
build / trigger-build-image (push) Successful in 13s
All checks were successful
build / trigger-build-image (push) Successful in 13s
This commit is contained in:
parent
07ef6cd5d8
commit
c48bc1a304
|
@ -307,6 +307,15 @@ def joj3_all_env(
|
|||
"#795548",
|
||||
help="label color for the issue created by this command",
|
||||
),
|
||||
end_time: Optional[datetime] = Option(None),
|
||||
penalty_config: str = Option(
|
||||
"",
|
||||
help=(
|
||||
"Configuration for penalties in the format "
|
||||
"'hours=factor'. "
|
||||
"Example: --penalty-config 24=0.75,48=0.5"
|
||||
),
|
||||
),
|
||||
) -> None:
|
||||
app.pretty_exceptions_enable = False
|
||||
set_settings(Settings(_env_file=env_path))
|
||||
|
@ -322,6 +331,7 @@ def joj3_all_env(
|
|||
logger.error("missing required env var")
|
||||
raise Exit(code=1)
|
||||
submitter_repo_name = env.github_repository.split("/")[-1]
|
||||
penalty_factor = joj3.get_penalty_factor(end_time, penalty_config)
|
||||
total_score = joj3.get_total_score(env.joj3_output_path)
|
||||
res = {
|
||||
"totalScore": total_score,
|
||||
|
@ -350,6 +360,7 @@ def joj3_all_env(
|
|||
submitter_repo_name,
|
||||
issue_label_name,
|
||||
issue_label_color,
|
||||
penalty_factor,
|
||||
)
|
||||
res["issue"] = issue_number
|
||||
gitea_issue_url = f"{submitter_repo_url}/issues/{issue_number}"
|
||||
|
@ -462,6 +473,14 @@ def joj3_check_env(
|
|||
),
|
||||
begin_time: Optional[datetime] = Option(None),
|
||||
end_time: Optional[datetime] = Option(None),
|
||||
penalty_config: str = Option(
|
||||
"",
|
||||
help=(
|
||||
"Configuration for penalties in the format "
|
||||
"'hours=factor'. "
|
||||
"Example: --penalty-config 24=0.75,48=0.5"
|
||||
),
|
||||
),
|
||||
) -> None:
|
||||
app.pretty_exceptions_enable = False
|
||||
set_settings(Settings(_env_file=env_path))
|
||||
|
@ -477,6 +496,7 @@ def joj3_check_env(
|
|||
time_msg, time_failed = tea.pot.joj3_check_submission_time(
|
||||
begin_time,
|
||||
end_time,
|
||||
penalty_config,
|
||||
)
|
||||
count_msg, count_failed = tea.pot.joj3_check_submission_count(
|
||||
env, grading_repo_name, group_config, scoreboard_filename
|
||||
|
|
|
@ -240,6 +240,7 @@ class Teapot:
|
|||
submitter_repo_name: str,
|
||||
issue_label_name: str,
|
||||
issue_label_color: str,
|
||||
penalty_factor: float,
|
||||
) -> int:
|
||||
title, comment = joj3.generate_title_and_comment(
|
||||
env.joj3_output_path,
|
||||
|
@ -251,6 +252,7 @@ class Teapot:
|
|||
submitter_in_issue_title,
|
||||
env.joj3_run_id,
|
||||
max_total_score,
|
||||
penalty_factor,
|
||||
)
|
||||
title_prefix = joj3.get_title_prefix(
|
||||
env.joj3_conf_name, env.github_actor, submitter_in_issue_title
|
||||
|
@ -299,15 +301,43 @@ class Teapot:
|
|||
self,
|
||||
begin_time: Optional[datetime] = None,
|
||||
end_time: Optional[datetime] = None,
|
||||
penalty_config: str = "",
|
||||
) -> Tuple[str, bool]:
|
||||
now = datetime.now()
|
||||
if (begin_time and now < begin_time) or (end_time and now > end_time):
|
||||
return (
|
||||
"### Submission Time Check Failed\n"
|
||||
f"Current time {now} is not in the valid range "
|
||||
f"[{begin_time}, {end_time}].\n",
|
||||
True,
|
||||
)
|
||||
penalties = joj3.parse_penalty_config(penalty_config)
|
||||
if penalties and end_time:
|
||||
penalty_end_time = end_time + timedelta(hours=penalties[-1][0])
|
||||
if begin_time and now < begin_time:
|
||||
return (
|
||||
"### Submission Time Check Failed\n"
|
||||
f"Current time {now} is not in the valid range "
|
||||
f"[{begin_time}, {end_time}].\n",
|
||||
True,
|
||||
)
|
||||
elif now > penalty_end_time:
|
||||
return (
|
||||
"### Submission Time Check Failed\n"
|
||||
f"Current time {now} is not in the valid range "
|
||||
f"[{begin_time}, {end_time}], and the penalty range "
|
||||
f"[{end_time}, {penalty_end_time}].\n",
|
||||
True,
|
||||
)
|
||||
else:
|
||||
return (
|
||||
"### Submission Time Check Passed\n"
|
||||
f"Current time {now} is not in the valid range "
|
||||
f"[{begin_time}, {end_time}], but in the penalty range "
|
||||
f"[{end_time}, {penalty_end_time}].\n",
|
||||
False,
|
||||
)
|
||||
else:
|
||||
if (begin_time and now < begin_time) or (end_time and now > end_time):
|
||||
return (
|
||||
"### Submission Time Check Failed\n"
|
||||
f"Current time {now} is not in the valid range "
|
||||
f"[{begin_time}, {end_time}].\n",
|
||||
True,
|
||||
)
|
||||
return (
|
||||
"### Submission Time Check Passed\n"
|
||||
f"Current time {now} is in the valid range "
|
||||
|
|
|
@ -2,8 +2,8 @@ import bisect
|
|||
import csv
|
||||
import json
|
||||
import os
|
||||
from datetime import datetime
|
||||
from typing import Any, Dict, List, Tuple
|
||||
from datetime import datetime, timedelta
|
||||
from typing import Any, Dict, List, Optional, Tuple
|
||||
|
||||
from pydantic_settings import BaseSettings
|
||||
|
||||
|
@ -215,6 +215,7 @@ def generate_title_and_comment(
|
|||
submitter_in_title: bool = True,
|
||||
run_id: str = "unknown",
|
||||
max_total_score: int = -1,
|
||||
penalty_factor: float = 1.0,
|
||||
) -> Tuple[str, str]:
|
||||
with open(score_file_path) as json_file:
|
||||
stages: List[Dict[str, Any]] = json.load(json_file)
|
||||
|
@ -234,6 +235,8 @@ def generate_title_and_comment(
|
|||
"Powered by [JOJ3](https://github.com/joint-online-judge/JOJ3) and "
|
||||
"[Joint-Teapot](https://github.com/BoYanZh/Joint-Teapot) with ❤️.\n"
|
||||
)
|
||||
if penalty_factor != 1.0:
|
||||
comment += f"Note: The total score is multiplied by a penalty factor of {penalty_factor}.\n"
|
||||
for stage in stages:
|
||||
if all(
|
||||
result["score"] == 0 and result["comment"].strip() == ""
|
||||
|
@ -254,6 +257,8 @@ def generate_title_and_comment(
|
|||
comment += "</details>\n\n"
|
||||
total_score += result["score"]
|
||||
comment += "\n"
|
||||
if penalty_factor != 1.0:
|
||||
total_score = round(total_score * penalty_factor)
|
||||
title = get_title_prefix(exercise_name, submitter, submitter_in_title)
|
||||
if max_total_score >= 0:
|
||||
title += f"{total_score} / {max_total_score}"
|
||||
|
@ -281,3 +286,29 @@ def get_title_prefix(
|
|||
if not submitter_in_title:
|
||||
title = f"JOJ3 Result for {exercise_name} - Score: "
|
||||
return title
|
||||
|
||||
|
||||
def parse_penalty_config(penalty_config: str) -> List[Tuple[float, float]]:
|
||||
res = []
|
||||
for penalty in penalty_config.split(","):
|
||||
hour, factor = map(float, penalty.split("="))
|
||||
res.append((hour, factor))
|
||||
res.sort(key=lambda x: x[0])
|
||||
return res
|
||||
|
||||
|
||||
def get_penalty_factor(
|
||||
end_time: Optional[datetime],
|
||||
penalty_config: str,
|
||||
) -> float:
|
||||
if not end_time or not penalty_config:
|
||||
return 1.0
|
||||
penalties = parse_penalty_config(penalty_config)
|
||||
now = datetime.now()
|
||||
res = 0.0
|
||||
for hour, factor in penalties[::-1]:
|
||||
if now < end_time + timedelta(hours=hour):
|
||||
res = factor
|
||||
else:
|
||||
break
|
||||
return res
|
||||
|
|
Loading…
Reference in New Issue
Block a user