diff --git a/config/__pycache__/settings.cpython-311.pyc b/config/__pycache__/settings.cpython-311.pyc index 8c9c2f7..7ccceb4 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 8b4d2d5..04a4eca 100644 --- a/config/settings.py +++ b/config/settings.py @@ -276,7 +276,7 @@ JAZZMIN_SETTINGS = { "core.Teacher": "fas fa-chalkboard-teacher", "core.Subject": "fas fa-book", "core.Resource": "fas fa-file-alt", - "core.EducationalLevel": "fas fa-layer-group", + "core.Classroom": "fas fa-layer-group", }, # Icons that are used when one is not manually specified "default_icon_parents": "fas fa-chevron-circle-right", @@ -298,4 +298,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 4069c42..e3f5397 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 new file mode 100644 index 0000000..aa5bbba Binary files /dev/null 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 3cf4a1d..d0c89de 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 31d12f8..544cde0 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 701adce..fdde019 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 287c66a..5832a93 100644 --- a/core/admin.py +++ b/core/admin.py @@ -1,26 +1,57 @@ from django.contrib import admin from django import forms -from .models import EducationalLevel, Teacher, Subject, Resource, Student +from django.utils.html import format_html +from django.urls import reverse +from .models import Classroom, Teacher, Subject, Resource, Student, City, Moderate -@admin.register(EducationalLevel) -class EducationalLevelAdmin(admin.ModelAdmin): - list_display = ('name_en', 'name_ar') +class ActionsModelAdmin(admin.ModelAdmin): + """ + Mixin to add an actions column to the admin list view. + """ + def actions_column(self, obj): + app_label = obj._meta.app_label + model_name = obj._meta.model_name + + edit_url = reverse(f'admin:{app_label}_{model_name}_change', args=[obj.pk]) + delete_url = reverse(f'admin:{app_label}_{model_name}_delete', args=[obj.pk]) + + return format_html( + '
' + '' + '' + '
', + edit_url, + delete_url + ) + actions_column.short_description = 'Actions' + +@admin.register(Classroom) +class ClassroomAdmin(ActionsModelAdmin): + list_display = ('name_en', 'name_ar', 'actions_column') @admin.register(Teacher) -class TeacherAdmin(admin.ModelAdmin): - list_display = ('user', 'specialization') +class TeacherAdmin(ActionsModelAdmin): + list_display = ('user', 'specialization', 'actions_column') @admin.register(Subject) -class SubjectAdmin(admin.ModelAdmin): - list_display = ('name_en', 'name_ar', 'level', 'price', 'teacher') - list_filter = ('level', 'teacher') +class SubjectAdmin(ActionsModelAdmin): + list_display = ('name_en', 'name_ar', 'classroom', 'price', 'teacher', 'actions_column') + list_filter = ('classroom', 'teacher') search_fields = ('name_en', 'name_ar') +@admin.register(City) +class CityAdmin(ActionsModelAdmin): + list_display = ('name', 'actions_column') + +@admin.register(Moderate) +class ModerateAdmin(ActionsModelAdmin): + list_display = ('name', 'actions_column') + class ResourceAdminForm(forms.ModelForm): - educational_level = forms.ModelChoiceField( - queryset=EducationalLevel.objects.all(), + classroom = forms.ModelChoiceField( + queryset=Classroom.objects.all(), required=True, - label="Filter by Educational Level" + label="Filter by Classroom" ) class Meta: @@ -30,22 +61,22 @@ class ResourceAdminForm(forms.ModelForm): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) if self.instance and self.instance.pk and self.instance.subject: - self.fields['educational_level'].initial = self.instance.subject.level + self.fields['classroom'].initial = self.instance.subject.classroom @admin.register(Resource) -class ResourceAdmin(admin.ModelAdmin): +class ResourceAdmin(ActionsModelAdmin): form = ResourceAdminForm - list_display = ('title_en', 'subject', 'created_at') - list_filter = ('subject__level', 'subject') - fields = ('educational_level', 'subject', 'title_en', 'title_ar', 'file', 'link') + list_display = ('title_en', 'subject', 'created_at', 'actions_column') + list_filter = ('subject__classroom', 'subject') + fields = ('classroom', 'subject', 'title_en', 'title_ar', 'file', 'link') class Media: js = ('js/admin_resource.js',) @admin.register(Student) -class StudentAdmin(admin.ModelAdmin): - list_display = ('user', 'level', 'phone_number') - list_filter = ('level',) +class StudentAdmin(ActionsModelAdmin): + list_display = ('user', 'classroom', 'mobile_number', 'city', 'moderate', 'actions_column') + list_filter = ('classroom', 'city', 'moderate') filter_horizontal = ('subscribed_subjects',) class Media: @@ -53,6 +84,6 @@ class StudentAdmin(admin.ModelAdmin): def get_form(self, request, obj=None, **kwargs): form = super().get_form(request, obj, **kwargs) - if obj and obj.level: - form.base_fields['subscribed_subjects'].queryset = Subject.objects.filter(level=obj.level) + 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 diff --git a/core/forms.py b/core/forms.py new file mode 100644 index 0000000..d2bdeb4 --- /dev/null +++ b/core/forms.py @@ -0,0 +1,57 @@ +from django import forms +from django.contrib.auth.models import User +from .models import Student, Subject, Classroom + +class StudentRegistrationForm(forms.ModelForm): + first_name = forms.CharField(max_length=30, required=True, label="Full Name (First)") + last_name = forms.CharField(max_length=30, required=True, label="Full Name (Last)") + username = forms.CharField(max_length=150, required=True) + email = forms.EmailField(required=True) + password = forms.CharField(widget=forms.PasswordInput, required=True) + + classroom = forms.ModelChoiceField(queryset=Classroom.objects.all(), required=True, empty_label="Select Classroom") + subjects = forms.ModelMultipleChoiceField( + queryset=Subject.objects.all(), + required=False, + widget=forms.CheckboxSelectMultiple, + label="Select Subjects" + ) + + class Meta: + model = Student + fields = ['mobile_number', 'moderate', 'city', 'avatar'] + widgets = { + 'avatar': forms.FileInput(attrs={'accept': 'image/*', 'capture': 'camera'}), # generic hint, but we'll use custom JS + } + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + # optimize subjects loading + self.fields['subjects'].queryset = Subject.objects.none() + + if 'classroom' in self.data: + try: + classroom_id = int(self.data.get('classroom')) + self.fields['subjects'].queryset = Subject.objects.filter(classroom_id=classroom_id) + except (ValueError, TypeError): + pass + elif self.instance.pk and self.instance.classroom: + self.fields['subjects'].queryset = Subject.objects.filter(classroom=self.instance.classroom) + + def save(self, commit=True): + # We need to save the User first, then the Student + user = User.objects.create_user( + username=self.cleaned_data['username'], + email=self.cleaned_data['email'], + password=self.cleaned_data['password'], + first_name=self.cleaned_data['first_name'], + last_name=self.cleaned_data['last_name'] + ) + student = super().save(commit=False) + student.user = user + student.classroom = self.cleaned_data['classroom'] + + if commit: + student.save() + student.subscribed_subjects.set(self.cleaned_data['subjects']) + return student \ No newline at end of file diff --git a/core/management/commands/seed_data.py b/core/management/commands/seed_data.py index c143bb3..3918e96 100644 --- a/core/management/commands/seed_data.py +++ b/core/management/commands/seed_data.py @@ -1,6 +1,6 @@ from django.core.management.base import BaseCommand from django.contrib.auth.models import User -from core.models import EducationalLevel, Subject, Teacher +from core.models import Classroom, Subject, Teacher class Command(BaseCommand): help = 'Seeds the database with sample data' @@ -14,13 +14,13 @@ class Command(BaseCommand): teacher, _ = Teacher.objects.get_or_create(user=user, specialization='Mathematics') - # Create Educational Levels - l1, _ = EducationalLevel.objects.get_or_create( + # Create Classrooms + l1, _ = Classroom.objects.get_or_create( name_en='Primary School', name_ar='المرحلة الابتدائية', description='Grades 1-6' ) - l2, _ = EducationalLevel.objects.get_or_create( + l2, _ = Classroom.objects.get_or_create( name_en='High School', name_ar='المرحلة الثانوية', description='Grades 10-12' @@ -28,7 +28,7 @@ class Command(BaseCommand): # Create Subjects Subject.objects.get_or_create( - level=l1, + classroom=l1, teacher=teacher, name_en='Basic Math', name_ar='الرياضيات الأساسية', @@ -37,7 +37,7 @@ class Command(BaseCommand): price=20.00 ) Subject.objects.get_or_create( - level=l2, + classroom=l2, teacher=teacher, name_en='Physics', name_ar='الفيزياء', @@ -46,7 +46,7 @@ class Command(BaseCommand): price=50.00 ) Subject.objects.get_or_create( - level=l2, + classroom=l2, teacher=teacher, name_en='Chemistry', name_ar='الكيمياء', @@ -55,4 +55,4 @@ class Command(BaseCommand): price=45.00 ) - self.stdout.write(self.style.SUCCESS('Successfully seeded sample data')) + self.stdout.write(self.style.SUCCESS('Successfully seeded sample data')) \ No newline at end of file diff --git a/core/migrations/0002_rename_educationallevel_classroom_and_more.py b/core/migrations/0002_rename_educationallevel_classroom_and_more.py new file mode 100644 index 0000000..74c3896 --- /dev/null +++ b/core/migrations/0002_rename_educationallevel_classroom_and_more.py @@ -0,0 +1,21 @@ +# Generated by Django 5.2.7 on 2026-02-03 19:10 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0001_initial'), + ] + + operations = [ + migrations.RenameModel( + old_name='EducationalLevel', + new_name='Classroom', + ), + migrations.AlterModelOptions( + name='classroom', + options={'verbose_name': 'Classroom', 'verbose_name_plural': 'Classrooms'}, + ), + ] diff --git a/core/migrations/0003_rename_level_student_classroom_and_more.py b/core/migrations/0003_rename_level_student_classroom_and_more.py new file mode 100644 index 0000000..b9026dc --- /dev/null +++ b/core/migrations/0003_rename_level_student_classroom_and_more.py @@ -0,0 +1,23 @@ +# Generated by Django 5.2.7 on 2026-02-03 19:15 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0002_rename_educationallevel_classroom_and_more'), + ] + + operations = [ + migrations.RenameField( + model_name='student', + old_name='level', + new_name='classroom', + ), + migrations.RenameField( + model_name='subject', + old_name='level', + new_name='classroom', + ), + ] diff --git a/core/migrations/0004_student_avatar_student_city_student_moderate.py b/core/migrations/0004_student_avatar_student_city_student_moderate.py new file mode 100644 index 0000000..89992c2 --- /dev/null +++ b/core/migrations/0004_student_avatar_student_city_student_moderate.py @@ -0,0 +1,28 @@ +# Generated by Django 5.2.7 on 2026-02-04 02:54 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0003_rename_level_student_classroom_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='student', + name='avatar', + field=models.ImageField(blank=True, null=True, upload_to='students/', verbose_name='Picture'), + ), + migrations.AddField( + model_name='student', + name='city', + field=models.CharField(blank=True, choices=[('cairo', 'Cairo'), ('giza', 'Giza'), ('alexandria', 'Alexandria')], max_length=50, verbose_name='City'), + ), + migrations.AddField( + model_name='student', + name='moderate', + field=models.CharField(blank=True, choices=[('morning', 'Morning'), ('evening', 'Evening')], max_length=50, verbose_name='Moderate'), + ), + ] diff --git a/core/migrations/0005_city_moderate_student_mobile_number_and_more.py b/core/migrations/0005_city_moderate_student_mobile_number_and_more.py new file mode 100644 index 0000000..e5b8534 --- /dev/null +++ b/core/migrations/0005_city_moderate_student_mobile_number_and_more.py @@ -0,0 +1,51 @@ +# Generated by Django 5.2.7 on 2026-02-04 03:46 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0004_student_avatar_student_city_student_moderate'), + ] + + operations = [ + migrations.CreateModel( + name='City', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=100, verbose_name='Name')), + ], + options={ + 'verbose_name': 'City', + 'verbose_name_plural': 'Cities', + }, + ), + migrations.CreateModel( + name='Moderate', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=100, verbose_name='Name')), + ], + options={ + 'verbose_name': 'Moderate', + 'verbose_name_plural': 'Moderates', + }, + ), + migrations.AddField( + model_name='student', + name='mobile_number', + field=models.CharField(blank=True, max_length=20, verbose_name='Mobile Number'), + ), + 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='City'), + ), + migrations.AlterField( + model_name='student', + name='moderate', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='core.moderate', verbose_name='Moderate'), + ), + ] diff --git a/core/migrations/__pycache__/0002_rename_educationallevel_classroom_and_more.cpython-311.pyc b/core/migrations/__pycache__/0002_rename_educationallevel_classroom_and_more.cpython-311.pyc new file mode 100644 index 0000000..8436b8e Binary files /dev/null and b/core/migrations/__pycache__/0002_rename_educationallevel_classroom_and_more.cpython-311.pyc differ diff --git a/core/migrations/__pycache__/0003_rename_level_student_classroom_and_more.cpython-311.pyc b/core/migrations/__pycache__/0003_rename_level_student_classroom_and_more.cpython-311.pyc new file mode 100644 index 0000000..676f49c Binary files /dev/null and b/core/migrations/__pycache__/0003_rename_level_student_classroom_and_more.cpython-311.pyc differ diff --git a/core/migrations/__pycache__/0004_student_avatar_student_city_student_moderate.cpython-311.pyc b/core/migrations/__pycache__/0004_student_avatar_student_city_student_moderate.cpython-311.pyc new file mode 100644 index 0000000..da1f27a Binary files /dev/null and b/core/migrations/__pycache__/0004_student_avatar_student_city_student_moderate.cpython-311.pyc differ diff --git a/core/migrations/__pycache__/0005_city_moderate_student_mobile_number_and_more.cpython-311.pyc b/core/migrations/__pycache__/0005_city_moderate_student_mobile_number_and_more.cpython-311.pyc new file mode 100644 index 0000000..646016c Binary files /dev/null and b/core/migrations/__pycache__/0005_city_moderate_student_mobile_number_and_more.cpython-311.pyc differ diff --git a/core/models.py b/core/models.py index ed0971a..5efebf0 100644 --- a/core/models.py +++ b/core/models.py @@ -11,16 +11,20 @@ class Teacher(models.Model): def __str__(self): return self.user.get_full_name() or self.user.username -class EducationalLevel(models.Model): +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) + class Meta: + verbose_name = _("Classroom") + verbose_name_plural = _("Classrooms") + def __str__(self): return self.name_en class Subject(models.Model): - level = models.ForeignKey(EducationalLevel, on_delete=models.CASCADE, related_name='subjects') + classroom = models.ForeignKey(Classroom, on_delete=models.CASCADE, related_name='subjects') teacher = models.ForeignKey(Teacher, on_delete=models.SET_NULL, null=True, blank=True, related_name='subjects') name_en = models.CharField(_("Name (English)"), max_length=200) name_ar = models.CharField(_("Name (Arabic)"), max_length=200) @@ -45,11 +49,36 @@ class Resource(models.Model): def __str__(self): return self.title_en +class City(models.Model): + name = models.CharField(_("Name"), max_length=100) + + class Meta: + verbose_name = _("City") + verbose_name_plural = _("Cities") + + def __str__(self): + return self.name + +class Moderate(models.Model): + name = models.CharField(_("Name"), max_length=100) + + class Meta: + verbose_name = _("Moderate") + verbose_name_plural = _("Moderates") + + def __str__(self): + return self.name + class Student(models.Model): user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='student_profile') - level = models.ForeignKey(EducationalLevel, on_delete=models.SET_NULL, null=True, blank=True, related_name='students') + 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') + + moderate = models.ForeignKey(Moderate, on_delete=models.SET_NULL, null=True, blank=True, verbose_name=_("Moderate")) + 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) def __str__(self): return self.user.get_full_name() or self.user.username \ No newline at end of file diff --git a/core/templates/base.html b/core/templates/base.html index f9b5bbd..000cc1a 100644 --- a/core/templates/base.html +++ b/core/templates/base.html @@ -130,9 +130,11 @@ + {% if user.is_staff %} + {% endif %}
{% if user.is_authenticated %} - {% trans "Hello" %}, {{ user.username }} + {% else %} + {% trans "Register" %} {% trans "Login" %} {% endif %}
diff --git a/core/templates/core/profile.html b/core/templates/core/profile.html new file mode 100644 index 0000000..07e1156 --- /dev/null +++ b/core/templates/core/profile.html @@ -0,0 +1,77 @@ +{% extends 'base.html' %} +{% load i18n static %} + +{% block title %}{% trans "My Profile" %} | EduPlatform{% endblock %} + +{% block content %} +
+
+
+
+
+
+ {% if student_profile and student_profile.avatar %} + Profile + {% else %} +
+ {{ user.username|make_list|first|upper }} +
+ {% endif %} +
+

{{ user.get_full_name|default:user.username }}

+

{{ user.email }}

+ {% if student_profile and student_profile.classroom %} + {{ student_profile.classroom.name_en }} + {% elif user.is_staff %} + {% trans "Staff / Admin" %} + {% endif %} +
+
+ +
+ + {% if student_profile %} +

{% trans "Student Details" %}

+
+
+ +

{{ user.username }}

+
+
+ +

{{ student_profile.city|default:"-" }}

+
+
+ +

{{ student_profile.moderate|default:"-" }}

+
+
+ +

{{ student_profile.mobile_number|default:"-" }}

+
+
+ +

{% trans "My Subjects" %}

+ {% if student_profile.subscribed_subjects.all %} +
+ {% for subject in student_profile.subscribed_subjects.all %} + + {{ subject.name_en }} + {% trans "View" %} + + {% endfor %} +
+ {% else %} +

{% trans "No subjects subscribed yet." %}

+ {% endif %} + {% else %} +
+ {% trans "You are logged in as a staff member or user without a student profile." %} +
+ {% endif %} +
+
+
+
+
+{% endblock %} \ No newline at end of file diff --git a/core/templates/core/registration.html b/core/templates/core/registration.html new file mode 100644 index 0000000..01f554d --- /dev/null +++ b/core/templates/core/registration.html @@ -0,0 +1,262 @@ +{% extends 'base.html' %} +{% load i18n static %} + +{% block title %}{% trans "Student Registration" %}{% endblock %} + +{% block content %} +
+
+
+
+

{% trans "Student Registration" %}

+ +
+ {% csrf_token %} + + {% if form.errors %} +
+ {{ form.errors }} +
+ {% endif %} + + +
{% trans "Personal Information" %}
+
+
+ + {{ form.first_name }} +
+
+ + {{ form.last_name }} +
+
+ +
+ + {{ form.username }} +
+ +
+ + {{ form.email }} +
+ +
+ + {{ form.password }} +
+ + +
{% trans "Student Details" %}
+
+
+ + {{ form.mobile_number }} +
+
+ + {{ form.moderate }} +
+
+ + {{ form.city }} +
+
+ + +
+ + {{ form.classroom }} +
+ +
+ +
+

{% trans "Select a classroom to see subjects." %}

+
+
+ +
+

{% trans "Total Amount:" %}

+

0.00

+
+ + +
{% trans "Profile Picture" %}
+
+
+
+ +
+ + +
+
+
+ + +
+ {% trans "No photo taken" %} +
+ +
+
+
+ + {{ form.avatar }} +
+
+ + +
+
+
+
+
+{% endblock %} + +{% block extra_js %} + +{% endblock %} \ No newline at end of file diff --git a/core/templates/core/subject_detail.html b/core/templates/core/subject_detail.html index e92bcd8..49302d7 100644 --- a/core/templates/core/subject_detail.html +++ b/core/templates/core/subject_detail.html @@ -11,7 +11,7 @@ @@ -27,7 +27,7 @@ {% endif %}
- {% if LANGUAGE_CODE == 'ar' %}{{ subject.level.name_ar }}{% else %}{{ subject.level.name_en }}{% endif %} + {% if LANGUAGE_CODE == 'ar' %}{{ subject.classroom.name_ar }}{% else %}{{ subject.classroom.name_en }}{% endif %}
diff --git a/core/urls.py b/core/urls.py index db3ba87..defe932 100644 --- a/core/urls.py +++ b/core/urls.py @@ -6,4 +6,8 @@ urlpatterns = [ path('set-language//', views.set_language, name='set_language'), path('subject//', views.subject_detail, name='subject_detail'), path('ajax/get-subjects-by-level/', views.get_subjects_by_level, name='get_subjects_by_level'), -] + path('register/', views.register_student, name='register_student'), + path('ajax/get-classroom-subjects/', views.get_classroom_subjects, name='get_classroom_subjects'), + path('profile/', views.profile, name='profile'), + path('logout/', views.custom_logout, name='logout'), +] \ No newline at end of file diff --git a/core/views.py b/core/views.py index 0113265..665968d 100644 --- a/core/views.py +++ b/core/views.py @@ -3,11 +3,14 @@ from django.utils import translation from django.conf import settings from django.http import JsonResponse from django.contrib.admin.views.decorators import staff_member_required -from .models import EducationalLevel, Subject, Teacher +from django.contrib.auth.decorators import login_required +from django.contrib.auth import logout +from .models import Classroom, Subject, Teacher, Student +from .forms import StudentRegistrationForm def index(request): # Fetch levels with their related subjects using prefetch_related for efficiency - levels = EducationalLevel.objects.prefetch_related('subjects').all() + levels = Classroom.objects.prefetch_related('subjects').all() context = { 'levels': levels, @@ -32,8 +35,41 @@ def subject_detail(request, pk): @staff_member_required def get_subjects_by_level(request): - level_id = request.GET.get('level_id') + level_id = request.GET.get('level_id') or request.GET.get('classroom_id') if not level_id: return JsonResponse([], safe=False) - subjects = Subject.objects.filter(level_id=level_id).values('id', 'name_en', 'name_ar') - return JsonResponse(list(subjects), safe=False) \ No newline at end of file + subjects = Subject.objects.filter(classroom_id=level_id).values('id', 'name_en', 'name_ar') + return JsonResponse(list(subjects), safe=False) + +def get_classroom_subjects(request): + """Public endpoint for registration form""" + classroom_id = request.GET.get('classroom_id') + if not classroom_id: + return JsonResponse([], safe=False) + subjects = Subject.objects.filter(classroom_id=classroom_id).values('id', 'name_en', 'price') + return JsonResponse(list(subjects), safe=False) + +def register_student(request): + if request.method == 'POST': + form = StudentRegistrationForm(request.POST, request.FILES) + if form.is_valid(): + student = form.save() + # Redirect to success page or login + return redirect('index') # Placeholder + else: + form = StudentRegistrationForm() + + return render(request, 'core/registration.html', {'form': form}) + +@login_required +def profile(request): + try: + student_profile = request.user.student_profile + except Student.DoesNotExist: + student_profile = None + + return render(request, 'core/profile.html', {'student_profile': student_profile}) + +def custom_logout(request): + logout(request) + return redirect('index') diff --git a/static/js/admin_resource.js b/static/js/admin_resource.js index c1b837f..514a034 100644 --- a/static/js/admin_resource.js +++ b/static/js/admin_resource.js @@ -2,12 +2,12 @@ 'use strict'; $(function() { console.log("Admin Resource JS loaded"); - var $levelSelect = $('#id_educational_level'); + var $levelSelect = $('#id_classroom'); var $subjectSelect = $('#id_subject'); // Check if elements exist if (!$levelSelect.length) { - console.warn("Educational Level field #id_educational_level not found"); + console.warn("Classroom field #id_classroom not found"); } if (!$subjectSelect.length) { console.warn("Subject field #id_subject not found"); @@ -17,7 +17,7 @@ var isArabic = $('html').attr('lang') === 'ar' || $('body').hasClass('rtl'); function updateSubjects(levelId, currentSubjectId) { - console.log("Updating subjects for level:", levelId); + console.log("Updating subjects for classroom:", levelId); // If no level selected, clear subjects if (!levelId) { $subjectSelect.html(''); diff --git a/static/js/admin_student.js b/static/js/admin_student.js index aa01ef6..143a254 100644 --- a/static/js/admin_student.js +++ b/static/js/admin_student.js @@ -1,7 +1,7 @@ (function($) { 'use strict'; $(function() { - var $levelSelect = $('#id_level'); + var $levelSelect = $('#id_classroom'); var $subjectSelect = $('#id_subscribed_subjects'); // Check if we are in the admin change form if (!$levelSelect.length || !$subjectSelect.length) return; @@ -75,4 +75,4 @@ updateSubjects($levelSelect.val(), selectedValues); } }); -})(django.jQuery); +})(django.jQuery); \ No newline at end of file diff --git a/staticfiles/js/admin_resource.js b/staticfiles/js/admin_resource.js index c1b837f..514a034 100644 --- a/staticfiles/js/admin_resource.js +++ b/staticfiles/js/admin_resource.js @@ -2,12 +2,12 @@ 'use strict'; $(function() { console.log("Admin Resource JS loaded"); - var $levelSelect = $('#id_educational_level'); + var $levelSelect = $('#id_classroom'); var $subjectSelect = $('#id_subject'); // Check if elements exist if (!$levelSelect.length) { - console.warn("Educational Level field #id_educational_level not found"); + console.warn("Classroom field #id_classroom not found"); } if (!$subjectSelect.length) { console.warn("Subject field #id_subject not found"); @@ -17,7 +17,7 @@ var isArabic = $('html').attr('lang') === 'ar' || $('body').hasClass('rtl'); function updateSubjects(levelId, currentSubjectId) { - console.log("Updating subjects for level:", levelId); + console.log("Updating subjects for classroom:", levelId); // If no level selected, clear subjects if (!levelId) { $subjectSelect.html(''); diff --git a/staticfiles/js/admin_student.js b/staticfiles/js/admin_student.js index aa01ef6..143a254 100644 --- a/staticfiles/js/admin_student.js +++ b/staticfiles/js/admin_student.js @@ -1,7 +1,7 @@ (function($) { 'use strict'; $(function() { - var $levelSelect = $('#id_level'); + var $levelSelect = $('#id_classroom'); var $subjectSelect = $('#id_subscribed_subjects'); // Check if we are in the admin change form if (!$levelSelect.length || !$subjectSelect.length) return; @@ -75,4 +75,4 @@ updateSubjects($levelSelect.val(), selectedValues); } }); -})(django.jQuery); +})(django.jQuery); \ No newline at end of file