formatting of execises

This commit is contained in:
2024-02-05 09:54:13 +01:00
parent e17d23f92a
commit c54518ed65
4 changed files with 56 additions and 10 deletions

View File

@ -1,6 +1,6 @@
import functools import functools
import multiprocessing import multiprocessing
from typing import Callable, Iterator from typing import Callable, Iterable, Iterator
class TimeoutException(Exception): class TimeoutException(Exception):
@ -43,7 +43,7 @@ def run_tests_for_task(task: type) -> None:
run_tests_for_task(task) run_tests_for_task(task)
def points_to_detuct(e: type | Callable) -> int: def points_to_deduct(e: type | Callable) -> int:
match e: match e:
case type() if hasattr(e, 'is_task'): case type() if hasattr(e, 'is_task'):
to_detuct = 0 to_detuct = 0
@ -51,7 +51,7 @@ def points_to_detuct(e: type | Callable) -> int:
if hasattr(test, 'is_test'): if hasattr(test, 'is_test'):
to_detuct += test.to_deduct() to_detuct += test.to_deduct()
for task in e.tasks: 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 return e.max_points if to_detuct > e.max_points else to_detuct
case Callable(test): case Callable(test):
return test.to_detuct() return test.to_detuct()
@ -76,6 +76,23 @@ class Exercise(object):
self.run_tests() self.run_tests()
self.deduct_points() 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): def run_tests(self):
for task in self.__tasks: for task in self.__tasks:
@ -83,7 +100,7 @@ class Exercise(object):
def deduct_points(self): def deduct_points(self):
for task in self.__tasks: 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 task.points = 0 if to_detuct > task.max_points else task.max_points - to_detuct
self.__points = functools.reduce( self.__points = functools.reduce(
lambda a, b: a + b, map(lambda t: t.points, self.__tasks), 0.0) lambda a, b: a + b, map(lambda t: t.points, self.__tasks), 0.0)

View File

@ -1,5 +1,5 @@
def is_prime(n: int) -> bool: def is_prime(n: int) -> bool:
if n < 2: if n <= 2:
return False return False
for i in range(2, n // 2 + 1): for i in range(2, n // 2 + 1):

View File

@ -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 from testing import eip_task, eip_test, Exercise
PRIMES = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, PRIMES = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37,
@ -11,7 +11,7 @@ MAX = 100
primes_exercise = Exercise("primes") primes_exercise = Exercise("primes")
@eip_task("`is_prime`", 4, primes_exercise) @eip_task("a) `is_prime`", 4, primes_exercise)
class IsPrime: class IsPrime:
@eip_test("`is_prime` nicht implementiert", 4) @eip_test("`is_prime` nicht implementiert", 4)
@ -36,7 +36,7 @@ class IsPrime:
has_annotation_callable(is_prime, [int], bool) has_annotation_callable(is_prime, [int], bool)
@eip_task("`next_prime`", 6, primes_exercise) @eip_task("b) `next_prime`", 6, primes_exercise)
class NextPrime: class NextPrime:
@eip_test("`next_prime` nicht implementiert", 6) @eip_test("`next_prime` nicht implementiert", 6)
@ -100,4 +100,4 @@ class PrimeFactorize:
if __name__ == "__main__": if __name__ == "__main__":
primes_exercise.run() primes_exercise.run()
print(f"# exercise ({primes_exercise.get_points():.2g} / {primes_exercise.get_max_points()})") print("\n".join(format(primes_exercise)))

View File

@ -1,5 +1,9 @@
from typing import Any, Callable, TypeAliasType 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: def has_annotation_callable(f: Callable, param_types: list[type], return_type: object = None) -> bool:
a = f.__annotations__ a = f.__annotations__
ret = a['return'] if 'return' in a else None ret = a['return'] if 'return' in a else None
@ -33,3 +37,28 @@ def format_callable(f: Callable[..., Any]) -> str:
def assert_annotation_callable(f: Callable[..., Any], param_types: list[type], return_type: object = None): 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)}" 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