grapher/grader/valuation.py
2025-02-07 14:44:03 +01:00

198 lines
5.6 KiB
Python

from collections.abc import Sequence, Iterable, Mapping
from typing import Any
import inspect
from abc import ABC, abstractmethod
import weakref
from PIL import ImageColor
from colour import Color
PASSED: str = "#1AFE49"
FAILED: str = "#FF124F"
def hex_to_rgba(color: str) -> tuple:
return tuple([e/255 for e in ImageColor.getcolor(color, "RGBA")])
def gradient(color1: str, color2: str, num: int) -> list[tuple]:
c1, c2 = Color(color1), Color(color2)
colors = list(c2.range_to(c1, num))
colors = [hex_to_rgba(str(c)) for c in colors]
return colors
class BaseGrading(Mapping, ABC):
__instances: list[Mapping] = list()
def __init__(self, schema: dict[str, int | float], name=None, alt_name=None):
all_str = all(isinstance(k, str) for k in schema.keys())
assert all_str or all(isinstance(k, int) for k in schema.keys()), "Keys must be all of type (str, int)"
assert all(isinstance(v, float) for v in schema.values()), "All values must be floats in range(0,1)"
assert all(v <= 1 and v >= 0 for v in schema.values()), "All values must be floats in range(0,1)"
if all_str:
self.schema = dict()
for k, v in schema.items():
self.schema[k.title()] = v
else:
self.schema = schema
self.__class__.__instances.append(weakref.proxy(self))
self.name = name
self.alt_name = alt_name
def __getitem__(self, index):
if index >= len(self):
raise IndexError
return self.schema[index]
def __len__(self) -> int:
return len(self.schema)
def __contains__(self, item: int | str | float) -> bool:
if isinstance(item, (int, str)):
if isinstance(item, str):
item = item.title()
return item in self.schema
if isinstance(item, float):
return item <= 1 and item >= 0
return False
def __iter__(self) -> Iterable:
yield from self.schema
def __reversed__(self) -> Iterable:
yield from reversed(self.schema)
def __str__(self) -> str:
return self.name
def __repr__(self) -> str:
return f"<{self.name}: ({str(self.schema)})>"
def __eq__(self, other) -> bool:
if other == self:
return True
if isinstance(other, BaseEval):
return self.schema == other.schema
return NotImplemented
def __ne__(self, other) -> bool:
return not self.__eq__(other)
def get(self, key: int | str) -> float | None:
if isinstance(key, str):
key = key.title()
if key in self:
return self.schema[key]
return None
def keys(self) -> tuple:
return list(self.schema.keys())
def values(self) -> tuple:
return list(self.schema.values())
def items(self) -> list[tuple]:
return list(self.schema.items())
@abstractmethod
def has_passed(self, value: int | float, max: int | float) -> bool:
pass
@abstractmethod
def get_grade(self, value: int | float, max: int | float) -> str | int:
pass
@abstractmethod
def get_grade_color(self, value: int | float, max: int | float) -> tuple:
pass
@classmethod
def get_instances(cls):
yield from cls.__instances
@classmethod
def get_instance(cls, name: str):
for instance in cls.__instances:
if instance.alt_name == name:
return instance
get_gradings = lambda: BaseGrading.get_instances()
get_grader = lambda name: BaseGrading.get_instance(name)
class StdPercentRule(BaseGrading):
def has_passed(self, value: int | float, max: int | float) -> bool:
return value >= max * self.schema["Passed"]
def get_grade(self, value: int | float, max: int | float) -> str:
return "Passed" if self.has_passed(value, max) else "Not Passed"
def get_grade_color(self, value: int | float, max: int | float) -> tuple:
if self.has_passed(value, max):
return hex_to_rgba(PASSED)
return hex_to_rgba(FAILED)
class StdGermanGrading(BaseGrading):
def has_passed(self, value: int | float, max: int | float) -> bool:
return value/max >= 0.45
def search_grade(self, value: float) -> int:
if value <= 0:
return min(self.schema.keys())
searched = max(self.schema.keys())
found = False
while not found:
if self.schema[searched] <= value:
found = True
else:
searched -= 1
return searched
def get_grade(self, value: int | float, max: int | float) -> int:
return self.search_grade(value/max)
def get_grade_color(self, value: float, max: int | float) -> tuple:
grade = self.get_grade(value, max)
colors = gradient(PASSED, FAILED, len(self.schema))
return colors[grade]
# Definitions
Std30PercentRule = StdPercentRule({
"pAssed": 0.3,
"Failed": 0.0
}, "Std30PercentRule", "30 Percent")
Std50PercentRule = StdPercentRule({
"Passed": 0.5,
"Failed": 0.0
}, "Std50PercentRule", "50 Percent")
StdGermanGradingMiddleSchool = StdGermanGrading({
1: 0.96,
2: 0.80,
3: 0.60,
4: 0.45,
5: 0.16,
6: 0.00
}, "StdGermanGradingMiddleSchool", "Secondary School")
StdGermanGradingHighSchool = StdGermanGrading({
15: 0.95,
14: 0.90,
13: 0.85,
12: 0.80,
11: 0.75,
10: 0.70,
9: 0.65,
8: 0.60,
7: 0.55,
6: 0.50,
5: 0.45,
4: 0.40,
3: 0.33,
2: 0.27,
1: 0.20,
0: 0.00
}, "StdGermanGradingHighSchool", "Oberstufe")
#print(StdGermanGradingHighSchool.get_grade(0.0, 24))