Compare commits

...

24 Commits

Author SHA1 Message Date
d785216396 feat: diff.output.xxx -> diff.xxx (#26)
All checks were successful
build / build (push) Successful in 1m38s
build / trigger-build-image (push) Successful in 8s
Reviewed-on: #26
Co-authored-by: Boming Zhang <bomingzh@sjtu.edu.cn>
Co-committed-by: Boming Zhang <bomingzh@sjtu.edu.cn>
2025-06-22 01:59:20 +08:00
8f1a82dc81
chore: remove unused field
All checks were successful
build / build (push) Successful in 3m11s
build / trigger-build-image (push) Successful in 18s
2025-06-21 12:25:58 -04:00
a49bc19b1e
chore: file_name -> filename
All checks were successful
build / build (push) Successful in 3m41s
build / trigger-build-image (push) Successful in 20s
2025-06-21 12:07:09 -04:00
1b4637d01c
chore: sort glob result for stable output
All checks were successful
build / build (push) Successful in 1m33s
build / trigger-build-image (push) Successful in 10s
2025-06-20 17:25:09 -04:00
875089cabd
fix: pdm run test
Some checks failed
build / build (push) Failing after 1m55s
build / trigger-build-image (push) Has been skipped
2025-06-20 17:18:00 -04:00
8d52febbc5
feat: new immutable dir (#25) 2025-06-20 17:07:24 -04:00
fd7d09e7b2
fix: more intuitive default score
All checks were successful
build / build (push) Successful in 2m4s
build / trigger-build-image (push) Successful in 9s
2025-06-20 11:54:51 -04:00
29952a9d2d
feat: support auto scoreboard name
All checks were successful
build / build (push) Successful in 1m56s
build / trigger-build-image (push) Successful in 9s
2025-06-20 04:01:45 -04:00
2bcf4f8c60
feat: support issue label config in repo.toml
All checks were successful
build / build (push) Successful in 1m49s
build / trigger-build-image (push) Successful in 11s
2025-06-20 03:40:16 -04:00
e2dc094263
feat: support penalty config
All checks were successful
build / build (push) Successful in 2m2s
build / trigger-build-image (push) Successful in 8s
2025-06-19 06:43:29 -04:00
42bbf3ce39
feat: warn on immutable file not found
All checks were successful
build / build (push) Successful in 1m42s
build / trigger-build-image (push) Successful in 9s
2025-06-18 10:14:29 -04:00
c6e2c63024
fix: cli time format
All checks were successful
build / build (push) Successful in 2m2s
build / trigger-build-image (push) Successful in 8s
2025-06-18 09:19:52 -04:00
cae592c5cf
test: update cases 2025-06-18 09:17:14 -04:00
52f82a4afc
feat: time.begin and time.end for teapot time check
All checks were successful
build / build (push) Successful in 1m52s
build / trigger-build-image (push) Successful in 8s
2025-06-18 09:13:34 -04:00
d8073e4eb6
fix: total score overwrite
All checks were successful
build / build (push) Successful in 1m52s
build / trigger-build-image (push) Successful in 10s
2025-06-17 02:15:10 -04:00
0b39aa7112
test: update cases 2025-06-17 02:05:40 -04:00
b46bc950f7
fix: optional max-total-score in repo.toml
Some checks failed
build / build (push) Failing after 1m48s
build / trigger-build-image (push) Has been skipped
2025-06-17 01:56:15 -04:00
340ba5d0c5
feat: more fields in result detail parser
All checks were successful
build / build (push) Successful in 1m54s
build / trigger-build-image (push) Successful in 13s
2025-06-17 01:42:25 -04:00
ed43414b86
refactor: simplify group str generation
All checks were successful
build / build (push) Successful in 1m51s
build / trigger-build-image (push) Successful in 8s
2025-06-16 10:09:29 -04:00
367a79850c
feat: support score in keyword parsers
All checks were successful
build / build (push) Successful in 1m47s
build / trigger-build-image (push) Successful in 10s
2025-06-15 10:49:54 -04:00
5404313199
feat: move teapot settings to repo.toml
All checks were successful
build / build (push) Successful in 1m43s
build / trigger-build-image (push) Successful in 8s
2025-06-15 02:05:15 -04:00
c6b833fbd5
fix: separated result status models
All checks were successful
build / build (push) Successful in 2m11s
build / trigger-build-image (push) Successful in 9s
2025-06-14 05:52:06 -04:00
b14d83c37b
fix: make it work for joj3-forge-convert
All checks were successful
build / build (push) Successful in 1m43s
build / trigger-build-image (push) Successful in 9s
2025-06-14 05:28:41 -04:00
2c1ad47f14
feat: get grading repo name from cwd
Some checks failed
build / build (push) Failing after 1m47s
build / trigger-build-image (push) Has been skipped
2025-06-14 05:21:35 -04:00
34 changed files with 419 additions and 129 deletions

View File

@ -23,9 +23,9 @@ jobs:
- name: PDM install dependencies
run: |
pdm install
- name: Lint
- name: All
run: |
pdm run lint
pdm run all
- name: Run
run: |
pdm run app --help

View File

@ -26,8 +26,8 @@ def convert_joj3_conf(repo_conf: repo.Config, task_conf: task.Config) -> result.
name=task_conf.task.name,
# exact folder difference specified by type
log_path=str(JOJ3_LOG_PATH),
expire_unix_timestamp=int(task_conf.release.end_time.timestamp()),
effective_unix_timestamp=int(task_conf.release.begin_time.timestamp()),
expire_unix_timestamp=0, # will be handled in the health check stage
effective_unix_timestamp=0, # will be handled in the health check stage
actor_csv_path=str(ACTOR_CSV_PATH), # students.csv position
max_total_score=(
repo_conf.max_total_score

View File

@ -1,8 +1,8 @@
import socket
import os
from pathlib import Path
from typing import List
from pydantic import AliasChoices, BaseModel, Field
from pydantic import AliasChoices, BaseModel, Field, model_validator
class Files(BaseModel):
@ -20,6 +20,18 @@ class Groups(BaseModel):
)
class Label(BaseModel):
name: str = "Kind/Testing"
color: str = "#795548"
class Issue(BaseModel):
label: Label = Label()
show_submitter: bool = Field(
True, validation_alias=AliasChoices("show-submitter", "show_submitter")
)
class Config(BaseModel):
max_size: float = Field(
10, ge=0, validation_alias=AliasChoices("max-size", "max_size")
@ -28,6 +40,10 @@ class Config(BaseModel):
sandbox_token: str = Field(
"", validation_alias=AliasChoices("sandbox-token", "sandbox_token")
)
gitea_token: str = Field(
"", validation_alias=AliasChoices("gitea-token", "gitea_token")
)
gitea_org: str = Field("", validation_alias=AliasChoices("gitea-org", "gitea_org"))
max_total_score: int = Field(
100, validation_alias=AliasChoices("max-total-score", "max_total_score")
)
@ -47,15 +63,24 @@ class Config(BaseModel):
root: Path = Path(".")
path: Path = Path("repo.toml")
grading_repo_name: str = Field(
f"{socket.gethostname().split('-')[0]}-joj",
"",
validation_alias=AliasChoices("grading-repo-name", "grading_repo_name"),
)
health_check_score: int = Field(
0, validation_alias=AliasChoices("health-check-score", "health_check_score")
)
submitter_in_issue_title: bool = Field(
True,
validation_alias=AliasChoices(
"submitter-in-issue-title", "submitter_in_issue_title"
),
issue: Issue = Issue()
immutable_path: Path = Field(
Path("immutable_files"),
validation_alias=AliasChoices("immutable-path", "immutable_path"),
)
@model_validator(mode="after")
def set_grading_repo_name_from_cwd(self) -> "Config":
if not self.grading_repo_name:
course_env = os.getenv("COURSE")
if course_env:
self.grading_repo_name = f"{course_env}-joj"
else:
self.grading_repo_name = Path.cwd().name
return self

View File

@ -177,6 +177,12 @@ class Config(BaseModel):
class DummyConfig(BaseModel):
score: int = 0
comment: Optional[str] = None
force_quit: Optional[bool] = Field(False, serialization_alias="forceQuit")
class ResultStatusConfig(BaseModel):
score: int = 0
comment: Optional[str] = None
force_quit_on_not_accepted: Optional[bool] = Field(
@ -186,7 +192,7 @@ class DummyConfig(BaseModel):
class DiffOutputConfig(BaseModel):
score: int = 100
file_name: str = Field("", serialization_alias="fileName")
filename: str = Field("", serialization_alias="filename")
answer_path: str = Field("", serialization_alias="answerPath")
compare_space: bool = Field(False, serialization_alias="compareSpace")
always_hide: bool = Field(False, serialization_alias="alwaysHide")
@ -217,6 +223,7 @@ class KeywordConfig(BaseModel):
class KeywordMatchConfig(BaseModel):
score: int = 0
matches: List[KeywordConfig] = []

View File

@ -1,7 +1,8 @@
import re
from datetime import datetime, timezone
from enum import Enum
from pathlib import Path
from typing import Any, Dict, List, Type
from typing import Any, Dict, List, Optional, Type
from pydantic import (
AliasChoices,
@ -38,6 +39,12 @@ class ParserResultDetail(BaseModel):
False, validation_alias=AliasChoices("proc-peak", "proc_peak")
) # Display peak process count
error: bool = False # Display error messages
code_block: bool = Field(
True, validation_alias=AliasChoices("code-block", "code_block")
) # Display file in code block
max_length: int = Field(
2048, validation_alias=AliasChoices("max-length", "max_length")
) # Max output length of each file
class ParserFile(BaseModel):
@ -58,7 +65,16 @@ class ParserDummy(BaseModel):
)
class ParserResultStatus(BaseModel):
comment: str = ""
score: int = 0
force_quit: bool = Field(
True, validation_alias=AliasChoices("force-quit", "force_quit")
)
class ParserKeyword(BaseModel):
score: int = 0
keyword: List[str] = []
weight: List[int] = []
@ -82,12 +98,39 @@ class ParserDiffOutputs(BaseModel):
class ParserDiff(BaseModel):
score: int = DEFAULT_CASE_SCORE
ignore_spaces: bool = Field(
True, validation_alias=AliasChoices("ignore-spaces", "ignore_spaces")
)
hide: bool = False
force_quit: bool = Field(
False, validation_alias=AliasChoices("force-quit", "force_quit")
)
max_length: int = Field(
2048, validation_alias=AliasChoices("max-length", "max_length")
)
max_lines: int = Field(50, validation_alias=AliasChoices("max-lines", "max_lines"))
hide_common_prefix: bool = Field(
False, validation_alias=AliasChoices("hide-common-prefix", "hide_common_prefix")
)
# remove below codes when migration is complete
output: ParserDiffOutputs = ParserDiffOutputs()
default_score: int = Field(
DEFAULT_CASE_SCORE,
validation_alias=AliasChoices("default-score", "default_score"),
)
@model_validator(mode="after")
def copy_output_fields(self) -> "ParserDiff":
if "default_score" in self.model_fields_set:
self.score = self.default_score
if not isinstance(self.output, ParserDiffOutputs):
return self
for field_name, field_value in self.output.model_dump().items():
if field_name in self.output.model_fields_set and hasattr(self, field_name):
setattr(self, field_name, field_value)
return self
class StageFiles(BaseModel):
import_: List[str] = Field([], validation_alias="import")
@ -141,7 +184,6 @@ class Case(BaseModel):
True, validation_alias=AliasChoices("copy-in-cwd", "copy_in_cwd")
)
limit: Limit = Limit()
score: int = 0
diff: ParserDiff = ParserDiff()
@ -151,8 +193,9 @@ class Stage(Case):
parsers: List[Parser] = [] # list of parsers
dummy: ParserDummy = ParserDummy()
result_status: ParserDummy = Field(
ParserDummy(), validation_alias=AliasChoices("result-status", "result_status")
result_status: ParserResultStatus = Field(
ParserResultStatus(),
validation_alias=AliasChoices("result-status", "result_status"),
)
keyword: ParserKeyword = ParserKeyword()
clangtidy: ParserKeyword = ParserKeyword()
@ -164,6 +207,7 @@ class Stage(Case):
validation_alias=AliasChoices("result-detail", "result_detail"),
)
file: ParserFile = ParserFile()
# diff: ParserDiff = ParserDiff() # inherited from Case
cases: Dict[str, Case] = {}
@ -195,17 +239,37 @@ class Release(BaseModel):
) # timestamp = 0, no begin time
class SubmissionTime(BaseModel):
begin: Optional[datetime] = None
end: Optional[datetime] = None
class Task(BaseModel):
name: str = "unknown"
class Penalties(BaseModel):
hours: List[float] = []
factors: List[float] = []
class Config(BaseModel):
root: Path = Path(".")
path: Path = Path("task.toml")
task: Task = Task() # Task name (e.g., hw3 ex5)
time: SubmissionTime = SubmissionTime() # Valid time configuration
release: Release = Release() # Release configuration
stages: List[Stage] = [] # list of stage configurations
groups: Groups = Groups()
max_total_score: int = Field(
100, validation_alias=AliasChoices("max-total-score", "max_total_score")
penalties: Penalties = Penalties()
max_total_score: Optional[int] = Field(
None, validation_alias=AliasChoices("max-total-score", "max_total_score")
)
scoreboard: str = "scoreboard.csv"
@model_validator(mode="after")
def set_scoreboard(self) -> "Config":
if self.scoreboard == "auto":
suffix = re.split(r"[-_/\s]+", self.task.name)[0]
self.scoreboard = f"scoreboard-{suffix}.csv"
return self

View File

@ -1,9 +1,26 @@
import hashlib
from pathlib import Path
from typing import List
from typing import List, Tuple
from joj3_config_generator.models import common, repo, result, task
from joj3_config_generator.models.const import TEAPOT_CONFIG_ROOT, TEAPOT_LOG_PATH
from joj3_config_generator.models.const import (
CACHE_ROOT,
TEAPOT_CONFIG_ROOT,
TEAPOT_LOG_PATH,
)
from joj3_config_generator.utils.logger import logger
def get_teapot_env(repo_conf: repo.Config) -> List[str]:
res = [
f"REPOS_DIR={CACHE_ROOT}",
f"LOG_FILE_PATH={TEAPOT_LOG_PATH}",
]
if repo_conf.gitea_org:
res.append(f"GITEA_ORG_NAME={repo_conf.gitea_org}")
if repo_conf.gitea_token:
res.append(f"GITEA_ACCESS_TOKEN={repo_conf.gitea_token}")
return res
def get_teapot_post_stage(
@ -17,13 +34,34 @@ def get_teapot_post_stage(
repo_conf.grading_repo_name,
"--max-total-score",
(
str(repo_conf.max_total_score)
if not task_conf.max_total_score
else str(task_conf.max_total_score)
str(task_conf.max_total_score)
if task_conf.max_total_score is not None
else str(repo_conf.max_total_score)
),
"--issue-label-name",
repo_conf.issue.label.name,
"--issue-label-color",
repo_conf.issue.label.color,
"--scoreboard-filename",
task_conf.scoreboard,
]
if not repo_conf.submitter_in_issue_title:
if not repo_conf.issue.show_submitter:
args.append("--no-submitter-in-issue-title")
if task_conf.time.end:
args.extend(
[
"--end-time",
task_conf.time.end.strftime("%Y-%m-%dT%H:%M:%S"),
]
)
if task_conf.penalties.hours:
penalty_config = ",".join(
f"{hour}={factor}"
for hour, factor in zip(
task_conf.penalties.hours, task_conf.penalties.factors
)
)
args.extend(["--penalty-config", penalty_config])
stage_conf = result.StageDetail(
name="teapot",
@ -32,7 +70,7 @@ def get_teapot_post_stage(
with_=result.ExecutorWith(
default=result.Cmd(
args=args,
env=[f"LOG_FILE_PATH={TEAPOT_LOG_PATH}"],
env=get_teapot_env(repo_conf),
cpu_limit=common.Time("30s"),
clock_limit=common.Time("60s"),
),
@ -47,14 +85,45 @@ def get_teapot_post_stage(
return stage_conf
def get_check_lists(repo_conf: repo.Config) -> Tuple[List[str], List[str]]:
base_dir = (repo_conf.root / repo_conf.path).parent
immutable_dir = base_dir / "immutable_files"
immutable_files = []
file_sums = []
file_names = []
for file in repo_conf.files.immutable:
file_path = immutable_dir / Path(file).name
if not file_path.exists():
logger.warning(f"Immutable file not found: {file_path}")
continue
immutable_files.append(file_path)
file_sums.append(calc_sha256sum(file_path))
file_names.append(file)
immutable_dir = (repo_conf.root / repo_conf.path).parent / repo_conf.immutable_path
if not immutable_dir.exists():
return file_sums, file_names
file_sums = []
file_names = []
for file_path in sorted(immutable_dir.glob("**/*")):
if file_path.is_dir():
continue
if not file_path.exists():
logger.warning(f"Immutable file not found: {file_path}")
continue
file_sums.append(calc_sha256sum(file_path))
file_names.append(file_path.relative_to(immutable_dir).as_posix())
return file_sums, file_names
def get_health_check_args(repo_conf: repo.Config) -> List[str]:
file_sums, file_names = get_check_lists(repo_conf)
return [
"/usr/local/bin/repo-health-checker",
"-root=.",
f"-repoSize={str(repo_conf.max_size)}",
*[f"-meta={meta}" for meta in repo_conf.files.required],
f"-checkFileSumList={','.join(get_hashes(repo_conf))}",
f"-checkFileNameList={','.join(repo_conf.files.immutable)}",
f"-checkFileSumList={','.join(file_sums)}",
f"-checkFileNameList={','.join(file_names)}",
]
@ -65,28 +134,34 @@ def get_teapot_check_args(repo_conf: repo.Config, task_conf: task.Config) -> Lis
str(TEAPOT_CONFIG_ROOT / "teapot.env"),
"--grading-repo-name",
repo_conf.grading_repo_name,
"--scoreboard-filename",
task_conf.scoreboard,
]
if repo_conf.groups.name:
group_config_str = ",".join(
group_str = lambda groups: ",".join(
f"{name}={max_count}:{time_period}"
for name, max_count, time_period in zip(
repo_conf.groups.name,
repo_conf.groups.max_count,
repo_conf.groups.time_period_hour,
groups.name,
groups.max_count,
groups.time_period_hour,
)
)
if task_conf.groups.name:
overwrite_group_config_str = ",".join(
f"{name}={max_count}:{time_period}"
for name, max_count, time_period in zip(
task_conf.groups.name,
task_conf.groups.max_count,
task_conf.groups.time_period_hour,
group_config = group_str(
task_conf.groups if task_conf.groups.name else repo_conf.groups
)
res.extend(["--group-config", group_config])
if task_conf.time.begin:
res.extend(["--begin-time", task_conf.time.begin.strftime("%Y-%m-%dT%H:%M:%S")])
if task_conf.time.end:
res.extend(["--end-time", task_conf.time.end.strftime("%Y-%m-%dT%H:%M:%S")])
if task_conf.penalties.hours:
penalty_config = ",".join(
f"{hour}={factor}"
for hour, factor in zip(
task_conf.penalties.hours, task_conf.penalties.factors
)
)
res.extend(["--group-config", overwrite_group_config_str])
else:
res.extend(["--group-config", group_config_str])
res.extend(["--penalty-config", penalty_config])
return res
@ -100,7 +175,8 @@ def get_health_check_stage(
name="local",
with_=result.ExecutorWith(
default=result.Cmd(
cpu_limit=common.Time("10s"), clock_limit=common.Time("20s")
cpu_limit=common.Time("10s"),
clock_limit=common.Time("20s"),
),
cases=[
result.OptionalCmd(
@ -108,7 +184,7 @@ def get_health_check_stage(
),
result.OptionalCmd(
args=get_teapot_check_args(repo_conf, task_conf),
env=[f"LOG_FILE_PATH={TEAPOT_LOG_PATH}"],
env=get_teapot_env(repo_conf),
),
],
),
@ -130,12 +206,3 @@ def calc_sha256sum(file_path: Path) -> str:
for byte_block in iter(lambda: f.read(64 * 1024), b""):
sha256_hash.update(byte_block)
return sha256_hash.hexdigest()
def get_hashes(repo_conf: repo.Config) -> List[str]:
base_dir = (repo_conf.root / repo_conf.path).parent
immutable_dir = base_dir / "immutable_files"
immutable_files = [
immutable_dir / Path(file).name for file in repo_conf.files.immutable
]
return [calc_sha256sum(file) for file in immutable_files]

View File

@ -61,7 +61,7 @@ def get_parser_handler_map(
task.Parser.CPPLINT: (fix_keyword, task_stage.cpplint),
task.Parser.RESULT_DETAIL: (fix_result_detail, task_stage.result_detail),
task.Parser.DUMMY: (fix_dummy, task_stage.dummy),
task.Parser.RESULT_STATUS: (fix_dummy, task_stage.result_status),
task.Parser.RESULT_STATUS: (fix_result_status, task_stage.result_status),
task.Parser.FILE: (fix_file, task_stage.file),
task.Parser.DIFF: (
partial(
@ -124,10 +124,11 @@ def fix_keyword(
for keyword, score in zip(keyword_config.keyword, keyword_config.weight):
score_groups.setdefault(score, []).append(keyword)
keyword_parser.with_ = result.KeywordMatchConfig(
score=keyword_config.score,
matches=[
result.KeywordConfig(keywords=keywords, score=score)
for score, keywords in score_groups.items()
]
],
)
@ -150,17 +151,29 @@ def fix_result_detail(
show_memory=result_detail_parser_config.mem,
show_error=result_detail_parser_config.error,
show_proc_peak=result_detail_parser_config.proc_peak,
files_in_code_block=result_detail_parser_config.code_block,
max_file_length=result_detail_parser_config.max_length,
)
def fix_dummy(
dummy_parser_config: task.ParserDummy, dummy_parser: result.Parser
) -> None:
# we don't use dummy parser in real application
dummy_parser.with_ = result.DummyConfig(
score=dummy_parser_config.score,
comment=dummy_parser_config.comment,
force_quit_on_not_accepted=dummy_parser_config.force_quit,
force_quit=dummy_parser_config.force_quit,
)
def fix_result_status(
result_status_parser_config: task.ParserResultStatus,
result_status_parser: result.Parser,
) -> None:
result_status_parser.with_ = result.ResultStatusConfig(
score=result_status_parser_config.score,
comment=result_status_parser_config.comment,
force_quit_on_not_accepted=result_status_parser_config.force_quit,
)
@ -215,18 +228,24 @@ def fix_diff(
if cmd.proc_limit == executor.with_.default.proc_limit:
cmd.proc_limit = None
stage_cases.append(cmd)
def get_diff_attribute(attribute_name: str) -> Any:
if case.diff and attribute_name in case.diff.model_fields_set:
return getattr(case.diff, attribute_name)
return getattr(task_stage.diff, attribute_name)
parser_case = result.DiffCasesConfig(
outputs=[
result.DiffOutputConfig(
score=case.diff.output.score,
file_name="stdout",
score=get_diff_attribute("score"),
filename="stdout",
answer_path=stdout,
force_quit_on_diff=case.diff.output.force_quit,
always_hide=case.diff.output.hide,
compare_space=not case.diff.output.ignore_spaces,
max_diff_length=case.diff.output.max_length,
max_diff_lines=case.diff.output.max_lines,
hide_common_prefix=case.diff.output.hide_common_prefix,
compare_space=not get_diff_attribute("ignore_spaces"),
always_hide=get_diff_attribute("hide"),
force_quit_on_diff=get_diff_attribute("force_quit"),
max_diff_length=get_diff_attribute("max_length"),
max_diff_lines=get_diff_attribute("max_lines"),
hide_common_prefix=get_diff_attribute("hide_common_prefix"),
)
]
)
@ -234,18 +253,20 @@ def fix_diff(
for case_name in unspecified_cases:
cmd = result.OptionalCmd(
stdin=result.LocalFile(src=str(base_dir / f"{case_name}.in")),
cpu_limit=None,
clock_limit=None,
memory_limit=None,
proc_limit=None,
)
stage_cases.append(cmd)
parser_case = result.DiffCasesConfig(
outputs=[
result.DiffOutputConfig(
score=task_stage.diff.default_score,
file_name="stdout",
score=task_stage.diff.score,
filename="stdout",
answer_path=str(base_dir / f"{case_name}.out"),
compare_space=not task_stage.diff.ignore_spaces,
always_hide=task_stage.diff.hide,
force_quit_on_diff=task_stage.diff.force_quit,
max_diff_length=task_stage.diff.max_length,
max_diff_lines=task_stage.diff.max_lines,
hide_common_prefix=task_stage.diff.hide_common_prefix,
)
]
)

View File

View File

View File

View File

View File

@ -5,6 +5,8 @@ sandbox-token = "test"
max-total-score = 1000
max-size = 50.5
immutable_path = "immutable"
# for tests
[groups]
name = ["joj", "run"]

View File

@ -1,8 +1,8 @@
{
"name": "hw7 ex2",
"logPath": "/home/tt/.cache/joj3/joj3.log",
"expireUnixTimestamp": 1735574399,
"effectiveUnixTimestamp": 1735487999,
"expireUnixTimestamp": 0,
"effectiveUnixTimestamp": 0,
"actorCsvPath": "/home/tt/.config/joj/students.csv",
"maxTotalScore": 10245871,
"stage": {
@ -64,8 +64,8 @@
"-repoSize=50.5",
"-meta=README.md",
"-meta=Changelog.md",
"-checkFileSumList=a5b63323a692d3d8b952442969649b4f823d58dae26429494f613df160710dfc,b1bbad25b830db0a77b15a033f9ca1b7ab44c1d2d05056412bd3e4421645f0bf,2ba059f3977e2e3dee6cacbfbf0ba2578baa1b8e04b4977aec400868b6e49856,3db23f7fb2ca9814617e767ddc41b77073180b3b0b73e87b5f2a6d3129f88f3a",
"-checkFileNameList=.gitignore,.gitattributes,.gitea/workflows/push.yaml,.gitea/workflows/release.yaml"
"-checkFileSumList=b1bbad25b830db0a77b15a033f9ca1b7ab44c1d2d05056412bd3e4421645f0bf,2ba059f3977e2e3dee6cacbfbf0ba2578baa1b8e04b4977aec400868b6e49856,3db23f7fb2ca9814617e767ddc41b77073180b3b0b73e87b5f2a6d3129f88f3a,a5b63323a692d3d8b952442969649b4f823d58dae26429494f613df160710dfc",
"-checkFileNameList=.gitattributes,.gitea/workflows/push.yaml,.gitea/workflows/release.yaml,.gitignore"
]
},
{
@ -75,10 +75,17 @@
"/home/tt/.config/teapot/teapot.env",
"--grading-repo-name",
"ece280-joj",
"--scoreboard-filename",
"scoreboard-hw7.csv",
"--group-config",
"Manuel=500:24,Boming=501:48,Nuvole=502:72"
"Manuel=500:24,Boming=501:48,Nuvole=502:72",
"--end-time",
"2024-12-30T23:59:59",
"--penalty-config",
"24.0=0.5,48.0=0.25,72.0=0.1"
],
"env": [
"REPOS_DIR=/home/tt/.cache",
"LOG_FILE_PATH=/home/tt/.cache/joint-teapot-debug.log"
]
}
@ -260,6 +267,7 @@
{
"name": "keyword",
"with": {
"score": 0,
"matches": [
{
"keywords": [
@ -368,6 +376,7 @@
{
"name": "clangtidy",
"with": {
"score": 0,
"matches": [
{
"keywords": [
@ -498,12 +507,14 @@
{
"name": "keyword",
"with": {
"score": 0,
"matches": []
}
},
{
"name": "cppcheck",
"with": {
"score": 0,
"matches": [
{
"keywords": [
@ -526,6 +537,7 @@
{
"name": "clangtidy",
"with": {
"score": 0,
"matches": []
}
},
@ -551,6 +563,7 @@
{
"name": "cpplint",
"with": {
"score": 0,
"matches": []
}
},
@ -559,7 +572,7 @@
"with": {
"score": 0,
"comment": "",
"forceQuitOnNotAccepted": false
"forceQuitOnNotAccepted": true
}
},
{
@ -573,7 +586,7 @@
"with": {
"score": 0,
"comment": "",
"forceQuitOnNotAccepted": false
"forceQuit": false
}
},
{
@ -651,6 +664,7 @@
{
"name": "cpplint",
"with": {
"score": 0,
"matches": [
{
"keywords": [
@ -749,7 +763,27 @@
"dataSegmentLimit": false,
"addressSpaceLimit": false
},
"cases": []
"cases": [
{
"stdin": {
"content": ""
},
"cpuLimit": 500000000,
"clockLimit": 1000000000,
"memoryLimit": 5242880
},
{
"stdin": {
"content": ""
},
"memoryLimit": 5242880
},
{
"stdin": {
"content": ""
}
}
]
}
},
"parsers": [
@ -757,7 +791,53 @@
"name": "diff",
"with": {
"name": "diff",
"cases": []
"cases": [
{
"outputs": [
{
"score": 10,
"filename": "stdout",
"answerPath": "/home/tt/.config/joj/basic/cases/case0.out",
"compareSpace": false,
"alwaysHide": false,
"forceQuitOnDiff": false,
"maxDiffLength": 2048,
"maxDiffLines": 50,
"hideCommonPrefix": false
}
]
},
{
"outputs": [
{
"score": 5,
"filename": "stdout",
"answerPath": "/home/tt/.config/joj/basic/cases/case1.out",
"compareSpace": false,
"alwaysHide": false,
"forceQuitOnDiff": false,
"maxDiffLength": 2048,
"maxDiffLines": 50,
"hideCommonPrefix": false
}
]
},
{
"outputs": [
{
"score": 10,
"filename": "stdout",
"answerPath": "/home/tt/.config/joj/basic/cases/case2.out",
"compareSpace": false,
"alwaysHide": true,
"forceQuitOnDiff": false,
"maxDiffLength": 2048,
"maxDiffLines": 50,
"hideCommonPrefix": false
}
]
}
]
}
},
{
@ -798,9 +878,20 @@
"--grading-repo-name",
"ece280-joj",
"--max-total-score",
"10245871"
"10245871",
"--issue-label-name",
"Kind/Testing",
"--issue-label-color",
"#795548",
"--scoreboard-filename",
"scoreboard-hw7.csv",
"--end-time",
"2024-12-30T23:59:59",
"--penalty-config",
"24.0=0.5,48.0=0.25,72.0=0.1"
],
"env": [
"REPOS_DIR=/home/tt/.cache",
"LOG_FILE_PATH=/home/tt/.cache/joint-teapot-debug.log"
],
"stdin": {

View File

@ -2,8 +2,13 @@
task.name = "hw7 ex2" # task name
max-total-score = 10245871
release.end-time = 2024-12-30 23:59:59+08:00
time.end = 2024-12-30 23:59:59+08:00
release.begin-time = 2024-12-29 23:59:59+08:00
scoreboard = "auto"
[penalties]
hours = [ 24, 48, 72 ]
factors = [ 0.5, 0.25, 0.1 ]
[groups]
name = ["Manuel", "Boming", "Nuvole"]
@ -87,23 +92,26 @@ copy-in-cwd = false
files.import = [ "h7/build/ex2-asan" ]
limit.mem = "128m"
diff.score = 10
parsers = [ "diff", "result-detail" ]
result-detail.exit-status = true
result-detail.stderr = true
# will be removed as long as the name is fixed
case0.diff.output.score = 5
case0.limit.cpu = "0.5s"
case0.limit.mem = "5m"
case0.diff.output.ignore-spaces = true
case0.diff.ignore-spaces = true
#case0.limit.stdout = 8
#case0.command = "./h7/build/ex2"
case0.in = "case0.in"
case1.diff.output.score = 5
case1.diff.score = 5
case1.limit.cpu = "1s"
case1.limit.mem = "5m"
case1.diff.output.ignore-spaces = true
case1.diff.ignore-spaces = true
#case1.limit.stdout = 8
#case1.command = "./h7/build/ex2"
case1.in = "case1.in"
case2.diff.hide = true

View File

@ -1,8 +1,8 @@
{
"name": "hw7 ex2",
"logPath": "/home/tt/.cache/joj3/joj3.log",
"expireUnixTimestamp": 1735574399,
"effectiveUnixTimestamp": 1735487999,
"expireUnixTimestamp": 0,
"effectiveUnixTimestamp": 0,
"actorCsvPath": "/home/tt/.config/joj/students.csv",
"maxTotalScore": 100,
"stage": {
@ -78,6 +78,7 @@
{
"name": "clangtidy",
"with": {
"score": 0,
"matches": [
{
"keywords": [

View File

@ -1,7 +1,7 @@
# general task configuration
task.name = "hw7 ex2" # task name
release.end-time = 2024-12-30 23:59:59+08:00
time.end = 2024-12-30 23:59:59+08:00
release.begin-time = 2024-12-29 23:59:59+08:00
[[stages]]

View File

@ -1,8 +1,8 @@
{
"name": "hw7 ex2",
"logPath": "/home/tt/.cache/joj3/joj3.log",
"expireUnixTimestamp": 1735574399,
"effectiveUnixTimestamp": 1735487999,
"expireUnixTimestamp": 0,
"effectiveUnixTimestamp": 0,
"actorCsvPath": "/home/tt/.config/joj/students.csv",
"maxTotalScore": 100,
"stage": {
@ -71,6 +71,7 @@
{
"name": "cppcheck",
"with": {
"score": 0,
"matches": [
{
"keywords": [

View File

@ -1,8 +1,8 @@
# general task configuration
task.name = "hw7 ex2" # task name
release.end-time = 2024-12-30 23:59:59+08:00
release.begin-time = 2024-12-29 23:59:59+08:00
time.end = 2024-12-30 23:59:59+08:00
time.begin = 2024-12-29 23:59:59+08:00
[[stages]]
name = "[cq] Cppcheck"

View File

@ -1,8 +1,8 @@
{
"name": "hw7 ex2",
"logPath": "/home/tt/.cache/joj3/joj3.log",
"expireUnixTimestamp": 1735574399,
"effectiveUnixTimestamp": 1735487999,
"expireUnixTimestamp": 0,
"effectiveUnixTimestamp": 0,
"actorCsvPath": "/home/tt/.config/joj/students.csv",
"maxTotalScore": 100,
"stage": {
@ -70,6 +70,7 @@
{
"name": "cpplint",
"with": {
"score": 0,
"matches": [
{
"keywords": [

View File

@ -1,7 +1,7 @@
# general task configuration
task.name = "hw7 ex2" # task name
release.end-time = 2024-12-30 23:59:59+08:00
time.end = 2024-12-30 23:59:59+08:00
release.begin-time = 2024-12-29 23:59:59+08:00
[[stages]]

View File

@ -1,8 +1,8 @@
{
"name": "hw7 ex2",
"logPath": "/home/tt/.cache/joj3/joj3.log",
"expireUnixTimestamp": 1735574399,
"effectiveUnixTimestamp": 1735487999,
"expireUnixTimestamp": 0,
"effectiveUnixTimestamp": 0,
"actorCsvPath": "/home/tt/.config/joj/students.csv",
"maxTotalScore": 100,
"stage": {
@ -144,7 +144,7 @@
"outputs": [
{
"score": 5,
"fileName": "stdout",
"filename": "stdout",
"answerPath": "/home/tt/.config/joj/diff/case0.out",
"compareSpace": false,
"alwaysHide": false,
@ -159,7 +159,7 @@
"outputs": [
{
"score": 123214122421,
"fileName": "stdout",
"filename": "stdout",
"answerPath": "/home/tt/.config/joj/diff/case1.out",
"compareSpace": false,
"alwaysHide": false,
@ -174,7 +174,7 @@
"outputs": [
{
"score": 1232131,
"fileName": "stdout",
"filename": "stdout",
"answerPath": "/home/tt/.config/joj/diff/case9.out",
"compareSpace": false,
"alwaysHide": false,
@ -189,7 +189,7 @@
"outputs": [
{
"score": 92321,
"fileName": "stdout",
"filename": "stdout",
"answerPath": "/home/tt/.config/joj/diff/task1/subtask1/case11.out",
"compareSpace": false,
"alwaysHide": false,
@ -204,7 +204,7 @@
"outputs": [
{
"score": 823131,
"fileName": "stdout",
"filename": "stdout",
"answerPath": "/home/tt/.config/joj/diff/task1/subtask1/case10.out",
"compareSpace": false,
"alwaysHide": false,
@ -219,7 +219,7 @@
"outputs": [
{
"score": 2590,
"fileName": "stdout",
"filename": "stdout",
"answerPath": "/home/tt/.config/joj/diff/task1/case5.out",
"compareSpace": false,
"alwaysHide": false,
@ -234,7 +234,7 @@
"outputs": [
{
"score": 100,
"fileName": "stdout",
"filename": "stdout",
"answerPath": "/home/tt/.config/joj/diff/case2.out",
"compareSpace": false,
"alwaysHide": false,
@ -249,7 +249,7 @@
"outputs": [
{
"score": 100,
"fileName": "stdout",
"filename": "stdout",
"answerPath": "/home/tt/.config/joj/diff/task1/case4.out",
"compareSpace": false,
"alwaysHide": false,
@ -264,7 +264,7 @@
"outputs": [
{
"score": 100,
"fileName": "stdout",
"filename": "stdout",
"answerPath": "/home/tt/.config/joj/diff/task2/case6.out",
"compareSpace": false,
"alwaysHide": false,
@ -279,7 +279,7 @@
"outputs": [
{
"score": 100,
"fileName": "stdout",
"filename": "stdout",
"answerPath": "/home/tt/.config/joj/diff/task2/case7.out",
"compareSpace": false,
"alwaysHide": false,
@ -294,7 +294,7 @@
"outputs": [
{
"score": 100,
"fileName": "stdout",
"filename": "stdout",
"answerPath": "/home/tt/.config/joj/diff/task2/case8.out",
"compareSpace": false,
"alwaysHide": false,

View File

@ -1,7 +1,7 @@
# general task configuration
task.name = "hw7 ex2" # task name
release.end-time = 2024-12-30 23:59:59+08:00
time.end = 2024-12-30 23:59:59+08:00
release.begin-time = 2024-12-29 23:59:59+08:00
[[stages]]
@ -17,26 +17,26 @@ parsers = [ "diff", "result-detail" ]
result-detail.exit-status = true
result-detail.stderr = true
diff.default-score = 100
diff.score = 100
case0.diff.output.score = 5
case0.diff.score = 5
case0.limit.cpu = "1s"
case0.limit.mem = "2m"
case0.diff.output.ignore-spaces = true
case0.diff.ignore-spaces = true
case0.command = "./h7/build/ex2"
case0.in = "case0.in"
case1.diff.output.score = 123214122421
case1.diff.score = 123214122421
case1.limit.cpu = "2s"
case1.limit.mem = "4m"
case1.diff.output.ignore-spaces = true
case1.diff.ignore-spaces = true
case1.command = "./h7/build/ex2"
case9.diff.output.score = 1232131
case9.diff.score = 1232131
case9.limit.mem = "10m"
case11.diff.output.score = 92321
case11.diff.score = 92321
case10.diff.output.score = 823131
case10.diff.score = 823131
case5.diff.output.score = 2590
case5.diff.score = 2590

View File

@ -1,8 +1,8 @@
{
"name": "hw7 ex2",
"logPath": "/home/tt/.cache/joj3/joj3.log",
"expireUnixTimestamp": 1735574399,
"effectiveUnixTimestamp": 1735487999,
"expireUnixTimestamp": 0,
"effectiveUnixTimestamp": 0,
"actorCsvPath": "/home/tt/.config/joj/students.csv",
"maxTotalScore": 100,
"stage": {
@ -74,6 +74,7 @@
{
"name": "elf",
"with": {
"score": 0,
"matches": [
{
"keywords": [

View File

@ -1,7 +1,7 @@
# general task configuration
task.name = "hw7 ex2" # task name
release.end-time = 2024-12-30 23:59:59+08:00
time.end = 2024-12-30 23:59:59+08:00
release.begin-time = 2024-12-29 23:59:59+08:00
[[stages]]

View File

@ -1,8 +1,8 @@
{
"name": "hw7 ex2",
"logPath": "/home/tt/.cache/joj3/joj3.log",
"expireUnixTimestamp": 1735574399,
"effectiveUnixTimestamp": 1735487999,
"expireUnixTimestamp": 0,
"effectiveUnixTimestamp": 0,
"actorCsvPath": "/home/tt/.config/joj/students.csv",
"maxTotalScore": 100,
"stage": {
@ -73,6 +73,7 @@
{
"name": "keyword",
"with": {
"score": 0,
"matches": [
{
"keywords": [

View File

@ -1,7 +1,7 @@
# general task configuration
task.name = "hw7 ex2" # task name
release.end-time = 2024-12-30 23:59:59+08:00
time.end = 2024-12-30 23:59:59+08:00
release.begin-time = 2024-12-29 23:59:59+08:00
[[stages]]

View File

@ -1,8 +1,8 @@
{
"name": "hw7 ex2",
"logPath": "/home/tt/.cache/joj3/joj3.log",
"expireUnixTimestamp": 1735574399,
"effectiveUnixTimestamp": 1735487999,
"expireUnixTimestamp": 0,
"effectiveUnixTimestamp": 0,
"actorCsvPath": "/home/tt/.config/joj/students.csv",
"maxTotalScore": 100,
"stage": {

View File

@ -1,7 +1,7 @@
# general task configuration
task.name = "hw7 ex2" # task name
release.end-time = 2024-12-30 23:59:59+08:00
time.end = 2024-12-30 23:59:59+08:00
release.begin-time = 2024-12-29 23:59:59+08:00
[[stages]]

View File

0
tests/create/__init__.py Normal file
View File