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>
98 lines
2.7 KiB
Python
98 lines
2.7 KiB
Python
"""
|
|
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)
|