u
This commit is contained in:
parent
1c3e89c4ec
commit
11c20c0474
11 changed files with 272 additions and 239 deletions
|
|
@ -4,8 +4,8 @@ from .models import *
|
|||
|
||||
@admin.register(Course)
|
||||
class CourseAdmin(admin.ModelAdmin):
|
||||
list_display = ('title', 'instructor', 'created_at', 'updated_at')
|
||||
search_fields = ('title', 'instructor__username')
|
||||
list_display = ('title', 'owner', 'created_at', 'updated_at')
|
||||
search_fields = ('title', 'owner__username')
|
||||
list_filter = ('created_at', 'updated_at')
|
||||
|
||||
@admin.register(Module)
|
||||
|
|
|
|||
18
lms/app/migrations/0005_rename_instructor_course_owner.py
Normal file
18
lms/app/migrations/0005_rename_instructor_course_owner.py
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 5.0.10 on 2025-01-14 08:17
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('app', '0004_course_rating'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RenameField(
|
||||
model_name='course',
|
||||
old_name='instructor',
|
||||
new_name='owner',
|
||||
),
|
||||
]
|
||||
21
lms/app/migrations/0006_module_created_by.py
Normal file
21
lms/app/migrations/0006_module_created_by.py
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
# Generated by Django 5.0.10 on 2025-01-14 11:08
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('app', '0005_rename_instructor_course_owner'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='module',
|
||||
name='created_by',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, verbose_name='Created By'),
|
||||
),
|
||||
]
|
||||
21
lms/app/migrations/0007_alter_module_created_by.py
Normal file
21
lms/app/migrations/0007_alter_module_created_by.py
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
# Generated by Django 5.0.10 on 2025-01-14 11:10
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('app', '0006_module_created_by'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='module',
|
||||
name='created_by',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Created By'),
|
||||
),
|
||||
]
|
||||
21
lms/app/migrations/0008_lesson_created_by.py
Normal file
21
lms/app/migrations/0008_lesson_created_by.py
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
# Generated by Django 5.0.10 on 2025-01-14 12:12
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('app', '0007_alter_module_created_by'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='lesson',
|
||||
name='created_by',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Created By'),
|
||||
),
|
||||
]
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
# Generated by Django 5.0.10 on 2025-01-14 13:02
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('app', '0008_lesson_created_by'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='lesson',
|
||||
name='description',
|
||||
field=models.TextField(null=True, verbose_name='Lesson Description'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='module',
|
||||
name='description',
|
||||
field=models.TextField(null=True, verbose_name='Module Description'),
|
||||
),
|
||||
]
|
||||
|
|
@ -13,7 +13,7 @@ class Course(models.Model):
|
|||
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")
|
||||
owner = 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")
|
||||
|
|
@ -33,9 +33,10 @@ class Course(models.Model):
|
|||
class Module(models.Model):
|
||||
id = models.UUIDField(primary_key=True, default=uuid4, editable=False)
|
||||
title = models.CharField(max_length=255, verbose_name="Module Title")
|
||||
description = models.TextField(verbose_name="Module Description")
|
||||
description = models.TextField(null=True, verbose_name="Module Description")
|
||||
course = models.ForeignKey(Course, on_delete=models.CASCADE, related_name='modules', verbose_name="Course")
|
||||
order = models.PositiveIntegerField(default=0, verbose_name="Order", unique=True)
|
||||
created_by = models.ForeignKey(User, on_delete=models.CASCADE, null=True, blank=True, verbose_name="Created By")
|
||||
|
||||
def str(self):
|
||||
return self.title
|
||||
|
|
@ -44,10 +45,12 @@ class Module(models.Model):
|
|||
class Lesson(models.Model):
|
||||
id = models.UUIDField(primary_key=True, default=uuid4, editable=False)
|
||||
title = models.CharField(max_length=255, verbose_name="Lesson Title")
|
||||
description = models.TextField(null=True, verbose_name="Lesson Description")
|
||||
content = models.TextField(verbose_name="Lesson Content")
|
||||
module = models.ForeignKey(Module, on_delete=models.CASCADE, related_name='lessons', verbose_name="Module")
|
||||
order = models.PositiveIntegerField(default=0, verbose_name="Order")
|
||||
file = models.FileField(upload_to='lesson_files/', null=True, blank=True, verbose_name="Attached File")
|
||||
created_by = models.ForeignKey(User, on_delete=models.CASCADE, null=True, blank=True, verbose_name="Created By")
|
||||
|
||||
def str(self):
|
||||
return self.title
|
||||
|
|
|
|||
|
|
@ -1,13 +1,32 @@
|
|||
from rest_framework.permissions import BasePermission
|
||||
from rest_framework.permissions import IsAuthenticated, BasePermission, SAFE_METHODS
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class IsOwnerOrReadOnly(BasePermission):
|
||||
|
||||
def has_object_permission(self, request, view, obj):
|
||||
|
||||
if request.method in SAFE_METHODS:
|
||||
return True
|
||||
|
||||
view_name = view.__class__.__name__
|
||||
match view_name:
|
||||
case "CourseViewSet":
|
||||
return obj.owner == request.user
|
||||
|
||||
case "ModuleViewSet":
|
||||
return obj.created_by == request.user
|
||||
|
||||
case "LessonViewSet":
|
||||
return obj.created_by == request.user
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class IsInstructor(BasePermission):
|
||||
"""
|
||||
Custom permission to allow access only to users with role 'instructor'.
|
||||
"""
|
||||
|
||||
def has_permission(self, request, view):
|
||||
# Ensure the user is authenticated and has a role of 'instructor'
|
||||
return request.user.is_authenticated and request.user.role == 'instructor'
|
||||
|
||||
class IsAdmin(BasePermission):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -11,36 +11,58 @@ from dj_rest_auth.registration.serializers import RegisterSerializer
|
|||
|
||||
|
||||
class CourseSerializer(serializers.ModelSerializer):
|
||||
instructor_name = serializers.CharField(source='instructor.username', read_only=True)
|
||||
owner_name = serializers.CharField(source='owner.username', read_only=True)
|
||||
class Meta:
|
||||
model = Course
|
||||
fields = ['id', 'title', 'description', 'is_paid', 'price', 'image', 'instructor_name', 'created_at', 'updated_at']
|
||||
fields = ['id', 'title', 'description', 'is_paid', 'price', 'image', 'owner_name', 'created_at', 'updated_at']
|
||||
read_only_fields = ['created_at', 'updated_at']
|
||||
|
||||
class ModuleSerializer(serializers.HyperlinkedModelSerializer):
|
||||
class Meta:
|
||||
model = Module
|
||||
fields = ['url', 'id', 'title', 'description', 'course', 'order']
|
||||
extra_kwargs = {
|
||||
'url': {'view_name': 'modules-detail', 'lookup_field': 'id'}
|
||||
}
|
||||
|
||||
|
||||
class LessonSerializer(serializers.HyperlinkedModelSerializer):
|
||||
class LessonSerializer(serializers.ModelSerializer):
|
||||
module = serializers.PrimaryKeyRelatedField(queryset=Module.objects.all())
|
||||
|
||||
class Meta:
|
||||
model = Lesson
|
||||
fields = ['url', 'id', 'title', 'content', 'module', 'order', 'file']
|
||||
extra_kwargs = {
|
||||
'url': {'view_name': 'lessons-detail', 'lookup_field': 'id'}
|
||||
}
|
||||
fields = ['id', 'title', 'description', 'content', 'module', 'order', 'file']
|
||||
|
||||
|
||||
class ModuleSerializer(serializers.ModelSerializer):
|
||||
lessons = serializers.SerializerMethodField()
|
||||
class Meta:
|
||||
model = Module
|
||||
fields = ['id', 'title', 'description', 'lessons', 'course', 'order']
|
||||
read_only_fields = ['course', 'lessons']
|
||||
|
||||
def get_lessons(self, obj):
|
||||
return obj.lessons.values('id', 'title', 'description')
|
||||
|
||||
|
||||
class EnrollmentSerializer(serializers.ModelSerializer):
|
||||
course_details = serializers.SerializerMethodField()
|
||||
class Meta:
|
||||
model = Enrollment
|
||||
fields = ['id', 'student', 'course', 'enrolled_at', 'completed']
|
||||
fields = ['id', 'course', 'course_details', 'enrolled_at', 'completed']
|
||||
read_only_fields = ['enrolled_at']
|
||||
|
||||
def get_course_details(self, obj):
|
||||
course = obj.course
|
||||
request = self.context.get('request')
|
||||
image_url = course.image.url if course.image else None
|
||||
|
||||
if image_url and request:
|
||||
image_url = request.build_absolute_uri(image_url)
|
||||
|
||||
return {
|
||||
"id": course.id,
|
||||
"title": course.title,
|
||||
"description": course.description,
|
||||
"image": image_url,
|
||||
"is_paid": course.is_paid,
|
||||
"price": course.price,
|
||||
"rating": course.rating,
|
||||
}
|
||||
|
||||
|
||||
|
||||
class QuizSerializer(serializers.ModelSerializer):
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ 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')
|
||||
|
|
|
|||
294
lms/app/views.py
294
lms/app/views.py
|
|
@ -5,69 +5,37 @@ 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()
|
||||
|
||||
from .permissions import IsOwnerOrReadOnly, IsAdmin
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.exceptions import PermissionDenied
|
||||
|
||||
|
||||
class CourseViewSet(ModelViewSet):
|
||||
"""
|
||||
A ViewSet for viewing and editing Course instances.
|
||||
"""
|
||||
permission_classes = [IsAuthenticated,]
|
||||
queryset = Course.objects.all()
|
||||
permission_classes = [IsOwnerOrReadOnly]
|
||||
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.
|
||||
"""
|
||||
user = self.request.user
|
||||
serializer.save(owner=self.request.user)
|
||||
|
||||
|
||||
serializer.save(instructor=user)
|
||||
|
||||
def perform_update(self, serializer):
|
||||
@action(detail=False, methods=['get'], url_path='my-courses', url_name='my_courses')
|
||||
def get_my_course(self, request):
|
||||
"""
|
||||
Ensure that only the instructor can update their course.
|
||||
Custom GET method to fetch detailed information about my courses.
|
||||
"""
|
||||
course = self.get_object()
|
||||
if course.instructor != self.request.user:
|
||||
return Response(
|
||||
{"detail": "You do not have permission to edit this course."},
|
||||
status=status.HTTP_403_FORBIDDEN,
|
||||
)
|
||||
serializer.save()
|
||||
|
||||
def perform_destroy(self, instance):
|
||||
"""
|
||||
Ensure that only the instructor can delete their course.
|
||||
"""
|
||||
if instance.instructor != self.request.user:
|
||||
return Response(
|
||||
{"detail": "You do not have permission to delete this course."},
|
||||
status=status.HTTP_403_FORBIDDEN,
|
||||
)
|
||||
instance.delete()
|
||||
|
||||
|
||||
|
||||
|
||||
my_courses = Course.objects.filter(owner=request.user)
|
||||
|
||||
# Serialize the data
|
||||
serializer = self.get_serializer(my_courses, many=True)
|
||||
|
||||
return Response(serializer.data)
|
||||
|
||||
|
||||
|
||||
|
|
@ -77,15 +45,13 @@ class ModuleViewSet(ModelViewSet):
|
|||
"""
|
||||
|
||||
serializer_class = ModuleSerializer
|
||||
permission_classes = [IsAuthenticated]
|
||||
lookup_field = 'id'
|
||||
|
||||
permission_classes = [IsAuthenticated, IsOwnerOrReadOnly]
|
||||
|
||||
def get_queryset(self):
|
||||
"""
|
||||
Return modules only if the user is the course instructor.
|
||||
Return modules only if the user is the course owner.
|
||||
"""
|
||||
course_id = self.request.query_params.get('course_id')
|
||||
course_id = self.request.query_params.get('pk')
|
||||
if course_id:
|
||||
course = Course.objects.filter(id=course_id).first()
|
||||
if course:
|
||||
|
|
@ -95,39 +61,18 @@ class ModuleViewSet(ModelViewSet):
|
|||
|
||||
def perform_create(self, serializer):
|
||||
"""
|
||||
Allow only the course instructor to create a module.
|
||||
Allow only the course owner to create a module.
|
||||
"""
|
||||
course_id = self.request.data.get('course')
|
||||
course = Course.objects.filter(id=course_id, instructor=self.request.user).first()
|
||||
course = Course.objects.filter(id=course_id, owner=self.request.user).first()
|
||||
if not course:
|
||||
return Response(
|
||||
{"detail": "You do not have permission to delete this course."},
|
||||
status=status.HTTP_403_FORBIDDEN,
|
||||
{"detail": "This course not found."},
|
||||
status=status.HTTP_404_NOT_FOUND,
|
||||
)
|
||||
serializer.save(course=course)
|
||||
serializer.save(course=course, created_by=self.request.user)
|
||||
|
||||
def perform_update(self, serializer):
|
||||
"""
|
||||
Allow only the course instructor to update the module.
|
||||
"""
|
||||
module = self.get_object()
|
||||
if module.course.instructor != self.request.user:
|
||||
return Response(
|
||||
{"detail": "You do not have permission to delete this course."},
|
||||
status=status.HTTP_403_FORBIDDEN,
|
||||
)
|
||||
serializer.save()
|
||||
|
||||
def perform_destroy(self, instance):
|
||||
"""
|
||||
Allow only the course instructor to delete the module.
|
||||
"""
|
||||
if instance.course.instructor != self.request.user:
|
||||
return Response(
|
||||
{"detail": "You do not have permission to delete this course."},
|
||||
status=status.HTTP_403_FORBIDDEN,
|
||||
)
|
||||
instance.delete()
|
||||
|
||||
class LessonViewSet(ModelViewSet):
|
||||
"""
|
||||
|
|
@ -135,143 +80,83 @@ class LessonViewSet(ModelViewSet):
|
|||
"""
|
||||
|
||||
serializer_class = LessonSerializer
|
||||
permission_classes = [IsAuthenticated]
|
||||
lookup_field = 'id'
|
||||
|
||||
permission_classes = [IsAuthenticated, IsOwnerOrReadOnly]
|
||||
|
||||
def get_queryset(self):
|
||||
"""
|
||||
Return lessons only if the user is authorized (instructor or student) and provides valid data.
|
||||
Return a specific lesson within a specific module only if the user is authorized.
|
||||
"""
|
||||
# حالة جلب كائن واحد
|
||||
if self.kwargs.get(self.lookup_field): # 'id' by default
|
||||
lesson = Lesson.objects.filter(id=self.kwargs[self.lookup_field]).first()
|
||||
if lesson:
|
||||
course = lesson.module.course
|
||||
# التحقق من الصلاحيات
|
||||
if course.instructor == self.request.user or Enrollment.objects.filter(student=self.request.user, course=course).exists():
|
||||
return Lesson.objects.filter(id=lesson.id)
|
||||
return Lesson.objects.none()
|
||||
lesson_id = self.request.query_params.get('lesson_id') # Get lesson ID from the request
|
||||
module_id = self.request.query_params.get('module_id') # Get module ID from the request
|
||||
|
||||
# حالة جلب مجموعة بناءً على module_id
|
||||
module_id = self.request.query_params.get('module_id')
|
||||
if module_id:
|
||||
module = Module.objects.filter(id=module_id).first()
|
||||
if module:
|
||||
course = module.course
|
||||
# التحقق من الصلاحيات
|
||||
if course.instructor == self.request.user or Enrollment.objects.filter(student=self.request.user, course=course).exists():
|
||||
queryset = Lesson.objects.filter(module=module).order_by('order')
|
||||
print(f"Queryset: {queryset}")
|
||||
return queryset
|
||||
# Check if both lesson_id and module_id are provided
|
||||
if not lesson_id or not module_id:
|
||||
return Lesson.objects.none() # Return no results if either is missing
|
||||
|
||||
# في حالة عدم وجود صلاحيات أو عدم تطابق البيانات
|
||||
return Lesson.objects.none()
|
||||
|
||||
|
||||
def perform_create(self, serializer):
|
||||
"""
|
||||
Allow only the course instructor to create a lesson within their module.
|
||||
"""
|
||||
module_id = self.request.data.get('module')
|
||||
module = Module.objects.filter(id=module_id, course__instructor=self.request.user).first()
|
||||
# Verify that the module exist
|
||||
module = Module.objects.filter(id=module_id).first()
|
||||
if not module:
|
||||
return Response(
|
||||
{"detail": "You do not have permission to create a lesson in this module."},
|
||||
status=status.HTTP_403_FORBIDDEN,
|
||||
)
|
||||
serializer.save(module=module)
|
||||
return Lesson.objects.none() # Return no results if the module does not exist
|
||||
|
||||
# Verify that the lesson exists and is associated with the module
|
||||
lesson = Lesson.objects.filter(id=lesson_id, module=module).first()
|
||||
if not lesson:
|
||||
return Lesson.objects.none() # Return no results if the lesson does not exist or is not linked to the module
|
||||
|
||||
# Check if the user has access (owner of the course or enrolled in the course)
|
||||
is_owner = module.course.owner == self.request.user
|
||||
is_enrolled = Enrollment.objects.filter(course=module.course, student=self.request.user).exists()
|
||||
|
||||
if is_owner or is_enrolled:
|
||||
return Lesson.objects.filter(id=lesson_id) # Return the lesson if the user is authorized
|
||||
|
||||
return Lesson.objects.none() # Deny access if the user is not authorized
|
||||
|
||||
|
||||
def perform_update(self, serializer):
|
||||
"""
|
||||
Allow only the course instructor to update a lesson within their module.
|
||||
"""
|
||||
lesson = self.get_object()
|
||||
if lesson.module.course.instructor != self.request.user:
|
||||
return Response(
|
||||
{"detail": "You do not have permission to update this lesson."},
|
||||
status=status.HTTP_403_FORBIDDEN,
|
||||
)
|
||||
serializer.save()
|
||||
|
||||
def perform_destroy(self, instance):
|
||||
"""
|
||||
Allow only the course instructor to delete a lesson within their module.
|
||||
"""
|
||||
|
||||
if instance.module.course.instructor != self.request.user:
|
||||
return Response(
|
||||
{"detail": "You do not have permission to delete this lesson."},
|
||||
status=status.HTTP_403_FORBIDDEN,
|
||||
)
|
||||
instance.delete()
|
||||
|
||||
class EnrollmentViewSet(ModelViewSet):
|
||||
queryset = Enrollment.objects.all()
|
||||
serializer_class = EnrollmentSerializer
|
||||
permission_classes = [IsAuthenticated]
|
||||
http_method_names = ['get', 'post', 'delete']
|
||||
|
||||
def retrieve(self, request, *args, **kwargs):
|
||||
instance = Enrollment.objects.filter(user=request.user)
|
||||
|
||||
serializer = self.get_serializer(instance)
|
||||
return Response(serializer.data)
|
||||
|
||||
|
||||
def create(self, request, *args, **kwargs):
|
||||
# Ensure the user is an instructor
|
||||
if request.user.role != 'instructor':
|
||||
return Response({"error": "Only instructors can enroll students"}, status=status.HTTP_403_FORBIDDEN)
|
||||
|
||||
# Get student and course data from the request
|
||||
student_id = request.data.get('student_id')
|
||||
course_id = request.data.get('course_id')
|
||||
|
||||
# Check if the student and course exist
|
||||
try:
|
||||
student = User.objects.get(id=student_id, role='student')
|
||||
course = Course.objects.get(id=course_id)
|
||||
except User.DoesNotExist:
|
||||
return Response({"error": "Student not found"}, status=status.HTTP_404_NOT_FOUND)
|
||||
except Course.DoesNotExist:
|
||||
return Response({"error": "Course not found"}, status=status.HTTP_404_NOT_FOUND)
|
||||
return Response({"detail": "Course not found"}, status=status.HTTP_404_NOT_FOUND)
|
||||
|
||||
# Ensure the current instructor is the course instructor
|
||||
if course.instructor != request.user:
|
||||
return Response({"error": "You can only enroll students in your own courses"}, status=status.HTTP_403_FORBIDDEN)
|
||||
if Enrollment.objects.filter(student=request.user, course=course).exists():
|
||||
return Response({"detail": "You are already subscribed to this course."}, status=status.HTTP_404_NOT_FOUND)
|
||||
elif course.owner == request.user:
|
||||
return Response({"detail": "You can't enroll in your course"}, status=status.HTTP_404_NOT_FOUND)
|
||||
|
||||
# Create a new enrollment
|
||||
enrollment = Enrollment.objects.create(student=student, course=course)
|
||||
enrollment = Enrollment.objects.create(student=request.user, course=course)
|
||||
serializer = self.get_serializer(enrollment)
|
||||
return Response(serializer.data, status=status.HTTP_201_CREATED)
|
||||
|
||||
def update(self, request, *args, **kwargs):
|
||||
# Ensure the user is an instructor
|
||||
if request.user.role != 'instructor':
|
||||
return Response({"error": "Only instructors can update enrollments"}, status=status.HTTP_403_FORBIDDEN)
|
||||
|
||||
# Get the enrollment object to update
|
||||
enrollment = self.get_object()
|
||||
|
||||
# Ensure the current instructor is the course instructor
|
||||
if enrollment.course.instructor != request.user:
|
||||
return Response({"error": "You can only update enrollments in your own courses"}, status=status.HTTP_403_FORBIDDEN)
|
||||
|
||||
# Update the enrollment
|
||||
serializer = self.get_serializer(enrollment, data=request.data, partial=True)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
serializer.save()
|
||||
return Response(serializer.data, status=status.HTTP_200_OK)
|
||||
|
||||
def destroy(self, request, *args, **kwargs):
|
||||
# Ensure the user is an instructor
|
||||
if request.user.role != 'instructor':
|
||||
return Response({"error": "Only instructors can delete enrollments"}, status=status.HTTP_403_FORBIDDEN)
|
||||
|
||||
# Get the enrollment object to delete
|
||||
enrollment = self.get_object()
|
||||
|
||||
# Ensure the current instructor is the course instructor
|
||||
if enrollment.course.instructor != request.user:
|
||||
return Response({"error": "You can only delete enrollments in your own courses"}, status=status.HTTP_403_FORBIDDEN)
|
||||
if enrollment.student != request.user:
|
||||
raise PermissionDenied("You do not have permission to delete this enrollment.")
|
||||
|
||||
# Delete the enrollment
|
||||
enrollment.delete()
|
||||
return Response({"message": "Enrollment deleted successfully"}, status=status.HTTP_204_NO_CONTENT)
|
||||
return Response({"detail": "Enrollment deleted successfully"}, status=status.HTTP_204_NO_CONTENT)
|
||||
|
||||
|
||||
class QuizViewSet(ModelViewSet):
|
||||
queryset = Quiz.objects.all()
|
||||
|
|
@ -279,19 +164,19 @@ class QuizViewSet(ModelViewSet):
|
|||
permission_classes = [IsAuthenticated]
|
||||
def create(self, request, *args, **kwargs):
|
||||
|
||||
# Ensure the user is an instructor
|
||||
if request.user.role != 'instructor':
|
||||
return Response({"error": "Only instructors can create quizzes"}, status=status.HTTP_403_FORBIDDEN)
|
||||
# Ensure the user is an owner
|
||||
if request.user.role != 'owner':
|
||||
return Response({"detail": "Only owners can create quizzes"}, status=status.HTTP_403_FORBIDDEN)
|
||||
# Get course data from the request
|
||||
moduleId = request.data.get('module')
|
||||
# Check if the course exists
|
||||
try:
|
||||
module = Module.objects.get(id=moduleId)
|
||||
except Course.DoesNotExist:
|
||||
return Response({"error": "Course not found"}, status=status.HTTP_404_NOT_FOUND)
|
||||
# Ensure the current instructor is the course instructor
|
||||
if module.course.instructor != request.user:
|
||||
return Response({"error": "You can only create quizzes for your own courses"}, status=status.HTTP_403_FORBIDDEN)
|
||||
return Response({"detail": "Course not found"}, status=status.HTTP_404_NOT_FOUND)
|
||||
# Ensure the current owner is the course owner
|
||||
if module.course.owner != request.user:
|
||||
return Response({"detail": "You can only create quizzes for your own courses"}, status=status.HTTP_403_FORBIDDEN)
|
||||
# Create a new quiz
|
||||
# data = request.data.copy() # نسخ البيانات لتجنب التعديل على الأصل
|
||||
# data.pop('module', None) # إزالة المفتاح module إذا كان موجودًا
|
||||
|
|
@ -301,14 +186,14 @@ class QuizViewSet(ModelViewSet):
|
|||
|
||||
|
||||
def update(self, request, *args, **kwargs):
|
||||
# Ensure the user is an instructor
|
||||
if request.user.role != 'instructor':
|
||||
return Response({"error": "Only instructors can update quizzes"}, status=status.HTTP_403_FORBIDDEN)
|
||||
# Ensure the user is an owner
|
||||
if request.user.role != 'owner':
|
||||
return Response({"detail": "Only owners can update quizzes"}, status=status.HTTP_403_FORBIDDEN)
|
||||
# Get the quiz object to update
|
||||
quiz = self.get_object()
|
||||
# Ensure the current instructor is the course instructor
|
||||
if quiz.module.course.instructor != request.user:
|
||||
return Response({"error": "You can only update quizzes for your own courses"}, status=status.HTTP_403_FORBIDDEN)
|
||||
# Ensure the current owner is the course owner
|
||||
if quiz.module.course.owner != request.user:
|
||||
return Response({"detail": "You can only update quizzes for your own courses"}, status=status.HTTP_403_FORBIDDEN)
|
||||
# Update the quiz
|
||||
serializer = self.get_serializer(quiz, data=request.data, partial=True)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
|
|
@ -316,17 +201,17 @@ class QuizViewSet(ModelViewSet):
|
|||
return Response(serializer.data, status=status.HTTP_200_OK)
|
||||
def destroy(self, request, *args, **kwargs):
|
||||
|
||||
# Ensure the user is an instructor
|
||||
if request.user.role != 'instructor':
|
||||
return Response({"error": "Only instructors can delete quizzes"}, status=status.HTTP_403_FORBIDDEN)
|
||||
# Ensure the user is an owner
|
||||
if request.user.role != 'owner':
|
||||
return Response({"detail": "Only owners can delete quizzes"}, status=status.HTTP_403_FORBIDDEN)
|
||||
# Get the quiz object to delete
|
||||
quiz = self.get_object()
|
||||
# Ensure the current instructor is the course instructor
|
||||
if quiz.module.course.instructor != request.user:
|
||||
return Response({"error": "You can only delete quizzes for your own courses"}, status=status.HTTP_403_FORBIDDEN)
|
||||
# Ensure the current owner is the course owner
|
||||
if quiz.module.course.owner != request.user:
|
||||
return Response({"detail": "You can only delete quizzes for your own courses"}, status=status.HTTP_403_FORBIDDEN)
|
||||
# Delete the quiz
|
||||
quiz.delete()
|
||||
return Response({"message": "Quiz deleted successfully"}, status=status.HTTP_204_NO_CONTENT)
|
||||
return Response({"detail": "Quiz deleted successfully"}, status=status.HTTP_204_NO_CONTENT)
|
||||
|
||||
|
||||
class CertificateViewSet(ModelViewSet):
|
||||
|
|
@ -336,7 +221,8 @@ class CertificateViewSet(ModelViewSet):
|
|||
|
||||
def get_permissions(self):
|
||||
if self.action == 'create':
|
||||
permission_classes = [IsInstructor]
|
||||
# permission_classes = [Isowner]
|
||||
pass
|
||||
elif self.action in ['update', 'destroy']:
|
||||
permission_classes = [IsAdmin]
|
||||
else:
|
||||
|
|
@ -354,12 +240,12 @@ class CertificateViewSet(ModelViewSet):
|
|||
course = Course.objects.get(id=courseId)
|
||||
student = User.objects.get(id=student_id, role='student')
|
||||
except User.DoesNotExist:
|
||||
return Response({"error": "Student not found"}, status=status.HTTP_404_NOT_FOUND)
|
||||
return Response({"detail": "Student not found"}, status=status.HTTP_404_NOT_FOUND)
|
||||
except Course.DoesNotExist:
|
||||
return Response({"error": "Course not found"}, status=status.HTTP_404_NOT_FOUND)
|
||||
# Ensure the current instructor is the course instructor
|
||||
if course.instructor != request.user:
|
||||
return Response({"error": "You can only create certificate for your own courses"}, status=status.HTTP_403_FORBIDDEN)
|
||||
return Response({"detail": "Course not found"}, status=status.HTTP_404_NOT_FOUND)
|
||||
# Ensure the current owner is the course owner
|
||||
if course.owner != request.user:
|
||||
return Response({"detail": "You can only create certificate for your own courses"}, status=status.HTTP_403_FORBIDDEN)
|
||||
|
||||
certificate = Certificate.objects.create(course=course, student=student)
|
||||
serializer = self.get_serializer(certificate)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue