diff --git a/assets/pasted-20260204-135454-c72e44de.png b/assets/pasted-20260204-135454-c72e44de.png new file mode 100644 index 0000000..1aa3dfd Binary files /dev/null and b/assets/pasted-20260204-135454-c72e44de.png differ diff --git a/config/__pycache__/settings.cpython-311.pyc b/config/__pycache__/settings.cpython-311.pyc index fa65535..eae1cef 100644 Binary files a/config/__pycache__/settings.cpython-311.pyc and b/config/__pycache__/settings.cpython-311.pyc differ diff --git a/config/settings.py b/config/settings.py index 7c015e4..6d4fdfd 100644 --- a/config/settings.py +++ b/config/settings.py @@ -138,7 +138,7 @@ AUTH_PASSWORD_VALIDATORS = [ # Internationalization # https://docs.djangoproject.com/en/5.2/topics/i18n/ -LANGUAGE_CODE = 'en-us' +LANGUAGE_CODE = 'ar' TIME_ZONE = 'UTC' @@ -307,4 +307,4 @@ JAZZMIN_SETTINGS = { "use_google_fonts_cdn": True, # Whether to show the UI customizer on the sidebar "show_ui_builder": True, -} +} \ No newline at end of file diff --git a/core/__pycache__/admin.cpython-311.pyc b/core/__pycache__/admin.cpython-311.pyc index f34b19d..066022d 100644 Binary files a/core/__pycache__/admin.cpython-311.pyc and b/core/__pycache__/admin.cpython-311.pyc differ diff --git a/core/__pycache__/forms.cpython-311.pyc b/core/__pycache__/forms.cpython-311.pyc index 7abaee6..0327d5e 100644 Binary files a/core/__pycache__/forms.cpython-311.pyc and b/core/__pycache__/forms.cpython-311.pyc differ diff --git a/core/__pycache__/models.cpython-311.pyc b/core/__pycache__/models.cpython-311.pyc index ebebf1c..258e187 100644 Binary files a/core/__pycache__/models.cpython-311.pyc and b/core/__pycache__/models.cpython-311.pyc differ diff --git a/core/__pycache__/urls.cpython-311.pyc b/core/__pycache__/urls.cpython-311.pyc index 2cb7086..02ad606 100644 Binary files a/core/__pycache__/urls.cpython-311.pyc and b/core/__pycache__/urls.cpython-311.pyc differ diff --git a/core/__pycache__/views.cpython-311.pyc b/core/__pycache__/views.cpython-311.pyc index bd12b3a..ded796e 100644 Binary files a/core/__pycache__/views.cpython-311.pyc and b/core/__pycache__/views.cpython-311.pyc differ diff --git a/core/admin.py b/core/admin.py index 7e25da6..df378f7 100644 --- a/core/admin.py +++ b/core/admin.py @@ -17,13 +17,13 @@ class ActionsModelAdmin(admin.ModelAdmin): return format_html( '
', edit_url, delete_url ) - actions_column.short_description = 'Actions' + actions_column.short_description = 'إجراءات' @admin.register(Classroom) class ClassroomAdmin(ActionsModelAdmin): @@ -42,7 +42,7 @@ class SubjectAdmin(ActionsModelAdmin): def get_teachers(self, obj): return ", ".join([str(t) for t in obj.teachers.all()]) - get_teachers.short_description = 'Teachers' + get_teachers.short_description = 'المعلمون' @admin.register(City) class CityAdmin(ActionsModelAdmin): @@ -57,7 +57,7 @@ class ResourceAdminForm(forms.ModelForm): classroom = forms.ModelChoiceField( queryset=Classroom.objects.all(), required=True, - label="Filter by Classroom" + label="تصفية حسب الصف" ) class Meta: @@ -92,4 +92,4 @@ class StudentAdmin(ActionsModelAdmin): form = super().get_form(request, obj, **kwargs) if obj and obj.classroom: form.base_fields['subscribed_subjects'].queryset = Subject.objects.filter(classroom=obj.classroom) - return form \ No newline at end of file + return form diff --git a/core/forms.py b/core/forms.py index 813bec2..4b0fc9f 100644 --- a/core/forms.py +++ b/core/forms.py @@ -1,25 +1,31 @@ from django import forms from django.contrib.auth.models import User -from .models import Student, Subject, Classroom, City, Governorate +from .models import Student, Subject, Classroom, City, Governorate, Teacher, Resource class StudentRegistrationForm(forms.ModelForm): - full_name = forms.CharField(max_length=150, required=True, label="Full Name") - username = forms.CharField(max_length=150, required=True) - email = forms.EmailField(required=True) - password = forms.CharField(widget=forms.PasswordInput, required=True) - password_confirm = forms.CharField(widget=forms.PasswordInput, required=True, label="Confirm Password") + full_name = forms.CharField(max_length=150, required=True, label="الاسم الكامل") + username = forms.CharField(max_length=150, required=True, label="اسم المستخدم") + email = forms.EmailField(required=True, label="البريد الإلكتروني") + password = forms.CharField(widget=forms.PasswordInput, required=True, label="كلمة المرور") + password_confirm = forms.CharField(widget=forms.PasswordInput, required=True, label="تأكيد كلمة المرور") - classroom = forms.ModelChoiceField(queryset=Classroom.objects.all(), required=True, empty_label="Select Classroom") + classroom = forms.ModelChoiceField(queryset=Classroom.objects.all(), required=True, empty_label="اختر الصف", label="الصف الدراسي") subjects = forms.ModelMultipleChoiceField( queryset=Subject.objects.all(), required=False, widget=forms.CheckboxSelectMultiple, - label="Select Subjects" + label="اختر المواد" ) class Meta: model = Student fields = ['mobile_number', 'governorate', 'city', 'avatar'] + labels = { + 'mobile_number': 'رقم الهاتف', + 'governorate': 'المحافظة', + 'city': 'المدينة', + 'avatar': 'الصورة الشخصية' + } widgets = { 'avatar': forms.FileInput(attrs={'accept': 'image/*', 'capture': 'camera'}), } @@ -65,7 +71,7 @@ class StudentRegistrationForm(forms.ModelForm): password_confirm = cleaned_data.get("password_confirm") if password and password_confirm and password != password_confirm: - self.add_error('password_confirm', "Passwords do not match") + self.add_error('password_confirm', "كلمات المرور غير متطابقة") return cleaned_data @@ -91,4 +97,59 @@ class StudentRegistrationForm(forms.ModelForm): if commit: student.save() student.subscribed_subjects.set(self.cleaned_data['subjects']) - return student \ No newline at end of file + return student + +class TeacherProfileForm(forms.ModelForm): + first_name = forms.CharField(max_length=30, required=True, label="الاسم الأول") + last_name = forms.CharField(max_length=150, required=True, label="الاسم الأخير") + email = forms.EmailField(required=True, label="البريد الإلكتروني") + + class Meta: + model = Teacher + fields = ['bio', 'specialization', 'avatar'] + labels = { + 'bio': 'نبذة عني', + 'specialization': 'التخصص', + 'avatar': 'الصورة الشخصية' + } + widgets = { + 'bio': forms.Textarea(attrs={'rows': 4}), + } + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + if self.instance and self.instance.user: + self.fields['first_name'].initial = self.instance.user.first_name + self.fields['last_name'].initial = self.instance.user.last_name + self.fields['email'].initial = self.instance.user.email + + for field_name, field in self.fields.items(): + field.widget.attrs['class'] = 'form-control' + + def save(self, commit=True): + teacher = super().save(commit=False) + if commit: + user = teacher.user + user.first_name = self.cleaned_data['first_name'] + user.last_name = self.cleaned_data['last_name'] + user.email = self.cleaned_data['email'] + user.save() + teacher.save() + return teacher + +class ResourceForm(forms.ModelForm): + class Meta: + model = Resource + fields = ['title_ar', 'title_en', 'resource_type', 'file', 'link'] + labels = { + 'title_ar': 'العنوان (عربي)', + 'title_en': 'العنوان (إنجليزي)', + 'resource_type': 'نوع المصدر', + 'file': 'الملف', + 'link': 'الرابط' + } + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + for field_name, field in self.fields.items(): + field.widget.attrs['class'] = 'form-control' diff --git a/core/migrations/0014_alter_city_options_alter_classroom_options_and_more.py b/core/migrations/0014_alter_city_options_alter_classroom_options_and_more.py new file mode 100644 index 0000000..fb778ff --- /dev/null +++ b/core/migrations/0014_alter_city_options_alter_classroom_options_and_more.py @@ -0,0 +1,314 @@ +# Generated by Django 5.2.7 on 2026-02-04 10:25 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0013_resource_resource_type_alter_resource_link'), + ] + + operations = [ + migrations.AlterModelOptions( + name='city', + options={'verbose_name': 'مدينة', 'verbose_name_plural': 'المدن'}, + ), + migrations.AlterModelOptions( + name='classroom', + options={'verbose_name': 'صف دراسي', 'verbose_name_plural': 'الصفوف الدراسية'}, + ), + migrations.AlterModelOptions( + name='governorate', + options={'verbose_name': 'محافظة', 'verbose_name_plural': 'المحافظات'}, + ), + migrations.AlterModelOptions( + name='platformsettings', + options={'verbose_name': 'إعدادات المنصة', 'verbose_name_plural': 'إعدادات المنصة'}, + ), + migrations.AlterModelOptions( + name='resource', + options={'verbose_name': 'مصدر تعليمي', 'verbose_name_plural': 'المصادر التعليمية'}, + ), + migrations.AlterModelOptions( + name='student', + options={'verbose_name': 'طالب', 'verbose_name_plural': 'الطلاب'}, + ), + migrations.AlterModelOptions( + name='subject', + options={'verbose_name': 'مادة دراسية', 'verbose_name_plural': 'المواد الدراسية'}, + ), + migrations.AlterModelOptions( + name='teacher', + options={'verbose_name': 'معلم', 'verbose_name_plural': 'المعلمون'}, + ), + migrations.AlterModelOptions( + name='thawaniconfiguration', + options={'verbose_name': 'إعدادات ثواني', 'verbose_name_plural': 'إعدادات ثواني'}, + ), + migrations.AlterModelOptions( + name='wablasconfiguration', + options={'verbose_name': 'إعدادات Wablas', 'verbose_name_plural': 'إعدادات Wablas'}, + ), + migrations.AlterField( + model_name='city', + name='governorate', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='cities', to='core.governorate', verbose_name='المحافظة'), + ), + migrations.AlterField( + model_name='city', + name='name_ar', + field=models.CharField(default='', max_length=100, verbose_name='الاسم (عربي)'), + ), + migrations.AlterField( + model_name='city', + name='name_en', + field=models.CharField(default='', max_length=100, verbose_name='الاسم (إنجليزي)'), + ), + migrations.AlterField( + model_name='classroom', + name='description', + field=models.TextField(blank=True, verbose_name='الوصف'), + ), + migrations.AlterField( + model_name='classroom', + name='name_ar', + field=models.CharField(max_length=100, verbose_name='الاسم (عربي)'), + ), + migrations.AlterField( + model_name='classroom', + name='name_en', + field=models.CharField(max_length=100, verbose_name='الاسم (إنجليزي)'), + ), + migrations.AlterField( + model_name='governorate', + name='name_ar', + field=models.CharField(default='', max_length=100, verbose_name='الاسم (عربي)'), + ), + migrations.AlterField( + model_name='governorate', + name='name_en', + field=models.CharField(default='', max_length=100, verbose_name='الاسم (إنجليزي)'), + ), + migrations.AlterField( + model_name='platformsettings', + name='address', + field=models.TextField(blank=True, null=True, verbose_name='العنوان'), + ), + migrations.AlterField( + model_name='platformsettings', + name='contact_email', + field=models.EmailField(blank=True, max_length=254, null=True, verbose_name='بريد التواصل'), + ), + migrations.AlterField( + model_name='platformsettings', + name='contact_phone', + field=models.CharField(blank=True, max_length=20, null=True, verbose_name='هاتف التواصل'), + ), + migrations.AlterField( + model_name='platformsettings', + name='description', + field=models.TextField(blank=True, null=True, verbose_name='وصف المنصة'), + ), + migrations.AlterField( + model_name='platformsettings', + name='facebook_link', + field=models.URLField(blank=True, null=True, verbose_name='رابط فيسبوك'), + ), + migrations.AlterField( + model_name='platformsettings', + name='instagram_link', + field=models.URLField(blank=True, null=True, verbose_name='رابط انستغرام'), + ), + migrations.AlterField( + model_name='platformsettings', + name='logo', + field=models.ImageField(blank=True, null=True, upload_to='platform/', verbose_name='الشعار'), + ), + migrations.AlterField( + model_name='platformsettings', + name='name', + field=models.CharField(default='منصتي التعليمية', max_length=100, verbose_name='اسم المنصة'), + ), + migrations.AlterField( + model_name='platformsettings', + name='twitter_link', + field=models.URLField(blank=True, null=True, verbose_name='رابط تويتر'), + ), + migrations.AlterField( + model_name='resource', + name='file', + field=models.FileField(blank=True, null=True, upload_to='resources/', verbose_name='الملف'), + ), + migrations.AlterField( + model_name='resource', + name='link', + field=models.URLField(blank=True, help_text='رابط YouTube للفيديو، أو رابط خارجي.', verbose_name='الرابط'), + ), + migrations.AlterField( + model_name='resource', + name='resource_type', + field=models.CharField(choices=[('FILE', 'ملف (PDF, Doc, إلخ)'), ('VIDEO', 'فيديو (YouTube)'), ('LINK', 'رابط خارجي')], default='FILE', max_length=10, verbose_name='نوع المصدر'), + ), + migrations.AlterField( + model_name='resource', + name='subject', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='resources', to='core.subject', verbose_name='المادة'), + ), + migrations.AlterField( + model_name='resource', + name='title_ar', + field=models.CharField(max_length=200, verbose_name='العنوان (عربي)'), + ), + migrations.AlterField( + model_name='resource', + name='title_en', + field=models.CharField(max_length=200, verbose_name='العنوان (إنجليزي)'), + ), + migrations.AlterField( + model_name='student', + name='avatar', + field=models.ImageField(blank=True, null=True, upload_to='students/', verbose_name='الصورة الشخصية'), + ), + migrations.AlterField( + model_name='student', + name='city', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='core.city', verbose_name='المدينة'), + ), + migrations.AlterField( + model_name='student', + name='classroom', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='students', to='core.classroom', verbose_name='الصف'), + ), + migrations.AlterField( + model_name='student', + name='email_otp_code', + field=models.CharField(blank=True, max_length=6, null=True, verbose_name='رمز تفعيل البريد'), + ), + migrations.AlterField( + model_name='student', + name='governorate', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='core.governorate', verbose_name='المحافظة'), + ), + migrations.AlterField( + model_name='student', + name='is_email_verified', + field=models.BooleanField(default=False, verbose_name='هل البريد مفعل؟'), + ), + migrations.AlterField( + model_name='student', + name='mobile_number', + field=models.CharField(blank=True, max_length=20, verbose_name='رقم الجوال'), + ), + migrations.AlterField( + model_name='student', + name='phone_number', + field=models.CharField(blank=True, max_length=20, verbose_name='رقم الهاتف الأرضي'), + ), + migrations.AlterField( + model_name='student', + name='subscribed_subjects', + field=models.ManyToManyField(blank=True, related_name='subscribers', to='core.subject', verbose_name='المواد المسجلة'), + ), + migrations.AlterField( + model_name='subject', + name='classroom', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='subjects', to='core.classroom', verbose_name='الصف'), + ), + migrations.AlterField( + model_name='subject', + name='description_ar', + field=models.TextField(blank=True, verbose_name='الوصف (عربي)'), + ), + migrations.AlterField( + model_name='subject', + name='description_en', + field=models.TextField(blank=True, verbose_name='الوصف (إنجليزي)'), + ), + migrations.AlterField( + model_name='subject', + name='google_drive_link', + field=models.URLField(blank=True, verbose_name='رابط Google Drive'), + ), + migrations.AlterField( + model_name='subject', + name='google_meet_link', + field=models.URLField(blank=True, verbose_name='رابط Google Meet'), + ), + migrations.AlterField( + model_name='subject', + name='image', + field=models.ImageField(blank=True, null=True, upload_to='subjects/', verbose_name='الصورة'), + ), + migrations.AlterField( + model_name='subject', + name='name_ar', + field=models.CharField(max_length=200, verbose_name='الاسم (عربي)'), + ), + migrations.AlterField( + model_name='subject', + name='name_en', + field=models.CharField(max_length=200, verbose_name='الاسم (إنجليزي)'), + ), + migrations.AlterField( + model_name='subject', + name='price', + field=models.DecimalField(decimal_places=2, default=0.0, max_digits=10, verbose_name='السعر'), + ), + migrations.AlterField( + model_name='subject', + name='teachers', + field=models.ManyToManyField(blank=True, related_name='subjects', to='core.teacher', verbose_name='المعلمون'), + ), + migrations.AlterField( + model_name='subject', + name='youtube_live_url', + field=models.URLField(blank=True, help_text='ضع رابط بث YouTube هنا للبثوث الكبيرة (500+ طالب).', verbose_name='رابط بث YouTube'), + ), + migrations.AlterField( + model_name='teacher', + name='avatar', + field=models.ImageField(blank=True, null=True, upload_to='teachers/', verbose_name='الصورة الشخصية'), + ), + migrations.AlterField( + model_name='teacher', + name='bio', + field=models.TextField(blank=True, verbose_name='نبذة'), + ), + migrations.AlterField( + model_name='teacher', + name='specialization', + field=models.CharField(blank=True, max_length=255, verbose_name='التخصص'), + ), + migrations.AlterField( + model_name='thawaniconfiguration', + name='api_key', + field=models.CharField(help_text='Thawani Secret Key', max_length=255, verbose_name='API Key'), + ), + migrations.AlterField( + model_name='thawaniconfiguration', + name='is_sandbox', + field=models.BooleanField(default=True, help_text='اختر هذا الخيار لاستخدام بيئة التجربة', verbose_name='وضع التجربة (Sandbox)'), + ), + migrations.AlterField( + model_name='thawaniconfiguration', + name='publishable_key', + field=models.CharField(help_text='Thawani Publishable Key', max_length=255, verbose_name='Publishable Key'), + ), + migrations.AlterField( + model_name='wablasconfiguration', + name='api_token', + field=models.CharField(max_length=255, verbose_name='API Token'), + ), + migrations.AlterField( + model_name='wablasconfiguration', + name='api_url', + field=models.URLField(default='https://texas.wablas.com', verbose_name='رابط API'), + ), + migrations.AlterField( + model_name='wablasconfiguration', + name='secret_key', + field=models.CharField(blank=True, max_length=255, null=True, verbose_name='Secret Key'), + ), + ] diff --git a/core/migrations/__pycache__/0014_alter_city_options_alter_classroom_options_and_more.cpython-311.pyc b/core/migrations/__pycache__/0014_alter_city_options_alter_classroom_options_and_more.cpython-311.pyc new file mode 100644 index 0000000..12f78db Binary files /dev/null and b/core/migrations/__pycache__/0014_alter_city_options_alter_classroom_options_and_more.cpython-311.pyc differ diff --git a/core/models.py b/core/models.py index 989c244..86d6d42 100644 --- a/core/models.py +++ b/core/models.py @@ -18,40 +18,48 @@ class SingletonModel(models.Model): class Teacher(models.Model): user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='teacher_profile') - bio = models.TextField(_("Bio"), blank=True) - avatar = models.ImageField(_("Avatar"), upload_to='teachers/', blank=True, null=True) - specialization = models.CharField(_("Specialization"), max_length=255, blank=True) + 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(_("Name (English)"), max_length=100) - name_ar = models.CharField(_("Name (Arabic)"), max_length=100) - description = models.TextField(_("Description"), blank=True) + name_en = models.CharField("الاسم (إنجليزي)", max_length=100) + name_ar = models.CharField("الاسم (عربي)", max_length=100) + description = models.TextField("الوصف", blank=True) class Meta: - verbose_name = _("Classroom") - verbose_name_plural = _("Classrooms") + verbose_name = "صف دراسي" + verbose_name_plural = "الصفوف الدراسية" def __str__(self): - return self.name_en + return self.name_ar or self.name_en class Subject(models.Model): - classroom = models.ForeignKey(Classroom, on_delete=models.CASCADE, related_name='subjects') - teachers = models.ManyToManyField(Teacher, blank=True, related_name='subjects') - name_en = models.CharField(_("Name (English)"), max_length=200) - name_ar = models.CharField(_("Name (Arabic)"), max_length=200) - description_en = models.TextField(_("Description (English)"), blank=True) - description_ar = models.TextField(_("Description (Arabic)"), blank=True) - price = models.DecimalField(_("Price"), max_digits=10, decimal_places=2, default=0.00) - image = models.ImageField(_("Image"), upload_to='subjects/', blank=True, null=True) - google_drive_link = models.URLField(_("Google Drive Link"), blank=True) - google_meet_link = models.URLField(_("Google Meet Link"), blank=True) - youtube_live_url = models.URLField(_("YouTube Live URL"), blank=True, help_text=_("Paste the YouTube Live link here for large broadcasts (500+ students).")) + 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_en + return self.name_ar or self.name_en def get_youtube_id(self): """Extracts the video ID from a YouTube URL.""" @@ -75,21 +83,25 @@ class Subject(models.Model): class Resource(models.Model): RESOURCE_TYPES = ( - ('FILE', _('File (PDF, Doc, etc.)')), - ('VIDEO', _('Video Link (YouTube)')), - ('LINK', _('External Link')), + ('FILE', 'ملف (PDF, Doc, إلخ)'), + ('VIDEO', 'فيديو (YouTube)'), + ('LINK', 'رابط خارجي'), ) - subject = models.ForeignKey(Subject, on_delete=models.CASCADE, related_name='resources') - title_en = models.CharField(_("Title (English)"), max_length=200) - title_ar = models.CharField(_("Title (Arabic)"), max_length=200) - resource_type = models.CharField(_("Resource Type"), max_length=10, choices=RESOURCE_TYPES, default='FILE') - file = models.FileField(_("File"), upload_to='resources/', blank=True, null=True) - link = models.URLField(_("Link"), blank=True, help_text=_("YouTube URL for Video type, or external URL for Link type.")) + 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_en + 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.""" @@ -118,42 +130,46 @@ class Resource(models.Model): return False class Governorate(models.Model): - name_en = models.CharField(_("Name (English)"), max_length=100, default="") - name_ar = models.CharField(_("Name (Arabic)"), max_length=100, default="") + name_en = models.CharField("الاسم (إنجليزي)", max_length=100, default="") + name_ar = models.CharField("الاسم (عربي)", max_length=100, default="") class Meta: - verbose_name = _("Governorate") - verbose_name_plural = _("Governorates") + verbose_name = "محافظة" + verbose_name_plural = "المحافظات" def __str__(self): - return f"{self.name_en}" + 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=_("Governorate"), null=True, blank=True) - name_en = models.CharField(_("Name (English)"), max_length=100, default="") - name_ar = models.CharField(_("Name (Arabic)"), max_length=100, default="") + 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 = _("City") - verbose_name_plural = _("Cities") + verbose_name = "مدينة" + verbose_name_plural = "المدن" def __str__(self): - return f"{self.name_en}" + 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') - phone_number = models.CharField(_("Phone Number"), max_length=20, blank=True) - mobile_number = models.CharField(_("Mobile Number"), max_length=20, blank=True) - subscribed_subjects = models.ManyToManyField(Subject, blank=True, related_name='subscribers') + 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=_("Governorate")) - city = models.ForeignKey(City, on_delete=models.SET_NULL, null=True, blank=True, verbose_name=_("City")) - avatar = models.ImageField(_("Picture"), upload_to='students/', blank=True, null=True) + 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) + 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 @@ -161,45 +177,45 @@ class Student(models.Model): # --- Configuration Models --- class WablasConfiguration(SingletonModel): - api_url = models.URLField(default="https://texas.wablas.com") - api_token = models.CharField(max_length=255) - secret_key = models.CharField(max_length=255, blank=True, null=True) + 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 Configuration" - verbose_name_plural = "Wablas Configuration" + verbose_name = "إعدادات Wablas" + verbose_name_plural = "إعدادات Wablas" def __str__(self): - return "Wablas Configuration" + return "إعدادات Wablas" class ThawaniConfiguration(SingletonModel): - api_key = models.CharField(max_length=255, help_text="Thawani Secret Key") - publishable_key = models.CharField(max_length=255, help_text="Thawani Publishable Key") - is_sandbox = models.BooleanField(default=True, help_text="Check to use Sandbox environment") + 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 = "Thawani Configuration" - verbose_name_plural = "Thawani Configuration" + verbose_name = "إعدادات ثواني" + verbose_name_plural = "إعدادات ثواني" def __str__(self): - return "Thawani Configuration" + return "إعدادات ثواني" class PlatformSettings(SingletonModel): - name = models.CharField(max_length=100, default="My School Platform") - 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) + 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) + 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 = "Platform Profile" - verbose_name_plural = "Platform Profile" + verbose_name = "إعدادات المنصة" + verbose_name_plural = "إعدادات المنصة" def __str__(self): - return "Platform Profile" \ No newline at end of file + return "إعدادات المنصة" diff --git a/core/templates/base.html b/core/templates/base.html index 0cc6e8a..8130aff 100644 --- a/core/templates/base.html +++ b/core/templates/base.html @@ -1,17 +1,14 @@ {% load i18n static %} - + -للمادة: {{ subject.name_ar }}
++ أنت على وشك حذف المصدر "{{ resource.title_ar }}". لا يمكن التراجع عن هذا الإجراء. +
+ + +المادة: {{ subject.name_ar }}
+- {% if LANGUAGE_CODE == 'ar' %} منصة تعليمية متكاملة تدعم الطلاب والمعلمين مع دروس حية ومصادر تعليمية متميزة. - {% else %} - A comprehensive educational platform supporting students and teachers with live classes and premium educational resources. - {% endif %}
{% trans "Join thousands of students learning today." %}
+انضم لآلاف الطلاب اليوم.
{% trans "Explore subjects tailored for each educational level." %}
+استكشف المواد المخصصة لكل مرحلة دراسية.
- {% if LANGUAGE_CODE == 'ar' %}{{ subject.description_ar|truncatewords:15 }}{% else %}{{ subject.description_en|truncatewords:15 }}{% endif %} + {{ subject.description_ar|truncatewords:15 }}
{% trans "Learn from our expert instructors." %}
+تعلم من نخبة من المعلمين.
{{ teacher.specialization|default:"Teacher" }}
+{{ teacher.specialization|default:"معلم" }}
- {{ teacher.bio|default:"No bio available."|truncatewords:20 }} + {{ teacher.bio|default:"لا توجد نبذة."|truncatewords:20 }}
- -{% trans "No teachers found at the moment." %}
+لا يوجد معلمين حالياً.
{% trans "Join interactive sessions via Google Meet." %}
+انضم لجلسات تفاعلية عبر Google Meet.
{% trans "Access course materials on Google Drive." %}
+الوصول لمواد الدورة عبر Google Drive.
{% trans "Secure payments with Thawani gateway." %}
+دفع آمن عبر بوابة ثواني.
{% trans "This class is being held on Google Meet. Click the button below to join." %}
+يقام هذا الدرس عبر Google Meet. انقر الزر أدناه للانضمام.
- {% trans "Join Google Meet" %} + انضم إلى Google Meet{% trans "Sign in to continue your learning journey" %}
+سجل الدخول لمتابعة رحلتك التعليمية
{{ user.email }}
{% if student_profile and student_profile.classroom %} - {{ student_profile.classroom.name_en }} + {{ student_profile.classroom.name_ar }} {% elif user.is_staff %} - {% trans "Staff / Admin" %} + طاقم / مسؤول {% endif %}{{ user.username }}
{{ student_profile.city|default:"-" }}
+ +{{ student_profile.city.name_ar|default:"-" }}
{{ student_profile.governorate|default:"-" }}
+ +{{ student_profile.governorate.name_ar|default:"-" }}
{{ student_profile.mobile_number|default:"-" }}
{% trans "No subjects subscribed yet." %}
+لم يتم الاشتراك في أي مواد بعد.
{% endif %} {% else %}{% trans "Join our learning platform today" %}
+انضم إلى منصتنا التعليمية اليوم