Skip to content

Commit

Permalink
Add author override, change crifx.toml format
Browse files Browse the repository at this point in the history
  • Loading branch information
FinnLidbetter committed May 30, 2024
1 parent 8913a45 commit 5e83e6c
Show file tree
Hide file tree
Showing 9 changed files with 100 additions and 32 deletions.
1 change: 1 addition & 0 deletions crifx/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@


def _dir_path_argparse_type(path):
"""Check that the provided path is a directory."""
if os.path.isdir(path):
return path
else:
Expand Down
34 changes: 20 additions & 14 deletions crifx/config_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,10 +80,13 @@ class LanguageGroupConfig:
required_ac_count: int = 0

@staticmethod
def from_toml_dict(
toml_dict: dict[str, Any], group_identifier: str
) -> "LanguageGroupConfig":
def from_toml_dict(toml_dict: dict[str, Any]) -> "LanguageGroupConfig":
"""Initialize a LanguageGroupConfig from a toml dict."""
group_identifier = toml_dict.get("name")
if group_identifier is None:
raise ValueError(
"Language group in the `crifx.toml` file is missing a 'name'"
)
language_names = toml_dict.get("languages", [])
languages = []
for language_name in language_names:
Expand All @@ -110,13 +113,16 @@ class AliasGroup:
aliases: list[str]

@staticmethod
def from_toml_dict(
toml_dict: dict[str, Any], group_identifier: str
) -> "AliasGroup":
def from_toml_dict(toml_dict: dict[str, Any]) -> "AliasGroup":
"""Parse an AliasGroup from a toml dictionary."""
aliases = toml_dict.get("aliases", [])
primary_name = toml_dict.get("primary_name")
if primary_name is None:
raise ValueError(
"The `crifx.toml` file is missing a 'primary_name' for one or more 'judge' tables."
)
git_name = toml_dict.get("git_name")
return AliasGroup(group_identifier, git_name, aliases)
aliases = toml_dict.get("aliases", [])
return AliasGroup(primary_name, git_name, aliases)


