From 1c3e89c4ec70723fdf019bb34311a8d3fbb13e0c Mon Sep 17 00:00:00 2001 From: AN Date: Sun, 12 Jan 2025 14:39:59 +0000 Subject: [PATCH] u --- lms/accounts/middleware.py | 35 ++++++++++ .../migrations/0008_customuser_image.py | 18 +++++ lms/accounts/models.py | 18 +++-- lms/app/migrations/0002_course_image.py | 18 +++++ .../0003_course_is_paid_course_price.py | 23 ++++++ lms/app/migrations/0004_course_rating.py | 18 +++++ lms/app/models.py | 16 ++++- lms/app/serializers.py | 70 +------------------ lms/app/urls.py | 1 + lms/app/views.py | 33 ++++++++- 10 files changed, 168 insertions(+), 82 deletions(-) create mode 100644 lms/accounts/middleware.py create mode 100644 lms/accounts/migrations/0008_customuser_image.py create mode 100644 lms/app/migrations/0002_course_image.py create mode 100644 lms/app/migrations/0003_course_is_paid_course_price.py create mode 100644 lms/app/migrations/0004_course_rating.py diff --git a/lms/accounts/middleware.py b/lms/accounts/middleware.py new file mode 100644 index 0000000..3ae0f08 --- /dev/null +++ b/lms/accounts/middleware.py @@ -0,0 +1,35 @@ +from django.contrib.auth import get_user_model + + +User = get_user_model() + +class TransitionManager: + def __init__(self, get_response): + self.get_response = get_response + + def __call__(self, request): + # هذه الخطوة تتم قبل وصول الطلب إلى الفيو + print("قبل معالجة الطلب") + + # تمرير الطلب إلى الفيو والحصول على الاستجابة + response = self.get_response(request) + + # التحقق من وجود المستخدم + try: + user = User.objects.get(email=request.user.email) + match user.role: + case "student": + print("طالب") + case "instructor": + print("instructor") + case "admin": + print("مشرف") + case _: + print("القيمة غير معروفة") + except User.DoesNotExist: + print("المستخدم غير موجود") + + print("بعد معالجة الطلب") + + return response + diff --git a/lms/accounts/migrations/0008_customuser_image.py b/lms/accounts/migrations/0008_customuser_image.py new file mode 100644 index 0000000..2bd0fc9 --- /dev/null +++ b/lms/accounts/migrations/0008_customuser_image.py @@ -0,0 +1,18 @@ +# Generated by Django 5.0.10 on 2025-01-12 14:39 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('accounts', '0007_remove_customuser_username_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='customuser', + name='image', + field=models.ImageField(blank=True, null=True, upload_to='account/profile_image/'), + ), + ] diff --git a/lms/accounts/models.py b/lms/accounts/models.py index 7fd8a3d..68aac90 100644 --- a/lms/accounts/models.py +++ b/lms/accounts/models.py @@ -19,27 +19,25 @@ class CustomUserManager(BaseUserManager): class CustomUser(AbstractUser): - # إزالة الحقل username من AbstractUser + ROLE_CHOICES = { + 'student': 'Student', + 'instructor': 'Instructor', + 'admin': 'Admin', + } username = None first_name = None last_name = None - # الحقول الخاصة بالمستخدم المخصص email = models.EmailField(unique=True) full_name = models.CharField(max_length=255, null=True, blank=True) - ROLE_CHOICES = [ - ('student', 'Student'), - ('instructor', 'Instructor'), - ('admin', 'Admin'), - ] + image = models.ImageField(upload_to="account/profile_image/", null=True, blank=True) + role = models.CharField(max_length=20, choices=ROLE_CHOICES, null=True, blank=True) - # تخصيص مدير المستخدم objects = CustomUserManager() - # تحديد الحقول الأساسية USERNAME_FIELD = 'email' - REQUIRED_FIELDS = [] # قائمة الحقول المطلوبة لإنشاء مستخدم عبر createsuperuser + REQUIRED_FIELDS = [] def __str__(self): return self.email diff --git a/lms/app/migrations/0002_course_image.py b/lms/app/migrations/0002_course_image.py new file mode 100644 index 0000000..2bb500f --- /dev/null +++ b/lms/app/migrations/0002_course_image.py @@ -0,0 +1,18 @@ +# Generated by Django 5.0.10 on 2025-01-12 10:03 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('app', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='course', + name='image', + field=models.ImageField(null=True, upload_to='courses/image'), + ), + ] diff --git a/lms/app/migrations/0003_course_is_paid_course_price.py b/lms/app/migrations/0003_course_is_paid_course_price.py new file mode 100644 index 0000000..d17909c --- /dev/null +++ b/lms/app/migrations/0003_course_is_paid_course_price.py @@ -0,0 +1,23 @@ +# Generated by Django 5.0.10 on 2025-01-12 10:16 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('app', '0002_course_image'), + ] + + operations = [ + migrations.AddField( + model_name='course', + name='is_paid', + field=models.BooleanField(default=False), + ), + migrations.AddField( + model_name='course', + name='price', + field=models.DecimalField(blank=True, decimal_places=2, max_digits=10, null=True), + ), + ] diff --git a/lms/app/migrations/0004_course_rating.py b/lms/app/migrations/0004_course_rating.py new file mode 100644 index 0000000..a09c735 --- /dev/null +++ b/lms/app/migrations/0004_course_rating.py @@ -0,0 +1,18 @@ +# Generated by Django 5.0.10 on 2025-01-12 14:39 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('app', '0003_course_is_paid_course_price'), + ] + + operations = [ + migrations.AddField( + model_name='course', + name='rating', + field=models.PositiveSmallIntegerField(blank=True, null=True), + ), + ] diff --git a/lms/app/models.py b/lms/app/models.py index 56836c1..3c71f51 100644 --- a/lms/app/models.py +++ b/lms/app/models.py @@ -1,7 +1,7 @@ from django.db import models from uuid import uuid4 from django.contrib.auth import get_user_model - +from rest_framework.exceptions import ValidationError User = get_user_model() @@ -10,12 +10,24 @@ class Course(models.Model): id = models.UUIDField(primary_key=True, default=uuid4, editable=False) title = models.CharField(max_length=255, verbose_name="Course Title") description = models.TextField(verbose_name="Course Description") - instructor = models.ForeignKey(User, on_delete=models.CASCADE, related_name='courses_taught', verbose_name="Instructor") + image = models.ImageField(upload_to="courses/image", null=True) + is_paid = models.BooleanField(default=False) + price = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True) + instructor = models.ForeignKey(User, on_delete=models.CASCADE, related_name='courses_taught', verbose_name="Instructor") + rating = models.PositiveSmallIntegerField(null=True, blank=True) created_at = models.DateTimeField(auto_now_add=True, verbose_name="Created At") updated_at = models.DateTimeField(auto_now=True, verbose_name="Updated At") def str(self): return self.title + + def clean(self): + if self.is_paid and (self.price is None or self.price <= 0): + raise ValidationError({'price': 'Price must be set and greater than 0 for paid products.'}) + + if not self.is_paid and self.price: + raise ValidationError({'price': 'Price must be empty for free products.'}) + # Table for modules (Module) class Module(models.Model): diff --git a/lms/app/serializers.py b/lms/app/serializers.py index 900c5b2..cb6c755 100644 --- a/lms/app/serializers.py +++ b/lms/app/serializers.py @@ -7,79 +7,15 @@ from allauth.account.models import EmailAddress from dj_rest_auth.registration.serializers import RegisterSerializer -class CustomLoginSerializer(LoginSerializer): - email = serializers.EmailField(required=True) - password = serializers.CharField(style={'input_type': 'password'}) - - def validate(self, attrs): - email = attrs.get('email') - password = attrs.get('password') - - if not email or not password: - raise serializers.ValidationError("Please enter both email and password.") - - User = get_user_model() - users = User.objects.filter(email=email) - - if not users.exists(): - raise serializers.ValidationError("Incorrect email.") - - if users.count() > 1: - raise serializers.ValidationError("Multiple users found with this email. Please contact support.") - - user = users.first() - - if not user.check_password(password): - raise serializers.ValidationError("Incorrect password.") - - if not self.is_email_verified(user): - raise serializers.ValidationError("Email not verified. Please verify your email first.") - - attrs['user'] = user - return attrs - - - def is_email_verified(self, user): - if hasattr(user, 'email_verified'): - return user.email_verified - else: - try: - email_address = EmailAddress.objects.get(user=user, email=user.email) - return email_address.verified - except EmailAddress.DoesNotExist: - return False - -class CustomRegisterSerializer(RegisterSerializer): - full_name = serializers.CharField(required=True) - - def save(self, request): - user = super().save(request) - user.full_name = self.data.get('full_name', '') - user.save() - return user - - - - - - - - - - - - - - - -class CourseSerializer(serializers.HyperlinkedModelSerializer): +class CourseSerializer(serializers.ModelSerializer): instructor_name = serializers.CharField(source='instructor.username', read_only=True) class Meta: model = Course - fields = ['url', 'id', 'title', 'description', 'instructor_name', 'created_at', 'updated_at'] + fields = ['id', 'title', 'description', 'is_paid', 'price', 'image', 'instructor_name', 'created_at', 'updated_at'] + read_only_fields = ['created_at', 'updated_at'] class ModuleSerializer(serializers.HyperlinkedModelSerializer): class Meta: diff --git a/lms/app/urls.py b/lms/app/urls.py index 7da3919..0da36a6 100644 --- a/lms/app/urls.py +++ b/lms/app/urls.py @@ -3,6 +3,7 @@ from .views import * from rest_framework.routers import DefaultRouter router = DefaultRouter() +router.register(r'courses-read', CourseRead, basename='course-read') router.register(r'courses', CourseViewSet, basename='course') router.register(r'modules', ModuleViewSet, basename='modules') router.register(r'lessons', LessonViewSet, basename='lessons') diff --git a/lms/app/views.py b/lms/app/views.py index ce982ec..a49c92d 100644 --- a/lms/app/views.py +++ b/lms/app/views.py @@ -1,25 +1,44 @@ from django.shortcuts import render from .serializers import * from .models import * -from rest_framework.viewsets import ModelViewSet +from rest_framework.viewsets import ModelViewSet, ReadOnlyModelViewSet from rest_framework.response import Response from rest_framework import status from rest_framework.permissions import IsAuthenticated, BasePermission from .permissions import IsInstructor, IsAdmin + + + +class CourseRead(ReadOnlyModelViewSet): + permission_classes = [IsAuthenticated] + serializer_class = CourseSerializer + queryset = Course.objects.all() + + + class CourseViewSet(ModelViewSet): """ A ViewSet for viewing and editing Course instances. """ permission_classes = [IsAuthenticated,] - queryset = Course.objects.all() serializer_class = CourseSerializer + + def get_queryset(self): + """ + Return courses belonging to the authenticated user. + """ + user = self.request.user + return Course.objects.filter(instructor=user) def perform_create(self, serializer): """ Save the post data when creating a new course. """ - serializer.save(instructor=self.request.user) + user = self.request.user + + + serializer.save(instructor=user) def perform_update(self, serializer): """ @@ -44,6 +63,14 @@ class CourseViewSet(ModelViewSet): ) instance.delete() + + + + + + + + class ModuleViewSet(ModelViewSet): """ ViewSet for managing modules.