[split-required] Split 700-870 LOC files across all services
backend-lehrer (11 files): - llm_gateway/routes/schools.py (867 → 5), recording_api.py (848 → 6) - messenger_api.py (840 → 5), print_generator.py (824 → 5) - unit_analytics_api.py (751 → 5), classroom/routes/context.py (726 → 4) - llm_gateway/routes/edu_search_seeds.py (710 → 4) klausur-service (12 files): - ocr_labeling_api.py (845 → 4), metrics_db.py (833 → 4) - legal_corpus_api.py (790 → 4), page_crop.py (758 → 3) - mail/ai_service.py (747 → 4), github_crawler.py (767 → 3) - trocr_service.py (730 → 4), full_compliance_pipeline.py (723 → 4) - dsfa_rag_api.py (715 → 4), ocr_pipeline_auto.py (705 → 4) website (6 pages): - audit-checklist (867 → 8), content (806 → 6) - screen-flow (790 → 4), scraper (789 → 5) - zeugnisse (776 → 5), modules (745 → 4) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
97
backend-lehrer/unit_analytics_helpers.py
Normal file
97
backend-lehrer/unit_analytics_helpers.py
Normal file
@@ -0,0 +1,97 @@
|
||||
"""
|
||||
Unit Analytics API - Helpers.
|
||||
|
||||
Database access, statistical computation, and utility functions.
|
||||
"""
|
||||
|
||||
import os
|
||||
import logging
|
||||
from typing import List, Dict, Optional
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Feature flags
|
||||
USE_DATABASE = os.getenv("GAME_USE_DATABASE", "true").lower() == "true"
|
||||
|
||||
# Database singleton
|
||||
_analytics_db = None
|
||||
|
||||
|
||||
async def get_analytics_database():
|
||||
"""Get analytics database instance."""
|
||||
global _analytics_db
|
||||
if not USE_DATABASE:
|
||||
return None
|
||||
if _analytics_db is None:
|
||||
try:
|
||||
from unit.database import get_analytics_db
|
||||
_analytics_db = await get_analytics_db()
|
||||
logger.info("Analytics database initialized")
|
||||
except ImportError:
|
||||
logger.warning("Analytics database module not available")
|
||||
except Exception as e:
|
||||
logger.warning(f"Analytics database not available: {e}")
|
||||
return _analytics_db
|
||||
|
||||
|
||||
def calculate_gain_distribution(gains: List[float]) -> Dict[str, int]:
|
||||
"""Calculate distribution of learning gains into buckets."""
|
||||
distribution = {
|
||||
"< -20%": 0,
|
||||
"-20% to -10%": 0,
|
||||
"-10% to 0%": 0,
|
||||
"0% to 10%": 0,
|
||||
"10% to 20%": 0,
|
||||
"> 20%": 0,
|
||||
}
|
||||
|
||||
for gain in gains:
|
||||
gain_percent = gain * 100
|
||||
if gain_percent < -20:
|
||||
distribution["< -20%"] += 1
|
||||
elif gain_percent < -10:
|
||||
distribution["-20% to -10%"] += 1
|
||||
elif gain_percent < 0:
|
||||
distribution["-10% to 0%"] += 1
|
||||
elif gain_percent < 10:
|
||||
distribution["0% to 10%"] += 1
|
||||
elif gain_percent < 20:
|
||||
distribution["10% to 20%"] += 1
|
||||
else:
|
||||
distribution["> 20%"] += 1
|
||||
|
||||
return distribution
|
||||
|
||||
|
||||
def calculate_trend(scores: List[float]) -> str:
|
||||
"""Calculate trend from a series of scores."""
|
||||
if len(scores) < 3:
|
||||
return "insufficient_data"
|
||||
|
||||
# Simple linear regression
|
||||
n = len(scores)
|
||||
x_mean = (n - 1) / 2
|
||||
y_mean = sum(scores) / n
|
||||
|
||||
numerator = sum((i - x_mean) * (scores[i] - y_mean) for i in range(n))
|
||||
denominator = sum((i - x_mean) ** 2 for i in range(n))
|
||||
|
||||
if denominator == 0:
|
||||
return "stable"
|
||||
|
||||
slope = numerator / denominator
|
||||
|
||||
if slope > 0.05:
|
||||
return "improving"
|
||||
elif slope < -0.05:
|
||||
return "declining"
|
||||
else:
|
||||
return "stable"
|
||||
|
||||
|
||||
def calculate_difficulty_rating(success_rate: float, avg_attempts: float) -> float:
|
||||
"""Calculate difficulty rating 1-5 based on success metrics."""
|
||||
# Lower success rate and higher attempts = higher difficulty
|
||||
base_difficulty = (1 - success_rate) * 3 + 1 # 1-4 range
|
||||
attempt_modifier = min(avg_attempts - 1, 1) # 0-1 range
|
||||
return min(5.0, base_difficulty + attempt_modifier)
|
||||
Reference in New Issue
Block a user