diff --git a/course/migrations/0018_alter_module_video.py b/course/migrations/0018_alter_module_video.py new file mode 100644 index 0000000..7f7bb8d --- /dev/null +++ b/course/migrations/0018_alter_module_video.py @@ -0,0 +1,18 @@ +# Generated by Django 5.1 on 2024-11-09 11:08 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('course', '0017_alter_note_module_alter_note_user'), + ] + + operations = [ + migrations.AlterField( + model_name='module', + name='video', + field=models.URLField(blank=True, max_length=500, null=True), + ), + ] diff --git a/course/migrations/0019_alter_module_video.py b/course/migrations/0019_alter_module_video.py new file mode 100644 index 0000000..6c6f5ac --- /dev/null +++ b/course/migrations/0019_alter_module_video.py @@ -0,0 +1,18 @@ +# Generated by Django 5.1 on 2024-11-09 11:11 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('course', '0018_alter_module_video'), + ] + + operations = [ + migrations.AlterField( + model_name='module', + name='video', + field=models.CharField(blank=True, max_length=500, null=True), + ), + ] diff --git a/course/serializers.py b/course/serializers.py index 0f2930d..4fed1b6 100644 --- a/course/serializers.py +++ b/course/serializers.py @@ -53,34 +53,8 @@ class ModuleSerializer(ModelSerializer): class Meta: model = Module - fields = "__all__" - - def validate_video(self, value): - """ - Validates the uploaded video file to ensure it is not - larger than 50 MB and has a valid format. - """ - if value.size > 50 * 1024 * 1024: - raise serializers.ValidationError('Video file size should not exceed 50 MB') - - if not value.name.endswith(('mp4', 'mov', 'avi')): - raise serializers.ValidationError('Invalid video type') - - return value - - - def validate_notes(self, value): - """ - Validates the uploaded notes file to ensure it is not - larger than 10 MB and has a valid format. - """ - if value.size > 10 * 1024 * 1024: - raise serializers.ValidationError('Notes file size should not be exceed 10MB') - - if not value.name.endswith(('pdf', 'docx')): - raise serializers.ValidationError('Invalid file type for notes') - return value - + fields = "__all__" + read_only_fields = ['video', 'notes'] def get_student_notes(self, obj): """ Retrieves the notes associated with the student for the module. @@ -112,17 +86,14 @@ def get_is_liked(self, obj): def update(self, instance, validated_data): """ - Updates the module instance with validated data. - Deletes old video and notes files if new ones are provided. + Handle the update of all fields except for video and notes. """ - new_video = validated_data.get('video', None) - if new_video and instance.video: - instance.video.delete(save=False) - - new_notes = validated_data.get('notes', None) - if new_notes and instance.notes: - instance.notes.delete(save=False) + # Exclude video and notes, handled in the view + print('validate -====',validated_data) + validated_data.pop('video', None) + validated_data.pop('notes', None) + # Update remaining fields for key, value in validated_data.items(): setattr(instance, key, value) diff --git a/course/urls.py b/course/urls.py index 4fe34fe..57def50 100644 --- a/course/urls.py +++ b/course/urls.py @@ -9,6 +9,7 @@ PaymentSuccess, ReviewViewSet, NotesViewSet, + get_presigned_url ) # Create a router for handling viewsets @@ -27,4 +28,6 @@ path('modules//mark-watched/', EditModuleView.as_view(), name='mark-watched'), path('stripe/course-purchase/', CoursePurchaseView.as_view(), name='stripe-payment'), path('payment_success/', PaymentSuccess.as_view(), name='payment-success'), + path('get-presigned-url/', get_presigned_url, name='get-presigned-url'), + ] diff --git a/course/views.py b/course/views.py index 6e3a0e7..9919f70 100644 --- a/course/views.py +++ b/course/views.py @@ -20,11 +20,48 @@ from django.db.models import Q from django.core.cache import cache from rest_framework.exceptions import NotFound +import boto3 +from rest_framework.decorators import api_view # Set the Stripe API key stripe.api_key = settings.STRIPE_SECRET_KEY + + + +@api_view(['GET']) +def get_presigned_url(request): + + s3 = boto3.client('s3', + aws_access_key_id=settings.AWS_ACCESS_KEY_ID, + aws_secret_access_key=settings.AWS_SECRET_ACCESS_KEY) + + file_type = request.GET.get('file_type') + filename = request.GET.get('filename') + + + if file_type == 'video': + key = f'module_videos/{filename}' + elif file_type == 'notes': + key = f'module_notes/{filename}' + else: + return Response({'error': 'Invalid file type'}, status=status.HTTP_400_BAD_REQUEST) + + + presigned_url = s3.generate_presigned_url( + ClientMethod='put_object', + Params={ + 'Bucket': "react-direct-bucket", + 'Key': key, + }, + ExpiresIn=3600 + ) + + return Response({'presignedUrl': presigned_url, 'key': key}) + + + class CategoryViewSet(ModelViewSet): """ API view for handling Category operations. @@ -238,8 +275,8 @@ def post(self, request, *args, **kwargs): title = data['title'] description = data['description'] duration = data['duration'] - video = request.FILES.get(f'video_{i}') - notes = request.FILES.get(f'notes_{i}') + video = data.get('video') + notes = data.get('notes') Module.objects.create( course=course, @@ -266,12 +303,54 @@ def patch(self, request, *args, **kwargs): """ Handle patch requests for toggling likes or marking a module as watched. """ - if 'toggle-like' in request.path: - return self.toggle_like(request, *args, **kwargs) - if 'mark-watched' in request.path: - return self.mark_watched(request, *args, **kwargs) + try: + if 'toggle-like' in request.path: + return self.toggle_like(request, *args, **kwargs) + if 'mark-watched' in request.path: + return self.mark_watched(request, *args, **kwargs) + except Exception as e: + return Response(str(e), status=status.HTTP_400_BAD_REQUEST) return super().patch(request, *args, **kwargs) + def perform_update(self, serializer): + """ + Handle the manual update of the 'video' and 'notes' paths. + """ + instance = self.get_object() + + # Handle video and notes path updates manually + video_url = self.request.data.get('video', '') + notes_url = self.request.data.get('notes', '') + + # Manually update video and notes + if video_url: + instance.video = video_url + if notes_url: + instance.notes = notes_url + + + # Now let the serializer handle the rest of the fields + validated_data = serializer.validated_data + validated_data.pop('video', None) + validated_data.pop('notes', None) + + # Update remaining fields with serializer and save + for key, value in validated_data.items(): + setattr(instance, key, value) + + # Save the instance + instance.save() + + # manually update the video and notes fields + if video_url: + instance.video = video_url + if notes_url: + instance.notes = notes_url + + instance.save() # Save again to persist video and notes + + + def toggle_like(self, request, pk=None): """ Toggle the like status of a module for the authenticated user. diff --git a/users/api/user_serializers.py b/users/api/user_serializers.py index 3e04cd6..9117736 100644 --- a/users/api/user_serializers.py +++ b/users/api/user_serializers.py @@ -125,7 +125,7 @@ def validate(self, attrs): }, code='authorization') # Check if verified - if not user.is_verified: + if not user.is_verified and user.role != 'admin': otp = generate_otp() user.otp = otp user.save()