feat: implement Stage 5 (Interactive Reader, EPUB/TXT parser, Bookshelf, Click-to-Translate) and Stage 4.3 (Strict CEFR level calculation)
This commit is contained in:
1 parent
c51c505b61
commit
d8a2c6e34f
9 files changed
+2101
-30
No files matched your search
+54
@@ -248,3 +248,57 @@ def build_activity_heatmap(review_dates: Dict[str, int], days: int = 180) -> Dic
|
||||
"current_streak": current_streak,
|
||||
"longest_streak": max(longest_streak, current_streak),
|
||||
}
|
||||
|
||||
|
||||
def calculate_cefr_level(
|
||||
mastered_count: int,
|
||||
review_count: int,
|
||||
retention_rate: float,
|
||||
total_cards: int = 0
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
Computes strict CEFR level based on active vocabulary weight and memory retention rate (PLAN.md 4.3).
|
||||
Only words in Mastered (weight 1.0) and Review (weight 0.5) stages count, scaled by retention rate %.
|
||||
"""
|
||||
safe_retention = max(0.0, min(100.0, float(retention_rate)))
|
||||
retention_factor = (safe_retention / 100.0) if safe_retention > 0 else 1.0
|
||||
|
||||
# Active weighted vocabulary: Mastered gives 1.0, Review gives 0.5
|
||||
weighted_words = (mastered_count * 1.0) + (review_count * 0.5)
|
||||
active_vocab = int(round(weighted_words * retention_factor))
|
||||
|
||||
# CEFR thresholds: (min_words, level, title, next_level, next_target)
|
||||
brackets = [
|
||||
(0, "A1", "Начальный словарный запас", "A2", 300),
|
||||
(300, "A2", "Элементарный словарный запас", "B1", 800),
|
||||
(800, "B1", "Уверенное понимание общих и рабочих тем", "B2", 1600),
|
||||
(1600, "B2", "Свободное чтение тех. литературы и документации", "C1", 3000),
|
||||
(3000, "C1", "Продвинутый профессиональный уровень", "C2", 5000),
|
||||
]
|
||||
|
||||
current_bracket = brackets[0]
|
||||
for b in brackets:
|
||||
if active_vocab >= b[0]:
|
||||
current_bracket = b
|
||||
|
||||
min_w, level, title, next_lvl, next_w = current_bracket
|
||||
words_to_next = max(0, next_w - active_vocab)
|
||||
range_span = next_w - min_w
|
||||
current_progress = active_vocab - min_w
|
||||
pct = int(min(100, max(0, (current_progress / range_span) * 100))) if range_span > 0 else 100
|
||||
|
||||
if words_to_next > 0:
|
||||
message = f"До уровня {next_lvl} осталось закрепить {words_to_next} активных слов"
|
||||
else:
|
||||
message = f"Уровень {level} полностью освоен!"
|
||||
|
||||
return {
|
||||
"level": level,
|
||||
"title": title,
|
||||
"active_vocab": active_vocab,
|
||||
"next_level": next_lvl,
|
||||
"words_to_next": words_to_next,
|
||||
"progress_percent": pct,
|
||||
"message": message,
|
||||
"retention_rate": safe_retention,
|
||||
}
|
||||
Reference in new issue
Block a user