From c54518ed65041d5cc3a2ee8a6eafe2db6147fc36 Mon Sep 17 00:00:00 2001 From: Nils Pukropp Date: Mon, 5 Feb 2024 09:54:13 +0100 Subject: [PATCH] formatting of execises --- src/testing.py | 25 +++++++++++++++++++++---- src/tests/primes.py | 2 +- src/tests/test_testing.py | 8 ++++---- src/utils.py | 31 ++++++++++++++++++++++++++++++- 4 files changed, 56 insertions(+), 10 deletions(-) diff --git a/src/testing.py b/src/testing.py index bd89a94..6a69b84 100644 --- a/src/testing.py +++ b/src/testing.py @@ -1,6 +1,6 @@ import functools import multiprocessing -from typing import Callable, Iterator +from typing import Callable, Iterable, Iterator class TimeoutException(Exception): @@ -43,7 +43,7 @@ def run_tests_for_task(task: type) -> None: run_tests_for_task(task) -def points_to_detuct(e: type | Callable) -> int: +def points_to_deduct(e: type | Callable) -> int: match e: case type() if hasattr(e, 'is_task'): to_detuct = 0 @@ -51,7 +51,7 @@ def points_to_detuct(e: type | Callable) -> int: if hasattr(test, 'is_test'): to_detuct += test.to_deduct() for task in e.tasks: - to_detuct += points_to_detuct(task) + to_detuct += points_to_deduct(task) return e.max_points if to_detuct > e.max_points else to_detuct case Callable(test): return test.to_detuct() @@ -76,6 +76,23 @@ class Exercise(object): self.run_tests() self.deduct_points() + @property + def id(self) -> str: + return self.__id + + @property + def max_points(self) -> float: + return self.__max_points + + @property + def points(self) -> float: + return self.__points + + @property + def tasks(self) -> Iterable[object]: + return self.__tasks + + def run_tests(self): for task in self.__tasks: @@ -83,7 +100,7 @@ class Exercise(object): def deduct_points(self): for task in self.__tasks: - to_detuct = points_to_detuct(task) + to_detuct = points_to_deduct(task) task.points = 0 if to_detuct > task.max_points else task.max_points - to_detuct self.__points = functools.reduce( lambda a, b: a + b, map(lambda t: t.points, self.__tasks), 0.0) diff --git a/src/tests/primes.py b/src/tests/primes.py index 1d1e63c..2cf672d 100644 --- a/src/tests/primes.py +++ b/src/tests/primes.py @@ -1,5 +1,5 @@ def is_prime(n: int) -> bool: - if n < 2: + if n <= 2: return False for i in range(2, n // 2 + 1): diff --git a/src/tests/test_testing.py b/src/tests/test_testing.py index b175e9c..5d0bc16 100644 --- a/src/tests/test_testing.py +++ b/src/tests/test_testing.py @@ -1,4 +1,4 @@ -from utils import has_annotation_callable +from utils import has_annotation_callable, format from testing import eip_task, eip_test, Exercise PRIMES = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, @@ -11,7 +11,7 @@ MAX = 100 primes_exercise = Exercise("primes") -@eip_task("`is_prime`", 4, primes_exercise) +@eip_task("a) `is_prime`", 4, primes_exercise) class IsPrime: @eip_test("`is_prime` nicht implementiert", 4) @@ -36,7 +36,7 @@ class IsPrime: has_annotation_callable(is_prime, [int], bool) -@eip_task("`next_prime`", 6, primes_exercise) +@eip_task("b) `next_prime`", 6, primes_exercise) class NextPrime: @eip_test("`next_prime` nicht implementiert", 6) @@ -100,4 +100,4 @@ class PrimeFactorize: if __name__ == "__main__": primes_exercise.run() - print(f"# exercise ({primes_exercise.get_points():.2g} / {primes_exercise.get_max_points()})") + print("\n".join(format(primes_exercise))) diff --git a/src/utils.py b/src/utils.py index b247768..399a500 100644 --- a/src/utils.py +++ b/src/utils.py @@ -1,5 +1,9 @@ from typing import Any, Callable, TypeAliasType +from more_itertools import flatten + +from testing import Exercise, points_to_deduct + def has_annotation_callable(f: Callable, param_types: list[type], return_type: object = None) -> bool: a = f.__annotations__ ret = a['return'] if 'return' in a else None @@ -32,4 +36,29 @@ def format_callable(f: Callable[..., Any]) -> str: def assert_annotation_callable(f: Callable[..., Any], param_types: list[type], return_type: object = None): - assert has_annotation_callable(f, param_types, return_type), f"{f.__name__}({", ".join(format_type(p) for p in param_types)}) -> {format_type(return_type)} != {format_callable(f)}" \ No newline at end of file + assert has_annotation_callable(f, param_types, return_type), f"{f.__name__}({", ".join(format_type(p) for p in param_types)}) -> {format_type(return_type)} != {format_callable(f)}" + +def format(ob: Exercise | object | Callable[..., Any]) -> list[str]: + match ob: + case Exercise(): + out = [f"# {ob.id} ({ob.points:.2g} / {ob.max_points:.2g})"] + return out + list(flatten(map(format, ob.tasks))) + case object() if hasattr(ob, 'is_task'): + return format_task("##", ob) + case Callable() if hasattr(ob, 'is_test'): + return format_test(ob) + case _: + return [] + +def format_test(test: Callable[..., Any]) -> list[str]: + if not test.has_failed: + return [] + return [f"- {test.msg} [`-{test.to_deduct():.2g}p`]"] + +def format_task(prefix: str, task: object) -> list[str]: + out = [f"{prefix} {task.header} - ({(task.max_points - points_to_deduct(task)):.2g} / {task.max_points:.2g})"] + for t in task.tasks: + out += format_task(prefix + "#", t) + for t in task.tests: + out += format_test(t) + return out \ No newline at end of file