""" focus_timer_manager.py مدير مؤقت التركيز Pomodoro في منصة Divid Teacher مسؤول عن: - جلسة دراسة 25 دقيقة - استراحة قصيرة 5 دقائق - استراحة طويلة بعد كل 4 جلسات - تحديد أصوات البداية والنهاية - حفظ تقدم الطالب في جلسات التركيز """ from datetime import datetime, timedelta from pathlib import Path from student_manager import StudentManager try: from config import ASSETS_FOLDER except Exception: ASSETS_FOLDER = "assets" class FocusTimerManager: def __init__(self): self.student_manager = StudentManager() self.assets_folder = Path(ASSETS_FOLDER) self.sounds_folder = self.assets_folder / "sounds" self.sounds_folder.mkdir(parents=True, exist_ok=True) self.default_settings = { "study_minutes": 25, "short_break_minutes": 5, "long_break_minutes": 15, "long_break_after_sessions": 4, "enabled": True, "use_sound": True, "use_voice_message": True } self.sound_files = { "pomodoro_start": str(self.sounds_folder / "pomodoro_start.mp3"), "pomodoro_end": str(self.sounds_folder / "pomodoro_end.mp3"), "break_start": str(self.sounds_folder / "break_start.mp3"), "break_end": str(self.sounds_folder / "break_end.mp3"), "long_break": str(self.sounds_folder / "long_break.mp3") } # ====================================================== # الوقت # ====================================================== def now(self): return datetime.utcnow() def now_text(self): return self.now().isoformat() # ====================================================== # تجهيز ملف التركيز داخل الطالب # ====================================================== def ensure_focus_profile(self, student): if "focus_timer" not in student or not isinstance(student["focus_timer"], dict): student["focus_timer"] = {} if "settings" not in student["focus_timer"]: student["focus_timer"]["settings"] = self.default_settings.copy() if "sessions" not in student["focus_timer"]: student["focus_timer"]["sessions"] = [] if "current_session" not in student["focus_timer"]: student["focus_timer"]["current_session"] = None if "completed_pomodoros" not in student["focus_timer"]: student["focus_timer"]["completed_pomodoros"] = 0 if "today_pomodoros" not in student["focus_timer"]: student["focus_timer"]["today_pomodoros"] = 0 if "last_session_at" not in student["focus_timer"]: student["focus_timer"]["last_session_at"] = "" return student def save_student(self, student): return self.student_manager.save_student(student) # ====================================================== # جلب الإعدادات # ====================================================== def get_settings(self, identity=None): if identity: student = self.student_manager.get_student(identity) if student: student = self.ensure_focus_profile(student) return student["focus_timer"]["settings"] return self.default_settings.copy() def update_settings( self, identity, study_minutes=25, short_break_minutes=5, long_break_minutes=15, long_break_after_sessions=4, enabled=True ): student = self.student_manager.get_student(identity) if not student: return None student = self.ensure_focus_profile(student) student["focus_timer"]["settings"] = { "study_minutes": int(study_minutes), "short_break_minutes": int(short_break_minutes), "long_break_minutes": int(long_break_minutes), "long_break_after_sessions": int(long_break_after_sessions), "enabled": bool(enabled), "use_sound": True, "use_voice_message": True } self.save_student(student) return student["focus_timer"]["settings"] # ====================================================== # بدء جلسة تركيز # ====================================================== def start_pomodoro( self, identity, subject="", lesson_title="", plan_type="A" ): student = self.student_manager.get_student(identity) if not student: return { "success": False, "message": "لم يتم العثور على الطالب.", "session": None } student = self.ensure_focus_profile(student) settings = student["focus_timer"]["settings"] start_time = self.now() end_time = start_time + timedelta( minutes=int(settings.get("study_minutes", 25)) ) session_number = len(student["focus_timer"]["sessions"]) + 1 session = { "session_id": f"pomodoro_{session_number}", "type": "study", "status": "active", "subject": subject, "lesson_title": lesson_title, "plan_type": plan_type, "start_time": start_time.isoformat(), "expected_end_time": end_time.isoformat(), "actual_end_time": "", "duration_minutes": int(settings.get("study_minutes", 25)), "sound_on_start": self.sound_files["pomodoro_start"], "sound_on_end": self.sound_files["pomodoro_end"], "voice_message_start": "بدأت جلسة التركيز. حاول أن تركز بهدوء.", "voice_message_end": "أحسنت، انتهت جلسة التركيز. خذ استراحة قصيرة." } student["focus_timer"]["current_session"] = session student["focus_timer"]["sessions"].append(session) student["focus_timer"]["last_session_at"] = self.now_text() self.save_student(student) return { "success": True, "message": "تم بدء جلسة التركيز.", "session": session } # ====================================================== # إنهاء جلسة التركيز # ====================================================== def finish_current_session(self, identity): student = self.student_manager.get_student(identity) if not student: return { "success": False, "message": "لم يتم العثور على الطالب.", "next": None } student = self.ensure_focus_profile(student) current = student["focus_timer"].get("current_session") if not current: return { "success": False, "message": "لا توجد جلسة نشطة.", "next": None } current["status"] = "completed" current["actual_end_time"] = self.now_text() student["focus_timer"]["completed_pomodoros"] += 1 student["focus_timer"]["today_pomodoros"] += 1 completed_count = student["focus_timer"]["completed_pomodoros"] settings = student["focus_timer"]["settings"] long_after = int(settings.get("long_break_after_sessions", 4)) if completed_count % long_after == 0: next_break = self.build_break_session( student=student, break_type="long_break" ) else: next_break = self.build_break_session( student=student, break_type="short_break" ) student["focus_timer"]["current_session"] = next_break student["focus_timer"]["sessions"].append(next_break) self.save_student(student) return { "success": True, "message": "تم إنهاء جلسة التركيز.", "next": next_break } # ====================================================== # بناء الاستراحة # ====================================================== def build_break_session(self, student, break_type="short_break"): settings = student["focus_timer"]["settings"] if break_type == "long_break": minutes = int(settings.get("long_break_minutes", 15)) sound = self.sound_files["long_break"] message_start = "أنهيت أربع جلسات تركيز. تستحق استراحة أطول." message_end = "انتهت الاستراحة الطويلة. نعود بهدوء." else: minutes = int(settings.get("short_break_minutes", 5)) sound = self.sound_files["break_start"] message_start = "بدأت الاستراحة القصيرة." message_end = "انتهت الاستراحة. نعود للدرس." start_time = self.now() end_time = start_time + timedelta(minutes=minutes) session_number = len(student["focus_timer"]["sessions"]) + 1 return { "session_id": f"{break_type}_{session_number}", "type": break_type, "status": "active", "start_time": start_time.isoformat(), "expected_end_time": end_time.isoformat(), "actual_end_time": "", "duration_minutes": minutes, "sound_on_start": sound, "sound_on_end": self.sound_files["break_end"], "voice_message_start": message_start, "voice_message_end": message_end } # ====================================================== # إنهاء الاستراحة # ====================================================== def finish_break(self, identity): student = self.student_manager.get_student(identity) if not student: return { "success": False, "message": "لم يتم العثور على الطالب." } student = self.ensure_focus_profile(student) current = student["focus_timer"].get("current_session") if not current: return { "success": False, "message": "لا توجد استراحة نشطة." } if current.get("type") not in ["short_break", "long_break"]: return { "success": False, "message": "الجلسة الحالية ليست استراحة." } current["status"] = "completed" current["actual_end_time"] = self.now_text() student["focus_timer"]["current_session"] = None self.save_student(student) return { "success": True, "message": "تم إنهاء الاستراحة.", "voice_message": current.get("voice_message_end", "") } # ====================================================== # حالة المؤقت # ====================================================== def get_timer_status(self, identity): student = self.student_manager.get_student(identity) if not student: return { "exists": False, "message": "لم يتم العثور على الطالب." } student = self.ensure_focus_profile(student) current = student["focus_timer"].get("current_session") return { "exists": True, "current_session": current, "completed_pomodoros": student["focus_timer"].get("completed_pomodoros", 0), "today_pomodoros": student["focus_timer"].get("today_pomodoros", 0), "settings": student["focus_timer"].get("settings", {}) } # ====================================================== # تنسيق للعرض # ====================================================== def format_timer_status(self, identity): status = self.get_timer_status(identity) if not status.get("exists"): return status.get("message", "لا توجد بيانات.") current = status.get("current_session") text = f""" # مؤقت التركيز Pomodoro عدد جلسات اليوم: {status.get("today_pomodoros", 0)} عدد الجلسات الكلي: {status.get("completed_pomodoros", 0)} """ if current: text += f""" ## الجلسة الحالية النوع: {current.get("type", "")} الحالة: {current.get("status", "")} البداية: {current.get("start_time", "")} النهاية المتوقعة: {current.get("expected_end_time", "")} المدة: {current.get("duration_minutes", "")} دقيقة رسالة البداية: {current.get("voice_message_start", "")} رسالة النهاية: {current.get("voice_message_end", "")} """ else: text += "\nلا توجد جلسة نشطة حاليًا." return text.strip() # ====================================================== # مسارات الأصوات # ====================================================== def get_sound_files(self): return self.sound_files