grapher/grader/valuation.py

198 lines
5.6 KiB
Python
Raw Normal View History

2025-02-07 14:44:03 +01:00
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))