from django.db import models from django.contrib.auth.models import User from django.utils.translation import gettext_lazy as _ import re class SingletonModel(models.Model): class Meta: abstract = True def save(self, *args, **kwargs): self.pk = 1 super(SingletonModel, self).save(*args, **kwargs) @classmethod def load(cls): obj, created = cls.objects.get_or_create(pk=1) return obj class Teacher(models.Model): user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='teacher_profile') bio = models.TextField("نبذة", blank=True) avatar = models.ImageField("الصورة الشخصية", upload_to='teachers/', blank=True, null=True) specialization = models.CharField("التخصص", max_length=255, blank=True) class Meta: verbose_name = "معلم" verbose_name_plural = "المعلمون" def __str__(self): return self.user.get_full_name() or self.user.username class Classroom(models.Model): name_en = models.CharField("الاسم (إنجليزي)", max_length=100) name_ar = models.CharField("الاسم (عربي)", max_length=100) description = models.TextField("الوصف", blank=True) class Meta: verbose_name = "صف دراسي" verbose_name_plural = "الصفوف الدراسية" def __str__(self): return self.name_ar or self.name_en class Subject(models.Model): classroom = models.ForeignKey(Classroom, on_delete=models.CASCADE, related_name='subjects', verbose_name="الصف") teachers = models.ManyToManyField(Teacher, blank=True, related_name='subjects', verbose_name="المعلمون") name_en = models.CharField("الاسم (إنجليزي)", max_length=200) name_ar = models.CharField("الاسم (عربي)", max_length=200) description_en = models.TextField("الوصف (إنجليزي)", blank=True) description_ar = models.TextField("الوصف (عربي)", blank=True) price = models.DecimalField("السعر", max_digits=10, decimal_places=2, default=0.00) image = models.ImageField("الصورة", upload_to='subjects/', blank=True, null=True) google_drive_link = models.URLField("رابط Google Drive", blank=True) google_meet_link = models.URLField("رابط Google Meet", blank=True) youtube_live_url = models.URLField("رابط بث YouTube", blank=True, help_text="ضع رابط بث YouTube هنا للبثوث الكبيرة (500+ طالب).") class Meta: verbose_name = "مادة دراسية" verbose_name_plural = "المواد الدراسية" def __str__(self): return self.name_ar or self.name_en def get_youtube_id(self): """Extracts the video ID from a YouTube URL.""" if not self.youtube_live_url: return None url = self.youtube_live_url.strip() # Handle various formats: # https://youtu.be/VIDEO_ID # https://www.youtube.com/watch?v=VIDEO_ID # https://www.youtube.com/live/VIDEO_ID # https://www.youtube.com/embed/VIDEO_ID # https://www.youtube.com/shorts/VIDEO_ID patterns = [ r'(?:v=|\/)([0-9A-Za-z_-]{11})(?:[?&]|$)', r'(?:youtu\.be\/)([0-9A-Za-z_-]{11})', r'(?:live\/)([0-9A-Za-z_-]{11})', r'(?:embed\/)([0-9A-Za-z_-]{11})', r'(?:shorts\/)([0-9A-Za-z_-]{11})', ] for pattern in patterns: match = re.search(pattern, url) if match: return match.group(1) return None class Resource(models.Model): RESOURCE_TYPES = ( ('FILE', 'ملف (PDF, Doc, إلخ)'), ('VIDEO', 'فيديو (YouTube)'), ('LINK', 'رابط خارجي'), ) subject = models.ForeignKey(Subject, on_delete=models.CASCADE, related_name='resources', verbose_name="المادة") title_en = models.CharField("العنوان (إنجليزي)", max_length=200) title_ar = models.CharField("العنوان (عربي)", max_length=200) resource_type = models.CharField("نوع المصدر", max_length=10, choices=RESOURCE_TYPES, default='FILE') file = models.FileField("الملف", upload_to='resources/', blank=True, null=True) link = models.URLField("الرابط", blank=True, help_text="رابط YouTube للفيديو، أو رابط خارجي.") created_at = models.DateTimeField(auto_now_add=True) class Meta: verbose_name = "مصدر تعليمي" verbose_name_plural = "المصادر التعليمية" def __str__(self): return self.title_ar or self.title_en def get_youtube_id(self): """Extracts the video ID from the link if it is a YouTube URL.""" if not self.link or self.resource_type != 'VIDEO': return None url = self.link.strip() patterns = [ r'(?:v=|\/)([0-9A-Za-z_-]{11})(?:[?&]|$)', r'(?:youtu\.be\/)([0-9A-Za-z_-]{11})', r'(?:live\/)([0-9A-Za-z_-]{11})', r'(?:embed\/)([0-9A-Za-z_-]{11})', r'(?:shorts\/)([0-9A-Za-z_-]{11})', ] for pattern in patterns: match = re.search(pattern, url) if match: return match.group(1) return None def is_image(self): if self.file: return self.file.name.lower().endswith(('.png', '.jpg', '.jpeg', '.gif', '.webp')) return False def is_pdf(self): if self.file: return self.file.name.lower().endswith('.pdf') return False class Governorate(models.Model): name_en = models.CharField("الاسم (إنجليزي)", max_length=100, default="") name_ar = models.CharField("الاسم (عربي)", max_length=100, default="") class Meta: verbose_name = "محافظة" verbose_name_plural = "المحافظات" def __str__(self): return self.name_ar or f"{self.name_en}" class City(models.Model): governorate = models.ForeignKey(Governorate, on_delete=models.CASCADE, related_name='cities', verbose_name="المحافظة", null=True, blank=True) name_en = models.CharField("الاسم (إنجليزي)", max_length=100, default="") name_ar = models.CharField("الاسم (عربي)", max_length=100, default="") class Meta: verbose_name = "مدينة" verbose_name_plural = "المدن" def __str__(self): return self.name_ar or f"{self.name_en}" class Student(models.Model): user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='student_profile') classroom = models.ForeignKey(Classroom, on_delete=models.SET_NULL, null=True, blank=True, related_name='students', verbose_name="الصف") phone_number = models.CharField("رقم الهاتف الأرضي", max_length=20, blank=True) mobile_number = models.CharField("رقم الجوال", max_length=20, blank=True) subscribed_subjects = models.ManyToManyField(Subject, blank=True, related_name='subscribers', verbose_name="المواد المسجلة") governorate = models.ForeignKey(Governorate, on_delete=models.SET_NULL, null=True, blank=True, verbose_name="المحافظة") city = models.ForeignKey(City, on_delete=models.SET_NULL, null=True, blank=True, verbose_name="المدينة") avatar = models.ImageField("الصورة الشخصية", upload_to='students/', blank=True, null=True) # Email Verification email_otp_code = models.CharField("رمز تفعيل البريد", max_length=6, blank=True, null=True) is_email_verified = models.BooleanField("هل البريد مفعل؟", default=False) class Meta: verbose_name = "طالب" verbose_name_plural = "الطلاب" def __str__(self): return self.user.get_full_name() or self.user.username # --- Configuration Models --- class WablasConfiguration(SingletonModel): api_url = models.URLField("رابط API", default="https://texas.wablas.com") api_token = models.CharField("API Token", max_length=255) secret_key = models.CharField("Secret Key", max_length=255, blank=True, null=True) class Meta: verbose_name = "إعدادات Wablas" verbose_name_plural = "إعدادات Wablas" def __str__(self): return "إعدادات Wablas" class ThawaniConfiguration(SingletonModel): api_key = models.CharField("API Key", max_length=255, help_text="Thawani Secret Key") publishable_key = models.CharField("Publishable Key", max_length=255, help_text="Thawani Publishable Key") is_sandbox = models.BooleanField("وضع التجربة (Sandbox)", default=True, help_text="اختر هذا الخيار لاستخدام بيئة التجربة") class Meta: verbose_name = "إعدادات ثواني" verbose_name_plural = "إعدادات ثواني" def __str__(self): return "إعدادات ثواني" class PlatformSettings(SingletonModel): name = models.CharField("اسم المنصة", max_length=100, default="منصتي التعليمية") logo = models.ImageField("الشعار", upload_to='platform/', blank=True, null=True) description = models.TextField("وصف المنصة", blank=True, null=True) contact_email = models.EmailField("بريد التواصل", blank=True, null=True) contact_phone = models.CharField("هاتف التواصل", max_length=20, blank=True, null=True) address = models.TextField("العنوان", blank=True, null=True) # Social Media facebook_link = models.URLField("رابط فيسبوك", blank=True, null=True) twitter_link = models.URLField("رابط تويتر", blank=True, null=True) instagram_link = models.URLField("رابط انستغرام", blank=True, null=True) class Meta: verbose_name = "إعدادات المنصة" verbose_name_plural = "إعدادات المنصة" def __str__(self): return "إعدادات المنصة" class Package(models.Model): name_en = models.CharField("الاسم (إنجليزي)", max_length=200) name_ar = models.CharField("الاسم (عربي)", max_length=200) description_en = models.TextField("الوصف (إنجليزي)", blank=True) description_ar = models.TextField("الوصف (عربي)", blank=True) price = models.DecimalField("السعر", max_digits=10, decimal_places=2, default=0.00) classroom = models.ForeignKey(Classroom, on_delete=models.CASCADE, related_name='packages', verbose_name="الصف", null=True, blank=True) image = models.ImageField("الصورة", upload_to='packages/', blank=True, null=True) subjects = models.ManyToManyField(Subject, related_name='packages', verbose_name="المواد") is_active = models.BooleanField("نشط", default=True) class Meta: verbose_name = "باقة" verbose_name_plural = "الباقات" def __str__(self): return self.name_ar or self.name_en