""" وظائف معالجة البيانات (تحليل الملفات التعريفية، إدارة التقدم، إلخ) """ import json from typing import Dict, List, Optional from datetime import datetime, timedelta import io def parse_profile_file(file_content: str, file_type: str = "json") -> Optional[Dict]: """ تحليل ملف تعريفي مرفوع Args: file_content: محتوى الملف file_type: نوع الملف (json, txt, markdown) Returns: القاموس المُحلل أو None """ try: if file_type == "json": return json.loads(file_content) elif file_type in ["txt", "markdown", "md"]: # تحليل بسيط من نص حر profile = { "name": extract_field(file_content, ["name", "الاسم", "اسم"]), "age": extract_field(file_content, ["age", "العمر", "عمر"]), "level": extract_field(file_content, ["level", "المستوى", "مستوى"]), "skills": extract_field(file_content, ["skills", "المهارات", "مهارات"]), "available_hours": extract_field(file_content, ["hours", "ساعات", "وقت متاح"]), "learning_style": extract_field(file_content, ["style", "أسلوب", "تفضيلات"]), "goals": extract_field(file_content, ["goals", "أهداف", "هدف"]), } return profile except Exception as e: print(f"خطأ في تحليل الملف: {str(e)}") return None def extract_field(text: str, keywords: List[str]) -> str: """استخراج حقل من نص حر بناءً على كلمات مفتاحية""" text_lower = text.lower() for keyword in keywords: if keyword.lower() in text_lower: # البحث عن السطر الذي يحتوي على الكلمة المفتاحية for line in text.split('\n'): if keyword.lower() in line.lower(): # استخراج القيمة بعد النقطتين أو المساواة if ':' in line: return line.split(':', 1)[1].strip() elif '=' in line: return line.split('=', 1)[1].strip() return "" def create_default_profile() -> Dict: """إنشاء ملف تعريفي افتراضي""" return { "name": "مستخدم جديد", "age": "", "level": "مبتدئ", "skills": [], "available_hours": 10, "learning_style": "مختلط (نظري وعملي)", "goals": "تعلم مهارات جديدة", "preferences": { "language": "ar", "notifications": True, "theme": "light" } } def validate_profile(profile: Dict) -> tuple[bool, List[str]]: """ التحقق من صحة الملف التعريفي Returns: (هل صالح, قائمة بالأخطاء) """ errors = [] required_fields = ["name", "level", "available_hours"] for field in required_fields: if field not in profile or not profile[field]: errors.append(f"الحقل '{field}' مطلوب") if profile.get("available_hours"): try: hours = int(profile["available_hours"]) if hours < 1 or hours > 40: errors.append("الوقت المتاح يجب أن يكون بين 1 و 40 ساعة أسبوعياً") except: errors.append("الوقت المتاح يجب أن يكون رقماً") return len(errors) == 0, errors def generate_schedule(curriculum: List[Dict], available_hours: int) -> List[Dict]: """ توليد جدول زمني بناءً على المنهج والوقت المتاح Args: curriculum: المنهج الدراسي available_hours: الساعات المتاحة أسبوعياً Returns: جدول زمني مفصل """ schedule = [] start_date = datetime.now() for week_num, unit in enumerate(curriculum, start=1): week_start = start_date + timedelta(weeks=week_num - 1) # توزيع الدروس على أيام الأسبوع lessons = unit.get("lessons", []) total_mins = sum(lesson.get("duration_mins", 60) for lesson in lessons) # إضافة وقت التجربة if unit.get("experiment"): total_mins += 120 # افتراض ساعتين للتجربة schedule_entry = { "week": week_num, "start_date": week_start.strftime("%Y-%m-%d"), "unit_title": unit.get("unit_title", f"الوحدة {week_num}"), "total_hours": round(total_mins / 60, 1), "daily_breakdown": distribute_to_days(lessons, available_hours), "experiment": unit.get("experiment"), "status": "pending" } schedule.append(schedule_entry) return schedule def distribute_to_days(lessons: List[Dict], hours_per_week: int) -> List[Dict]: """توزيع الدروس على أيام الأسبوع""" days = ["الأحد", "الاثنين", "الثلاثاء", "الأربعاء", "الخميس"] distribution = [] mins_per_day = (hours_per_week * 60) // len(days) current_day = 0 current_day_mins = 0 current_day_lessons = [] for lesson in lessons: lesson_mins = lesson.get("duration_mins", 60) if current_day_mins + lesson_mins > mins_per_day and current_day_lessons: # حفظ اليوم الحالي والانتقال للتالي distribution.append({ "day": days[current_day % len(days)], "lessons": current_day_lessons.copy(), "total_mins": current_day_mins }) current_day += 1 current_day_mins = 0 current_day_lessons = [] current_day_lessons.append(lesson) current_day_mins += lesson_mins # إضافة آخر يوم if current_day_lessons: distribution.append({ "day": days[current_day % len(days)], "lessons": current_day_lessons, "total_mins": current_day_mins }) return distribution def calculate_progress(completed_units: List[int], total_units: int) -> Dict: """ حساب التقدم الإجمالي Returns: معلومات التقدم """ completed = len(completed_units) percentage = (completed / total_units * 100) if total_units > 0 else 0 return { "completed_units": completed, "total_units": total_units, "percentage": round(percentage, 1), "remaining_units": total_units - completed, "status": get_progress_status(percentage) } def get_progress_status(percentage: float) -> str: """تحديد حالة التقدم""" if percentage < 25: return "بداية الرحلة 🌱" elif percentage < 50: return "في الطريق 🚀" elif percentage < 75: return "تقدم ممتاز 🌟" elif percentage < 100: return "قرب الإنجاز 🎯" else: return "مكتمل! 🎉" def export_profile_to_json(profile: Dict) -> str: """تصدير الملف التعريفي إلى JSON""" return json.dumps(profile, ensure_ascii=False, indent=2) def export_progress_to_json(progress_data: Dict) -> str: """تصدير بيانات التقدم إلى JSON""" return json.dumps(progress_data, ensure_ascii=False, indent=2) def load_local_data(filename: str) -> Optional[Dict]: """تحميل بيانات من ملف JSON محلي""" try: with open(f"data/{filename}", "r", encoding="utf-8") as f: return json.load(f) except: return None def save_local_data(filename: str, data: Dict) -> bool: """حفظ بيانات في ملف JSON محلي""" try: with open(f"data/{filename}", "w", encoding="utf-8") as f: json.dump(data, f, ensure_ascii=False, indent=2) return True except: return False def get_completion_stats(completed_lessons: List[str], curriculum: List[Dict]) -> Dict: """حساب إحصائيات الإنجاز المفصلة""" total_lessons = sum(len(unit.get("lessons", [])) for unit in curriculum) completed = len(completed_lessons) total_experiments = len([u for u in curriculum if u.get("experiment")]) completed_experiments = 0 # يمكن تتبعها بشكل منفصل return { "lessons": { "completed": completed, "total": total_lessons, "percentage": round(completed / total_lessons * 100, 1) if total_lessons > 0 else 0 }, "experiments": { "completed": completed_experiments, "total": total_experiments, "percentage": round(completed_experiments / total_experiments * 100, 1) if total_experiments > 0 else 0 }, "overall_percentage": round((completed + completed_experiments) / (total_lessons + total_experiments) * 100, 1) }