222 lines
9.5 KiB
Python
222 lines
9.5 KiB
Python
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
|
|
# Handle various formats:
|
|
# https://youtu.be/VIDEO_ID
|
|
# https://www.youtube.com/watch?v=VIDEO_ID
|
|
# https://www.youtube.com/live/VIDEO_ID
|
|
import re
|
|
patterns = [
|
|
r'(?:v=|\/)([0-9A-Za-z_-]{11}).*',
|
|
r'(?:youtu\.be\/)([0-9A-Za-z_-]{11})',
|
|
r'(?:live\/)([0-9A-Za-z_-]{11})',
|
|
]
|
|
for pattern in patterns:
|
|
match = re.search(pattern, self.youtube_live_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
|
|
|
|
patterns = [
|
|
r'(?:v=|\/)([0-9A-Za-z_-]{11}).*',
|
|
r'(?:youtu\.be\/)([0-9A-Za-z_-]{11})',
|
|
r'(?:live\/)([0-9A-Za-z_-]{11})',
|
|
]
|
|
for pattern in patterns:
|
|
match = re.search(pattern, self.link)
|
|
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 "إعدادات المنصة"
|