Spaces:
Running on Zero
Running on Zero
| # session_manager.py | |
| import os | |
| from datetime import datetime | |
| from student_manager import StudentManager | |
| from curriculum_manager import CurriculumManager | |
| from level_test_manager import LevelTestManager | |
| from mastery_manager import MasteryManager | |
| from attention_manager import AttentionManager | |
| from device_identity_manager import DeviceIdentityManager | |
| try: | |
| from general_culture_manager import GeneralCultureManager | |
| except Exception: | |
| GeneralCultureManager = None | |
| try: | |
| from gap_plan_manager import GapPlanManager | |
| except Exception: | |
| GapPlanManager = None | |
| class SessionManager: | |
| def __init__(self): | |
| self.student_manager = StudentManager() | |
| self.curriculum_manager = CurriculumManager() | |
| self.level_test_manager = LevelTestManager() | |
| self.mastery_manager = MasteryManager() | |
| self.attention_manager = AttentionManager() | |
| self.device_manager = DeviceIdentityManager() | |
| self.general_culture_manager = None | |
| if GeneralCultureManager is not None: | |
| try: | |
| self.general_culture_manager = GeneralCultureManager() | |
| except Exception: | |
| self.general_culture_manager = None | |
| self.gap_plan_manager = None | |
| if GapPlanManager is not None: | |
| try: | |
| self.gap_plan_manager = GapPlanManager() | |
| except Exception: | |
| self.gap_plan_manager = None | |
| self.admin_password = os.getenv("ADMIN_PASSWORD", "1234") | |
| def now(self): | |
| return datetime.utcnow().isoformat() | |
| def normalize_identity(self, identity): | |
| try: | |
| return self.student_manager.normalize_identity(identity) | |
| except Exception: | |
| return str(identity or "").strip().lower().replace(" ", "_") | |
| def normalize_text(self, text): | |
| text = str(text or "").strip().lower() | |
| text = text.replace("أ", "ا").replace("إ", "ا").replace("آ", "ا") | |
| text = text.replace("ة", "ه") | |
| text = text.replace("ى", "ي") | |
| text = text.replace("ؤ", "و") | |
| text = text.replace("ئ", "ي") | |
| text = " ".join(text.split()) | |
| return text | |
| def normalize_grade(self, grade): | |
| try: | |
| return self.curriculum_manager.normalize_grade(grade) | |
| except Exception: | |
| return str(grade or "").strip() | |
| def get_student(self, identity): | |
| identity = self.normalize_identity(identity) | |
| try: | |
| student = self.student_manager.get_student(identity) | |
| if isinstance(student, dict): | |
| return student | |
| except Exception: | |
| pass | |
| return None | |
| def save_student(self, student): | |
| try: | |
| return self.student_manager.save_student(student) | |
| except Exception: | |
| return False | |
| def normalize_subject_key(self, subject): | |
| text = self.normalize_text(subject) | |
| if "رياض" in text or "math" in text or "ماث" in text: | |
| return "math", "رياضيات" | |
| if "عربي" in text or "arabic" in text: | |
| return "arabic", "لغة عربية" | |
| if "انجليزي" in text or "انكليزي" in text or "english" in text: | |
| return "english", "لغة إنجليزية" | |
| if "فيز" in text or "physics" in text: | |
| return "physics", "فيزياء" | |
| if "كيم" in text or "chemistry" in text: | |
| return "chemistry", "كيمياء" | |
| if "احياء" in text or "حيات" in text or "biology" in text: | |
| return "biology", "أحياء" | |
| if "ارض" in text or "جيولوج" in text or "earth" in text or "geology" in text: | |
| return "earth_science", "علوم الأرض" | |
| if "بيئ" in text or "environment" in text: | |
| return "environmental_science", "علوم بيئية" | |
| if ( | |
| "حاسوب" in text | |
| or "كمبيوتر" in text | |
| or "computer" in text | |
| or "ict" in text | |
| or "تكنولوجيا" in text | |
| or "رقمي" in text | |
| or "برمج" in text | |
| ): | |
| return "computer", "حاسوب" | |
| if "علوم" in text or "science" in text: | |
| return "science", "علوم" | |
| return text.replace(" ", "_") or "unknown", subject or "غير محدد" | |
| def is_level_test_subject(self, subject): | |
| key, name = self.normalize_subject_key(subject) | |
| level_subjects = { | |
| "math", | |
| "arabic", | |
| "english", | |
| "science", | |
| "computer", | |
| "physics", | |
| "chemistry", | |
| "biology", | |
| "earth_science", | |
| "environmental_science" | |
| } | |
| return key in level_subjects | |
| def decide_subject_route_safe(self, grade, subject): | |
| key, subject_name = self.normalize_subject_key(subject) | |
| if self.is_level_test_subject(subject): | |
| return { | |
| "route": "level_test", | |
| "subject_key": key, | |
| "subject_name": subject_name, | |
| "subject_type": "level_test", | |
| "needs_level_test": True, | |
| "grade": self.normalize_grade(grade), | |
| "message": "هذه مادة من مواد فحص المستوى." | |
| } | |
| return { | |
| "route": "general_culture", | |
| "subject_key": key, | |
| "subject_name": subject_name, | |
| "subject_type": "general_culture", | |
| "needs_level_test": False, | |
| "grade": self.normalize_grade(grade), | |
| "message": "هذه المادة ليست من مواد فحص المستوى." | |
| } | |
| def register_student_safe(self, student): | |
| identity = student.get("identity", "") | |
| try: | |
| result = self.student_manager.register_student(student) | |
| if isinstance(result, dict): | |
| return result | |
| except Exception: | |
| pass | |
| try: | |
| result = self.student_manager.register_student(**student) | |
| if isinstance(result, dict): | |
| return result | |
| except Exception: | |
| pass | |
| try: | |
| self.student_manager.save_student(student) | |
| except Exception: | |
| pass | |
| loaded = self.get_student(identity) | |
| if isinstance(loaded, dict): | |
| return loaded | |
| return student | |
| def build_new_student( | |
| self, | |
| student_name, | |
| email, | |
| grade, | |
| age, | |
| parent_password | |
| ): | |
| identity = self.normalize_identity(student_name) | |
| return { | |
| "identity": identity, | |
| "name": str(student_name or "").strip(), | |
| "email": str(email or "").strip(), | |
| "grade": str(grade or "").strip(), | |
| "age": str(age or "").strip(), | |
| "parent_password": str(parent_password or "").strip(), | |
| "created_at": self.now(), | |
| "updated_at": self.now(), | |
| "level_test": { | |
| "completed": False, | |
| "result": None | |
| }, | |
| "level_tests": {}, | |
| "homework": [], | |
| "flashcards": [], | |
| "quizzes": [], | |
| "reports": [], | |
| "lesson_history": [], | |
| "progress": {}, | |
| "skills": {}, | |
| "general_culture": {}, | |
| "authorized_devices": [] | |
| } | |
| def verify_student_login(self, student_name, hardware_fingerprint): | |
| return self.device_manager.verify_name_and_fingerprint( | |
| student_name=student_name, | |
| fingerprint=hardware_fingerprint | |
| ) | |
| def complete_new_student_after_verify( | |
| self, | |
| student_name, | |
| email, | |
| grade, | |
| age, | |
| parent_password, | |
| hardware_fingerprint | |
| ): | |
| student_name = str(student_name or "").strip() | |
| email = str(email or "").strip() | |
| grade = str(grade or "").strip() | |
| age = str(age or "").strip() | |
| parent_password = str(parent_password or "").strip() | |
| hardware_fingerprint = str(hardware_fingerprint or "").strip() | |
| if not student_name: | |
| return { | |
| "success": False, | |
| "message": "أدخل اسم الطالب.", | |
| "student": None, | |
| "next_step": "missing_name" | |
| } | |
| if not email: | |
| return { | |
| "success": False, | |
| "message": "أدخل البريد الإلكتروني.", | |
| "student": None, | |
| "next_step": "missing_email" | |
| } | |
| if not grade: | |
| return { | |
| "success": False, | |
| "message": "أدخل الصف.", | |
| "student": None, | |
| "next_step": "missing_grade" | |
| } | |
| if not parent_password: | |
| return { | |
| "success": False, | |
| "message": "أدخل كلمة مرور ولي الأمر.", | |
| "student": None, | |
| "next_step": "missing_parent_password" | |
| } | |
| if not hardware_fingerprint: | |
| return { | |
| "success": False, | |
| "message": "اضغط تحقق لأخذ البصمة.", | |
| "student": None, | |
| "next_step": "missing_fingerprint" | |
| } | |
| owner = self.device_manager.find_owner_by_fingerprint(hardware_fingerprint) | |
| if isinstance(owner, dict): | |
| return { | |
| "success": False, | |
| "message": "هذه البصمة مرتبطة بحساب آخر. لا يمكن استخدام جهاز واحد لحسابين.", | |
| "student": owner, | |
| "next_step": "blocked" | |
| } | |
| existing = self.device_manager.find_one_student_by_name(student_name) | |
| if isinstance(existing, dict): | |
| return { | |
| "success": False, | |
| "message": "الاسم موجود سابقًا. استخدم تحقق الجهاز الجديد بالبريد وكلمة مرور ولي الأمر.", | |
| "student": existing, | |
| "next_step": "verify_existing_device" | |
| } | |
| student = self.build_new_student( | |
| student_name=student_name, | |
| email=email, | |
| grade=grade, | |
| age=age, | |
| parent_password=parent_password | |
| ) | |
| student = self.register_student_safe(student) | |
| link_result = self.device_manager.link_device_to_student( | |
| identity=student.get("identity", self.normalize_identity(student_name)), | |
| fingerprint=hardware_fingerprint, | |
| label="الجهاز الأساسي" | |
| ) | |
| if not link_result.get("success"): | |
| return { | |
| "success": False, | |
| "message": link_result.get("message", "فشل ربط البصمة."), | |
| "student": student, | |
| "next_step": "device_error" | |
| } | |
| student = link_result.get("student", student) | |
| return { | |
| "success": True, | |
| "message": "تم إنشاء حساب الطالب وربط الجهاز. التالي: فحص المستوى.", | |
| "student": student, | |
| "next_step": "level_test" | |
| } | |
| def verify_existing_device( | |
| self, | |
| student_name, | |
| email, | |
| parent_password, | |
| hardware_fingerprint | |
| ): | |
| student_name = str(student_name or "").strip() | |
| email = str(email or "").strip() | |
| parent_password = str(parent_password or "").strip() | |
| hardware_fingerprint = str(hardware_fingerprint or "").strip() | |
| if not student_name or not email or not hardware_fingerprint: | |
| return { | |
| "success": False, | |
| "message": "أدخل الاسم والبريد واضغط تحقق لأخذ البصمة.", | |
| "student": None, | |
| "next_step": "missing_data" | |
| } | |
| student = self.device_manager.find_one_student_by_name(student_name) | |
| if not isinstance(student, dict): | |
| return { | |
| "success": False, | |
| "message": "لم يتم العثور على الطالب.", | |
| "student": None, | |
| "next_step": "not_found" | |
| } | |
| saved_email = str(student.get("email", "")).strip().lower() | |
| given_email = email.strip().lower() | |
| if saved_email and saved_email != given_email: | |
| return { | |
| "success": False, | |
| "message": "البريد الإلكتروني غير مطابق لملف الطالب.", | |
| "student": None, | |
| "next_step": "email_mismatch" | |
| } | |
| saved_parent_password = str(student.get("parent_password", "")).strip() | |
| if saved_parent_password: | |
| if str(parent_password) != saved_parent_password: | |
| return { | |
| "success": False, | |
| "message": "كلمة مرور ولي الأمر غير صحيحة.", | |
| "student": None, | |
| "next_step": "parent_password_mismatch" | |
| } | |
| else: | |
| if not parent_password: | |
| return { | |
| "success": False, | |
| "message": "أدخل كلمة مرور ولي الأمر لحفظها وربط الجهاز.", | |
| "student": None, | |
| "next_step": "missing_parent_password" | |
| } | |
| student["parent_password"] = parent_password | |
| self.save_student(student) | |
| owner = self.device_manager.find_owner_by_fingerprint(hardware_fingerprint) | |
| if isinstance(owner, dict): | |
| owner_identity = self.normalize_identity(owner.get("identity", "")) | |
| student_identity = self.normalize_identity(student.get("identity", "")) | |
| if owner_identity != student_identity: | |
| return { | |
| "success": False, | |
| "message": "هذه البصمة مرتبطة بحساب آخر. لا يمكن استخدام جهاز واحد لحسابين.", | |
| "student": owner, | |
| "next_step": "blocked" | |
| } | |
| link_result = self.device_manager.link_device_to_student( | |
| identity=student.get("identity", self.normalize_identity(student_name)), | |
| fingerprint=hardware_fingerprint, | |
| label="جهاز إضافي" | |
| ) | |
| if not link_result.get("success"): | |
| return { | |
| "success": False, | |
| "message": link_result.get("message", "فشل ربط الجهاز."), | |
| "student": None, | |
| "next_step": "device_error" | |
| } | |
| student = link_result.get("student", student) | |
| return { | |
| "success": True, | |
| "message": "تم توثيق الجهاز الجديد وربطه بحساب الطالب.", | |
| "student": student, | |
| "next_step": self.decide_next_step(student) | |
| } | |
| def subject_needs_level_test(self, student, subject): | |
| if not isinstance(student, dict): | |
| return False | |
| if not self.is_level_test_subject(subject): | |
| return False | |
| key, subject_name = self.normalize_subject_key(subject) | |
| level_tests = student.get("level_tests", {}) | |
| if isinstance(level_tests, dict): | |
| subject_result = level_tests.get(key) | |
| if isinstance(subject_result, dict) and subject_result.get("completed"): | |
| return False | |
| level_test = student.get("level_test", {}) | |
| if isinstance(level_test, dict): | |
| result = level_test.get("result", {}) | |
| if isinstance(result, dict): | |
| if result.get("subject_key") == key and level_test.get("completed"): | |
| return False | |
| return True | |
| def has_required_mastery_tasks(self, identity): | |
| try: | |
| return self.mastery_manager.has_required_mastery_tasks(identity) | |
| except Exception: | |
| return False | |
| def has_general_culture_tasks(self, identity): | |
| if self.general_culture_manager is None: | |
| return False | |
| try: | |
| return self.general_culture_manager.has_required_culture_tasks(identity) | |
| except Exception: | |
| return False | |
| def has_gap_plan_tasks(self, identity): | |
| if self.gap_plan_manager is None: | |
| return False | |
| try: | |
| tasks = self.gap_plan_manager.get_waiting_tasks(identity) | |
| return len(tasks) > 0 | |
| except Exception: | |
| return False | |
| def check_required_tasks(self, identity, subject=""): | |
| identity = self.normalize_identity(identity) | |
| student = self.get_student(identity) | |
| if not isinstance(student, dict): | |
| return { | |
| "allowed": False, | |
| "screen": "identity_required", | |
| "message": "لم يتم العثور على الطالب.", | |
| "tasks": [] | |
| } | |
| tasks = [] | |
| if subject and self.subject_needs_level_test(student, subject): | |
| tasks.append({ | |
| "type": "level_test", | |
| "title": "فحص المستوى", | |
| "message": "يجب إكمال فحص المستوى لهذه المادة أولًا." | |
| }) | |
| if self.has_required_mastery_tasks(identity): | |
| tasks.append({ | |
| "type": "mastery_badges", | |
| "title": "أوسمة الإتقان", | |
| "message": "يوجد أوسمة إتقان مطلوبة." | |
| }) | |
| if self.has_general_culture_tasks(identity): | |
| tasks.append({ | |
| "type": "general_culture", | |
| "title": "الثقافة العامة", | |
| "message": "يوجد أسئلة ثقافة عامة مطلوبة." | |
| }) | |
| if self.has_gap_plan_tasks(identity): | |
| tasks.append({ | |
| "type": "gap_plan", | |
| "title": "الخطة B", | |
| "message": "يوجد مهام من الخطة B." | |
| }) | |
| if tasks: | |
| return { | |
| "allowed": False, | |
| "screen": tasks[0]["type"], | |
| "message": tasks[0]["message"], | |
| "tasks": tasks | |
| } | |
| return { | |
| "allowed": True, | |
| "screen": "lesson", | |
| "message": "يمكن دخول الدرس.", | |
| "tasks": [] | |
| } | |
| def decide_next_step(self, student, subject="رياضيات"): | |
| if not isinstance(student, dict): | |
| return "identity_required" | |
| if not student.get("grade"): | |
| return "complete_profile" | |
| if self.subject_needs_level_test(student, subject): | |
| return "level_test" | |
| required = self.check_required_tasks( | |
| identity=student.get("identity", ""), | |
| subject=subject | |
| ) | |
| if not required.get("allowed"): | |
| return required.get("screen", "tasks") | |
| return "lesson" | |
| def start_student_session( | |
| self, | |
| identity="", | |
| name="", | |
| email="", | |
| grade="", | |
| age="", | |
| hardware_fingerprint="", | |
| device_source="hardware" | |
| ): | |
| student_name = name or identity or email | |
| verify = self.verify_student_login( | |
| student_name=student_name, | |
| hardware_fingerprint=hardware_fingerprint | |
| ) | |
| if verify.get("status") == "matched": | |
| student = verify.get("student") | |
| return { | |
| "success": True, | |
| "message": "تم الدخول من الاسم والبصمة.", | |
| "screen": "student_dashboard", | |
| "student": student, | |
| "next_step": self.decide_next_step(student) | |
| } | |
| if verify.get("status") == "new_student": | |
| return self.complete_new_student_after_verify( | |
| student_name=student_name, | |
| email=email, | |
| grade=grade, | |
| age=age, | |
| parent_password="", | |
| hardware_fingerprint=hardware_fingerprint | |
| ) | |
| return { | |
| "success": False, | |
| "message": verify.get("message", "لم يتم الدخول."), | |
| "screen": verify.get("status", "login_failed"), | |
| "student": verify.get("student") | |
| } | |
| def route_subject(self, identity, subject): | |
| identity = self.normalize_identity(identity) | |
| student = self.get_student(identity) | |
| if not isinstance(student, dict): | |
| return { | |
| "success": False, | |
| "screen": "identity_required", | |
| "message": "لم يتم العثور على الطالب.", | |
| "data": None | |
| } | |
| route = self.decide_subject_route_safe( | |
| grade=student.get("grade", ""), | |
| subject=subject | |
| ) | |
| if route.get("route") == "level_test": | |
| return { | |
| "success": True, | |
| "screen": "level_test", | |
| "message": route.get("message", "هذه مادة من مواد فحص المستوى."), | |
| "route": route, | |
| "data": None | |
| } | |
| return { | |
| "success": True, | |
| "screen": "general_culture", | |
| "message": route.get("message", "هذه المادة ليست من مواد فحص المستوى."), | |
| "route": route, | |
| "data": None | |
| } | |
| def create_level_test_for_subject(self, identity, subject): | |
| identity = self.normalize_identity(identity) | |
| student = self.get_student(identity) | |
| if not isinstance(student, dict): | |
| return { | |
| "success": False, | |
| "message": "لم يتم العثور على الطالب.", | |
| "test": None | |
| } | |
| if not self.is_level_test_subject(subject): | |
| return { | |
| "success": False, | |
| "message": "هذه المادة ليست من مواد فحص المستوى.", | |
| "test": None | |
| } | |
| key, subject_name = self.normalize_subject_key(subject) | |
| return self.level_test_manager.create_level_test( | |
| student=student, | |
| subject=subject_name | |
| ) | |
| def submit_level_test(self, identity, subject, test_data, answers_text): | |
| return self.level_test_manager.evaluate_level_test( | |
| identity=identity, | |
| subject=subject, | |
| test_data=test_data, | |
| answers_text=answers_text | |
| ) | |
| def admin_login(self, password): | |
| if str(password or "") == str(self.admin_password): | |
| return { | |
| "success": True, | |
| "message": "تم دخول الإدارة." | |
| } | |
| return { | |
| "success": False, | |
| "message": "كلمة مرور الإدارة غير صحيحة." | |
| } | |
| def student_status_summary(self, identity): | |
| identity = self.normalize_identity(identity) | |
| student = self.get_student(identity) | |
| if not isinstance(student, dict): | |
| return "لم يتم العثور على الطالب." | |
| devices = student.get("authorized_devices", []) | |
| if not isinstance(devices, list): | |
| devices = [] | |
| level_tests = student.get("level_tests", {}) | |
| if not isinstance(level_tests, dict): | |
| level_tests = {} | |
| text = f""" | |
| # حالة الطالب | |
| الاسم: {student.get("name", "")} | |
| البريد: {student.get("email", "")} | |
| الصف: {student.get("grade", "")} | |
| العمر: {student.get("age", "")} | |
| ## الأجهزة الموثقة | |
| عدد الأجهزة: {len(devices)} | |
| ## فحوصات المستوى المحفوظة | |
| """ | |
| if level_tests: | |
| for key, result in level_tests.items(): | |
| if isinstance(result, dict): | |
| text += f""" | |
| - {result.get("subject", key)}: {result.get("percentage", 0)}% - {result.get("level", "")} | |
| """ | |
| else: | |
| text += "\nلا توجد فحوصات مستوى محفوظة بعد." | |
| text += f""" | |
| ## التوجيه التالي | |
| {self.decide_next_step(student)} | |
| """ | |
| return text.strip() |