-
Notifications
You must be signed in to change notification settings - Fork 13
Expand file tree
/
Copy pathscore_statistics.py
More file actions
65 lines (56 loc) · 1.88 KB
/
Copy pathscore_statistics.py
File metadata and controls
65 lines (56 loc) · 1.88 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
"""Shared score statistics used by both the score page and Wenzhou context."""
from __future__ import annotations
import math
from dataclasses import dataclass
from typing import Iterable, Mapping
@dataclass(frozen=True)
class ScoreStatistics:
total_credit: float
weighted_gpa: float | None
weighted_average: float | None
incomplete_count: int
course_count: int
def calculate_score_statistics(scores: Iterable[Mapping]) -> ScoreStatistics | None:
"""Calculate the score page's credit-weighted summary once, consistently."""
rows = [one for one in scores if isinstance(one, Mapping)]
total_credit = 0.0
gpa_total = 0.0
gpa_credit = 0.0
score_total = 0.0
score_credit = 0.0
incomplete = 0
courses = 0
for row in rows:
credit = _finite_number(row.get("coursePoint"))
if credit is None or credit <= 0:
incomplete += 1
continue
courses += 1
total_credit += credit
gpa = _finite_number(row.get("gpa"))
score = _finite_number(row.get("score"))
if gpa is not None:
gpa_total += gpa * credit
gpa_credit += credit
if score is not None:
score_total += score * credit
score_credit += credit
if gpa is None or score is None:
incomplete += 1
if total_credit <= 0:
return None
return ScoreStatistics(
total_credit=total_credit,
weighted_gpa=gpa_total / gpa_credit if gpa_credit else None,
weighted_average=score_total / score_credit if score_credit else None,
incomplete_count=incomplete,
course_count=courses,
)
def _finite_number(value) -> float | None:
if isinstance(value, bool):
return None
try:
number = float(value)
except (TypeError, ValueError):
return None
return number if math.isfinite(number) else None