class Config:
Expand All @@ -129,18 +135,18 @@ def __init__(self, toml_dict):
)
self.language_group_configs = []
self.alias_groups = []
language_groups = toml_dict.get("language_groups", {})
for group_identifier, language_group_dict in language_groups.items():
language_groups = toml_dict.get("language_group", [])
for language_group_dict in language_groups:
language_group_config = LanguageGroupConfig.from_toml_dict(
language_group_dict, group_identifier
language_group_dict,
)
if not language_group_config.language_group.languages:
# No languages parsed from the language group.
continue
self.language_group_configs.append(language_group_config)
alias_groups = toml_dict.get("aliases", {})
for identifier, alias_group_dict in alias_groups.items():
alias_group = AliasGroup.from_toml_dict(alias_group_dict, identifier)
alias_groups = toml_dict.get("judge", [])
for alias_group_dict in alias_groups:
alias_group = AliasGroup.from_toml_dict(alias_group_dict)
self.alias_groups.append(alias_group)
for alias_group_1, alias_group_2 in itertools.product(
self.alias_groups, self.alias_groups
Expand Down
39 changes: 30 additions & 9 deletions crifx/problemset_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import logging
import os
import re
import tomllib
from typing import Any

Expand All @@ -26,6 +27,7 @@

TEST_CASE_IMAGE_EXTENSIONS = ["png", "jpg", "jpeg"]
PROBLEM_REVIEW_STATUS_FILENAME = "crifx-problem-status.toml"
CRIFX_AUTHOR_PATTERN = "crifx!\(author=([a-zA-Z0-9_ ]+)\)"


class ProblemSetParser:
Expand Down Expand Up @@ -199,26 +201,45 @@ def _parse_submissions_dir(
if language is None:
continue
submission_path = os.path.join(submissions_dir, filename)
git_user_guess = self.git_manager.guess_file_author(submission_path)
filename_guess = self.guess_author_by_filename(filename)
if filename_guess is None:
judge = (
self.judges_by_name.get(getattr(git_user_guess, "name"))
or UNKNOWN_JUDGE
)
else:
judge = filename_guess
lines_of_code = 0
file_bytes = 0
author_name_override = None
try:
with open(submission_path, "r") as submission_file:
submission_lines = submission_file.readlines()
for line_number, line in enumerate(submission_lines):
author_match = re.search(CRIFX_AUTHOR_PATTERN, line)
if author_match is not None:
logging.debug(
"Found author override for file %s on line %d",
submission_path,
line_number + 1,
)
author_name_override = author_match.group(1)
break
lines_of_code = len(submission_file.readlines())
file_bytes = os.stat(submission_path).st_size
except (FileExistsError, FileNotFoundError, PermissionError):
logging.warning(
"Could not determine size of submission at path '%s'",
submission_path,
)
git_user_guess = self.git_manager.guess_file_author(submission_path)
filename_guess = self.guess_author_by_filename(filename)
if author_name_override is not None:
judge = UNKNOWN_JUDGE
for candidate_judge in self.judges_by_name.values():
if candidate_judge.has_alias(author_name_override):
judge = candidate_judge
break
elif filename_guess is not None:
judge = filename_guess

else:
judge = (
self.judges_by_name.get(getattr(git_user_guess, "name"))
or UNKNOWN_JUDGE
)
submission = Submission(
judge,
filename,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
#!/bin/python3

"""
This file uses 'jd' in the filename.
This is an alias defined for Jane Doe in the crifx.toml file.
"""

from sys import stdin

tokens = stdin.readline().split(" ")
Expand Down
29 changes: 22 additions & 7 deletions examples/example_problemset/crifx.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,21 +22,36 @@ validator_reviewers = 2
# There must be at least 2 judges who have reviewed the test data.
data_reviewers = 2

# Section for defining language groups.
[language_groups]
# Non alphanumeric characters can be used in the language group name crifx
# the language group name is written in double quotes "".
[language_groups."c/c++"]
# Define judge names in `[[judge]]` tables.
[[judge]]
# Primary name is required.
primary_name = "Jane Doe"
# git_name is not required.
# git_name = "janedoe"
# Aliases is a list of zero or more alternative names.
aliases = ["jd", "jane"]

[[judge]]
primary_name = "Homer Simpson"
git_name = "homers123"
aliases = []

# Tables for defining language groups.
[[language_group]]
# Non alphanumeric characters can be used in the language group name.
name = "c/c++"
# The names of the languages in the group. Language names are cast to
# lowercase before comparison to language names known to crifx.
languages = ["C", "C++"]
# The number of AC submissions required for this group.
required_ac_count = 0

[language_groups."java/kotlin"]
[[language_group]]
name = "java/kotlin"
languages = ["Java", "Kotlin"]
required_ac_count = 0

[language_groups.python]
[[language_group]]
name = "python"
languages = ["Python"]
required_ac_count = 0
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

# Use a crifx command to override the author for this submission instead
# of relying on git blame information.
# crifx!(author="Homer Simpson")
# crifx!(author=Homer Simpson)

from sys import stdin

Expand Down
3 changes: 3 additions & 0 deletions tests/scenarios/sample_contest_1/crifx.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[[judge]]
primary_name = "Jane Doe"
aliases = []
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
/**
* Example solution with a name override.
*
* crifx!(author=Jane Doe)
*/

public class hello_world {
public static void main(String[] args) {
System.out.println("Hello world!");
Expand Down
12 changes: 11 additions & 1 deletion tests/test_problemset_parsing.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import os

from crifx.config_parser import parse_config
from crifx.contest_objects import ProgrammingLanguage
from crifx.git_manager import GitManager
from crifx.problemset_parser import ProblemSetParser

Expand All @@ -10,7 +12,8 @@ def test_scenario_1(scenarios_path):
"""Test the basic scenario 1."""
path = os.path.join(scenarios_path, "sample_contest_1")
git_manager = GitManager(scenarios_path)
parser = ProblemSetParser(path, git_manager, [])
config = parse_config(path)
parser = ProblemSetParser(path, git_manager, config.alias_groups)
problemset = parser.parse_problemset()
problem_a = next(
problem for problem in problemset.problems if problem.name == "problem_a"
Expand All @@ -32,3 +35,10 @@ def test_scenario_1(scenarios_path):
assert len(problem_a.ac_submissions) == 2
assert len(problem_a.wa_submissions) == 1
assert len(problem_a.tle_submissions) == 0

problem_a_java_ac = next(
sub
for sub in problem_a.ac_submissions
if sub.language is ProgrammingLanguage.JAVA
)
assert problem_a_java_ac.author.primary_name == "Jane Doe"

0 comments on commit 5e83e6c

Please sign in to comment.