- FastAPI backend with JWT auth, roles (admin/moderator/user) - PDF upload (up to 500MB) with streaming, PyMuPDF text extraction - ChromaDB vectorization per page with metadata - LiteLLM AI question extraction from PDF (not generation) - Image extraction from PDF pages, graceful fallback - Quiz modes: timed (countdown timer) + learning (answers shown inline) - Page-by-page question navigation with dot navigator - TTS endpoint using LiteLLM (Google Vertex / OpenAI voices) - Admin dashboard: AI model management per task, user role management - Moderator role: upload PDFs, create sections, generate quizzes - Spaced repetition reminders via SMTP email (SM-2 intervals) - APScheduler daily reminder jobs - Celery + Redis for background PDF processing - React frontend with all pages - Docker Compose deployment (nginx + backend + celery + redis) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
61 lines
1.9 KiB
Python
61 lines
1.9 KiB
Python
from datetime import datetime, timedelta
|
|
|
|
from sqlalchemy.orm import Session
|
|
|
|
from app.models.reminder import ReminderSchedule
|
|
|
|
# SM-2 simplified intervals in days
|
|
INTERVALS = [1, 3, 7, 14, 30]
|
|
|
|
|
|
def update_reminder_schedule(
|
|
db: Session,
|
|
user_id: int,
|
|
quiz_id: int,
|
|
score_percentage: float,
|
|
):
|
|
"""Update or create a reminder schedule based on quiz performance."""
|
|
reminder = db.query(ReminderSchedule).filter(
|
|
ReminderSchedule.user_id == user_id,
|
|
ReminderSchedule.quiz_id == quiz_id,
|
|
).first()
|
|
|
|
if not reminder:
|
|
reminder = ReminderSchedule(
|
|
user_id=user_id,
|
|
quiz_id=quiz_id,
|
|
performance_score=score_percentage,
|
|
interval_days=INTERVALS[0],
|
|
next_reminder_at=datetime.utcnow() + timedelta(days=INTERVALS[0]),
|
|
is_active=True,
|
|
)
|
|
db.add(reminder)
|
|
else:
|
|
reminder.performance_score = score_percentage
|
|
reminder.is_active = True
|
|
|
|
# Find current interval index
|
|
current_idx = 0
|
|
for i, interval in enumerate(INTERVALS):
|
|
if reminder.interval_days <= interval:
|
|
current_idx = i
|
|
break
|
|
|
|
if score_percentage < 70:
|
|
# Poor performance: reset to shortest interval
|
|
new_idx = 0
|
|
elif score_percentage < 90:
|
|
# Decent performance: advance one step
|
|
new_idx = min(current_idx + 1, len(INTERVALS) - 1)
|
|
else:
|
|
# Excellent performance: advance or deactivate
|
|
if current_idx >= len(INTERVALS) - 1:
|
|
reminder.is_active = False
|
|
db.commit()
|
|
return
|
|
new_idx = min(current_idx + 2, len(INTERVALS) - 1)
|
|
|
|
reminder.interval_days = INTERVALS[new_idx]
|
|
reminder.next_reminder_at = datetime.utcnow() + timedelta(days=INTERVALS[new_idx])
|
|
|
|
db.commit()
|