From 011b25d0177b90bd8a19362492c7321e7d4fc798 Mon Sep 17 00:00:00 2001 From: mohsentaba Date: Sun, 31 May 2026 09:09:17 +0330 Subject: [PATCH] swagger problem resolved with adding ref_name to views --- apps/account/permissions.py | 70 ++++++++++++++--- apps/account/views/user.py | 42 +++++++--- apps/blog/views_admin.py | 9 ++- apps/certificate/views.py | 13 +++- apps/course/serializers/admin.py | 3 + apps/course/views/admin.py | 127 +++++++++++++++++++++++++------ apps/quiz/serializers/admin.py | 1 + apps/quiz/views/admin.py | 47 +++++++++--- apps/transaction/views.py | 14 +++- 9 files changed, 263 insertions(+), 63 deletions(-) diff --git a/apps/account/permissions.py b/apps/account/permissions.py index 65616b5..2b5555e 100644 --- a/apps/account/permissions.py +++ b/apps/account/permissions.py @@ -1,12 +1,58 @@ - - - - - -from rest_framework.permissions import BasePermission - - -class IsActiveUser(BasePermission): - - def has_permission(self, request, view): - return request.user and request.user.is_active \ No newline at end of file + + + + + +from rest_framework.permissions import BasePermission, SAFE_METHODS + + +class IsActiveUser(BasePermission): + + def has_permission(self, request, view): + return request.user and request.user.is_active + + +class IsSuperAdmin(BasePermission): + """ + Allows access only to super admins and staff admins. + """ + def has_permission(self, request, view): + return ( + request.user and + request.user.is_authenticated and + request.user.is_active and + (request.user.is_superuser or request.user.user_type in ['super_admin', 'admin']) + ) + + +class IsPanelUser(BasePermission): + """ + Allows access to all administrative panel roles: super admins, admins, and professors. + """ + def has_permission(self, request, view): + return ( + request.user and + request.user.is_authenticated and + request.user.is_active and + (request.user.is_superuser or request.user.user_type in ['super_admin', 'admin', 'professor']) + ) + + +class IsSuperAdminOrReadOnlyForProfessor(BasePermission): + """ + Allows full read-write access to super admins/admins, + but only read-only (GET, HEAD, OPTIONS) access to professors. + """ + def has_permission(self, request, view): + if not request.user or not request.user.is_authenticated or not request.user.is_active: + return False + + # Super admin / Admin can do everything + if request.user.is_superuser or request.user.user_type in ['super_admin', 'admin']: + return True + + # Professor has read-only access + if request.user.user_type == 'professor': + return request.method in SAFE_METHODS + + return False \ No newline at end of file diff --git a/apps/account/views/user.py b/apps/account/views/user.py index b39e47b..6bb739b 100644 --- a/apps/account/views/user.py +++ b/apps/account/views/user.py @@ -29,7 +29,7 @@ from utils.redis import RedisManager from utils.exceptions import AppAPIException from utils import send_email, is_valid_email from config.settings import base as settings -from apps.account.permissions import IsActiveUser +from apps.account.permissions import IsActiveUser, IsSuperAdminOrReadOnlyForProfessor from apps.account.doc import * logger = logging.getLogger(__name__) @@ -424,7 +424,7 @@ class UserLoginView(CreateAPIView): "fullname": user.fullname, "email": user.email, "token": token.key, - "user_type": user.user_type_based_on_groups, + "user_type": user.user_type, "avatar": request.build_absolute_uri(user.avatar.url) if user.avatar else None, }, status=status.HTTP_201_CREATED) @@ -583,18 +583,27 @@ class AdminLoginView(CreateAPIView): serializer.is_valid(raise_exception=True) data = serializer.data - email = request.data['email'] + email = request.data['email'].strip().lower() try: - user_obj = User.objects.get(email=email) + user_obj = User.objects.get(email__iexact=email) except User.DoesNotExist: raise ValidationError({"email": "No admin found with that information."}) - # --- THE CRUCIAL ADMIN CHECK --- - # Adjust 'is_staff' or 'is_superuser' based on how you define admins in Django - if not (user_obj.is_staff or user_obj.is_superuser): + # --- PANEL ACCESS CHECK --- + # Allow super_admin, admin (is_staff/is_superuser), professors + # Check both user_type field AND group membership (in case they differ) + ALLOWED_TYPES = ('professor', 'admin', 'super_admin') + is_panel_user = ( + user_obj.is_staff or + user_obj.is_superuser or + getattr(user_obj, 'user_type', None) in ALLOWED_TYPES or + user_obj.groups.filter(name="Professor Group").exists() + ) + if not is_panel_user: raise AuthenticationFailed("No admin found with that information.") - user = authenticate(request, username=email, password=data['password']) + # Use the actual stored email for authentication (ensures case matches DB) + user = authenticate(request, username=user_obj.email, password=data['password']) if not user: raise ValidationError({"password": "Password is incorrect"}) @@ -612,12 +621,18 @@ class AdminLoginView(CreateAPIView): device_os='web_admin' ) + # Determine effective user_type (user_type field may lag behind groups) + effective_user_type = user.user_type + if effective_user_type not in ('professor', 'admin', 'super_admin'): + if user.groups.filter(name="Professor Group").exists(): + effective_user_type = 'professor' + return Response({ "id": user.id, "fullname": user.fullname, "email": user.email, "token": token.key, - "user_type": user.user_type_based_on_groups, + "user_type": effective_user_type, "avatar": request.build_absolute_uri(user.avatar.url) if user.avatar else None, }, status=status.HTTP_201_CREATED) @@ -634,7 +649,7 @@ class AdminUserViewSet(ModelViewSet): Includes searching, filtering, and pagination. """ serializer_class = AdminUserSerializer - permission_classes = [IsAuthenticated, IsAdminUser] # Strictly admin only + permission_classes = [IsAuthenticated, IsSuperAdminOrReadOnlyForProfessor] authentication_classes = [TokenAuthentication] pagination_class = StandardResultsSetPagination filter_backends = [] # Disable default backend filtering to use custom manual filtering @@ -642,6 +657,13 @@ class AdminUserViewSet(ModelViewSet): def get_queryset(self): queryset = User.objects.filter(is_active=True, email__isnull=False).exclude(email='') + # Restrict queryset for professors to only students of their courses + if self.request.user.user_type == 'professor': + queryset = queryset.filter( + user_type='student', + participated_courses__course__professor_id=self.request.user.id + ) + # Handle Search search_query = self.request.query_params.get('search', None) if search_query: diff --git a/apps/blog/views_admin.py b/apps/blog/views_admin.py index 7a443a1..b757ea8 100644 --- a/apps/blog/views_admin.py +++ b/apps/blog/views_admin.py @@ -1,6 +1,7 @@ from django.db.models import Q from rest_framework.viewsets import ModelViewSet -from rest_framework.permissions import IsAuthenticated, IsAdminUser +from rest_framework.permissions import IsAuthenticated +from apps.account.permissions import IsSuperAdmin from rest_framework.authentication import TokenAuthentication from rest_framework.parsers import MultiPartParser, FormParser, JSONParser from rest_framework.decorators import action @@ -19,7 +20,7 @@ class AdminBlogViewSet(ModelViewSet): """ Admin CRUD ViewSet for Blog management. """ - permission_classes = [IsAuthenticated, IsAdminUser] + permission_classes = [IsAuthenticated, IsSuperAdmin] authentication_classes = [TokenAuthentication] pagination_class = StandardResultsSetPagination # Allow multipart so admins can upload thumbnail images directly @@ -63,7 +64,7 @@ class AdminBlogContentViewSet(ModelViewSet): Admin CRUD ViewSet for managing the content blocks inside a specific blog. """ serializer_class = AdminBlogContentSerializer - permission_classes = [IsAuthenticated, IsAdminUser] + permission_classes = [IsAuthenticated, IsSuperAdmin] authentication_classes = [TokenAuthentication] parser_classes = (MultiPartParser, FormParser, JSONParser) @@ -103,7 +104,7 @@ class AdminBlogSeoViewSet(ModelViewSet): Admin CRUD ViewSet for managing SEO meta tags for a specific blog. """ serializer_class = AdminBlogSeoSerializer - permission_classes = [IsAuthenticated, IsAdminUser] + permission_classes = [IsAuthenticated, IsSuperAdmin] authentication_classes = [TokenAuthentication] def get_queryset(self): diff --git a/apps/certificate/views.py b/apps/certificate/views.py index f8670bf..e062d23 100644 --- a/apps/certificate/views.py +++ b/apps/certificate/views.py @@ -56,19 +56,28 @@ class UserCertificatesListView(generics.ListAPIView): from rest_framework.viewsets import ModelViewSet -from rest_framework.permissions import IsAdminUser from apps.certificate.serializers import AdminCertificateSerializer +from apps.account.permissions import IsPanelUser from django.db.models import Q + +def is_professor(request): + return getattr(request.user, 'user_type', None) == 'professor' + + class AdminCertificateViewSet(ModelViewSet): queryset = Certificate.objects.all() serializer_class = AdminCertificateSerializer - permission_classes = [permissions.IsAuthenticated, IsAdminUser] + permission_classes = [permissions.IsAuthenticated, IsPanelUser] authentication_classes = [TokenAuthentication] pagination_class = StandardResultsSetPagination def get_queryset(self): queryset = Certificate.objects.all().select_related('student', 'course') + + # Professors can only see certificates for students in their courses + if is_professor(self.request): + queryset = queryset.filter(course__professor_id=self.request.user.id) # Search query search_query = self.request.query_params.get('search', None) diff --git a/apps/course/serializers/admin.py b/apps/course/serializers/admin.py index a7955e8..876a7ba 100644 --- a/apps/course/serializers/admin.py +++ b/apps/course/serializers/admin.py @@ -152,6 +152,9 @@ class AdminCourseLessonSerializer(serializers.Serializer): video_link = serializers.CharField(max_length=500, required=False, allow_blank=True, allow_null=True) duration = serializers.IntegerField(min_value=0, required=False, default=0) + class Meta: + ref_name = 'CourseAdminCourseLesson' + def to_representation(self, instance): """ Map a CourseLesson instance to representation fields. diff --git a/apps/course/views/admin.py b/apps/course/views/admin.py index c24c9a3..b589f80 100644 --- a/apps/course/views/admin.py +++ b/apps/course/views/admin.py @@ -1,10 +1,12 @@ from django.db.models import Q from rest_framework import viewsets -from rest_framework.permissions import IsAuthenticated, IsAdminUser +from rest_framework.permissions import IsAuthenticated from rest_framework.authentication import TokenAuthentication from rest_framework.decorators import action from rest_framework.response import Response +from rest_framework.exceptions import PermissionDenied from utils.pagination import StandardResultsSetPagination +from apps.account.permissions import IsPanelUser, IsSuperAdminOrReadOnlyForProfessor from apps.course.models import ( Course, CourseCategory, @@ -34,11 +36,17 @@ from apps.course.serializers.admin import ( AdminLiveSessionDetailSerializer ) + +def is_professor(request): + return getattr(request.user, 'user_type', None) == 'professor' + + class AdminCourseViewSet(viewsets.ModelViewSet): """ Admin CRUD ViewSet for Course management. + Professors can only access/modify their own courses. """ - permission_classes = [IsAuthenticated, IsAdminUser] + permission_classes = [IsAuthenticated, IsPanelUser] authentication_classes = [TokenAuthentication] pagination_class = StandardResultsSetPagination @@ -50,6 +58,10 @@ class AdminCourseViewSet(viewsets.ModelViewSet): def get_queryset(self): queryset = Course.objects.all().select_related('category', 'professor') + # Professors can only see their own courses + if is_professor(self.request): + queryset = queryset.filter(professor_id=self.request.user.id) + # Search Query search_query = self.request.query_params.get('search', None) if search_query: @@ -81,18 +93,41 @@ class AdminCourseViewSet(viewsets.ModelViewSet): return queryset.order_by('-id') + def perform_create(self, serializer): + # If the logged-in user is a professor, force the professor field to themselves + if is_professor(self.request): + serializer.save(professor_id=self.request.user.id) + else: + serializer.save() + + def perform_update(self, serializer): + # Prevent professors from reassigning a course to another professor + if is_professor(self.request): + instance = self.get_object() + if instance.professor_id != self.request.user.id: + raise PermissionDenied("You can only edit your own courses.") + serializer.save(professor_id=self.request.user.id) + else: + serializer.save() + class AdminChapterViewSet(viewsets.ModelViewSet): """ Admin CRUD ViewSet for Course Chapters. + Professors can only access chapters of their own courses. """ - permission_classes = [IsAuthenticated, IsAdminUser] + permission_classes = [IsAuthenticated, IsPanelUser] authentication_classes = [TokenAuthentication] serializer_class = AdminCourseChapterSerializer pagination_class = None def get_queryset(self): queryset = CourseChapter.objects.all() + + # Professors can only see chapters of their own courses + if is_professor(self.request): + queryset = queryset.filter(course__professor_id=self.request.user.id) + course_id = self.request.query_params.get('course', None) if course_id: queryset = queryset.filter(course_id=course_id) @@ -119,15 +154,20 @@ class AdminChapterViewSet(viewsets.ModelViewSet): class AdminCourseLessonViewSet(viewsets.ModelViewSet): """ Admin CRUD ViewSet for Lessons associated with a Course/Chapter. + Professors can only access lessons in their own courses. """ - permission_classes = [IsAuthenticated, IsAdminUser] + permission_classes = [IsAuthenticated, IsPanelUser] authentication_classes = [TokenAuthentication] serializer_class = AdminCourseLessonSerializer pagination_class = None def get_queryset(self): queryset = CourseLesson.objects.all().select_related('lesson', 'chapter') - + + # Professors can only see lessons of their own courses + if is_professor(self.request): + queryset = queryset.filter(course__professor_id=self.request.user.id) + course_id = self.request.query_params.get('course', None) if course_id: queryset = queryset.filter(course_id=course_id) @@ -166,14 +206,20 @@ class AdminCourseLessonViewSet(viewsets.ModelViewSet): class AdminCourseAttachmentViewSet(viewsets.ModelViewSet): """ Admin CRUD ViewSet for Course Attachments. + Professors can only access attachments of their own courses. """ - permission_classes = [IsAuthenticated, IsAdminUser] + permission_classes = [IsAuthenticated, IsPanelUser] authentication_classes = [TokenAuthentication] serializer_class = AdminCourseAttachmentSerializer pagination_class = None def get_queryset(self): queryset = CourseAttachment.objects.all().select_related('attachment') + + # Professors can only see attachments of their own courses + if is_professor(self.request): + queryset = queryset.filter(course__professor_id=self.request.user.id) + course_id = self.request.query_params.get('course', None) if course_id: queryset = queryset.filter(course_id=course_id) @@ -190,14 +236,20 @@ class AdminCourseAttachmentViewSet(viewsets.ModelViewSet): class AdminCourseGlossaryViewSet(viewsets.ModelViewSet): """ Admin CRUD ViewSet for Course Glossary terms. + Professors can only access glossaries of their own courses. """ - permission_classes = [IsAuthenticated, IsAdminUser] + permission_classes = [IsAuthenticated, IsPanelUser] authentication_classes = [TokenAuthentication] serializer_class = AdminCourseGlossarySerializer pagination_class = None def get_queryset(self): queryset = CourseGlossary.objects.all().select_related('glossary') + + # Professors can only see glossaries of their own courses + if is_professor(self.request): + queryset = queryset.filter(course__professor_id=self.request.user.id) + course_id = self.request.query_params.get('course', None) if course_id: queryset = queryset.filter(course_id=course_id) @@ -214,12 +266,13 @@ class AdminCourseGlossaryViewSet(viewsets.ModelViewSet): class AdminCourseCategoryViewSet(viewsets.ModelViewSet): """ Admin CRUD ViewSet for Course Categories. + Professors have read-only access. """ - permission_classes = [IsAuthenticated, IsAdminUser] + permission_classes = [IsAuthenticated, IsSuperAdminOrReadOnlyForProfessor] authentication_classes = [TokenAuthentication] serializer_class = AdminCourseCategorySerializer pagination_class = StandardResultsSetPagination - + def get_queryset(self): queryset = CourseCategory.objects.all().order_by('-id') search = self.request.query_params.get('search', None) @@ -230,37 +283,53 @@ class AdminCourseCategoryViewSet(viewsets.ModelViewSet): class AdminLessonViewSet(viewsets.ModelViewSet): """ - Admin CRUD ViewSet for base Lesson. + Admin CRUD ViewSet for base Lesson (Repository). + Professors can only see lessons linked to their own courses. """ - permission_classes = [IsAuthenticated, IsAdminUser] + permission_classes = [IsAuthenticated, IsPanelUser] authentication_classes = [TokenAuthentication] serializer_class = AdminLessonSerializer pagination_class = StandardResultsSetPagination - + def get_queryset(self): queryset = Lesson.objects.all().order_by('-id') + + # Professors can only see lessons associated with their own courses + if is_professor(self.request): + queryset = queryset.filter( + course_lessons__course__professor_id=self.request.user.id + ).distinct() + search = self.request.query_params.get('search', None) if search: queryset = queryset.filter(title__icontains=search) - + content_type = self.request.query_params.get('content_type', None) if content_type: queryset = queryset.filter(content_type=content_type) - + return queryset class AdminGlossaryViewSet(viewsets.ModelViewSet): """ - Admin CRUD ViewSet for base Glossary. + Admin CRUD ViewSet for base Glossary (Repository). + Professors can only see glossaries linked to their own courses. """ - permission_classes = [IsAuthenticated, IsAdminUser] + permission_classes = [IsAuthenticated, IsPanelUser] authentication_classes = [TokenAuthentication] serializer_class = AdminGlossarySerializer pagination_class = StandardResultsSetPagination - + def get_queryset(self): queryset = Glossary.objects.all().order_by('-id') + + # Professors can only see glossaries associated with their own courses + if is_professor(self.request): + queryset = queryset.filter( + course_glossaries__course__professor_id=self.request.user.id + ).distinct() + search = self.request.query_params.get('search', None) if search: queryset = queryset.filter(Q(title__icontains=search) | Q(description__icontains=search)) @@ -269,15 +338,23 @@ class AdminGlossaryViewSet(viewsets.ModelViewSet): class AdminAttachmentViewSet(viewsets.ModelViewSet): """ - Admin CRUD ViewSet for base Attachment. + Admin CRUD ViewSet for base Attachment (Repository). + Professors can only see attachments linked to their own courses. """ - permission_classes = [IsAuthenticated, IsAdminUser] + permission_classes = [IsAuthenticated, IsPanelUser] authentication_classes = [TokenAuthentication] serializer_class = AdminAttachmentSerializer pagination_class = StandardResultsSetPagination - + def get_queryset(self): queryset = Attachment.objects.all().order_by('-id') + + # Professors can only see attachments associated with their own courses + if is_professor(self.request): + queryset = queryset.filter( + course_attachments__course__professor_id=self.request.user.id + ).distinct() + search = self.request.query_params.get('search', None) if search: queryset = queryset.filter(title__icontains=search) @@ -287,8 +364,9 @@ class AdminAttachmentViewSet(viewsets.ModelViewSet): class AdminLiveSessionViewSet(viewsets.ModelViewSet): """ Admin CRUD ViewSet for Course Live Sessions. + Professors can only access live sessions of their own courses. """ - permission_classes = [IsAuthenticated, IsAdminUser] + permission_classes = [IsAuthenticated, IsPanelUser] authentication_classes = [TokenAuthentication] pagination_class = StandardResultsSetPagination @@ -299,7 +377,11 @@ class AdminLiveSessionViewSet(viewsets.ModelViewSet): def get_queryset(self): queryset = CourseLiveSession.objects.all().select_related('course') - + + # Professors can only see live sessions of their own courses + if is_professor(self.request): + queryset = queryset.filter(course__professor_id=self.request.user.id) + search = self.request.query_params.get('search', None) if search: queryset = queryset.filter( @@ -329,4 +411,3 @@ class AdminLiveSessionViewSet(viewsets.ModelViewSet): queryset = queryset.filter(ended_at__lte=ended_before) return queryset.order_by('-started_at') - diff --git a/apps/quiz/serializers/admin.py b/apps/quiz/serializers/admin.py index 1e4f645..86a8a1e 100644 --- a/apps/quiz/serializers/admin.py +++ b/apps/quiz/serializers/admin.py @@ -16,6 +16,7 @@ class AdminCourseLessonSerializer(serializers.ModelSerializer): class Meta: model = CourseLesson fields = ['id', 'title'] + ref_name = 'QuizAdminCourseLesson' def get_title(self, obj): if obj.title: diff --git a/apps/quiz/views/admin.py b/apps/quiz/views/admin.py index fa53d9d..0d77448 100644 --- a/apps/quiz/views/admin.py +++ b/apps/quiz/views/admin.py @@ -1,7 +1,7 @@ from django.db.models import Q from rest_framework.viewsets import ModelViewSet from rest_framework.generics import ListAPIView -from rest_framework.permissions import IsAuthenticated, IsAdminUser +from rest_framework.permissions import IsAuthenticated from rest_framework.authentication import TokenAuthentication from rest_framework.decorators import action from rest_framework.response import Response @@ -11,6 +11,7 @@ from utils.pagination import StandardResultsSetPagination from apps.quiz.models import Quiz, Question, QuizParticipant from apps.course.models import Course, CourseLesson, Participant from apps.account.models import User +from apps.account.permissions import IsPanelUser from apps.quiz.serializers.admin import ( AdminCourseSerializer, @@ -23,12 +24,16 @@ from apps.quiz.serializers.admin import ( ) +def is_professor(request): + return getattr(request.user, 'user_type', None) == 'professor' + + class AdminQuizViewSet(ModelViewSet): """ Admin CRUD ViewSet for Quiz management. - Includes searching, filtering, and retrieving participants/absentees list. + Professors can only access quizzes of their own courses. """ - permission_classes = [IsAuthenticated, IsAdminUser] + permission_classes = [IsAuthenticated, IsPanelUser] authentication_classes = [TokenAuthentication] pagination_class = StandardResultsSetPagination @@ -40,6 +45,10 @@ class AdminQuizViewSet(ModelViewSet): def get_queryset(self): queryset = Quiz.objects.all().select_related('course', 'lesson') + # Professors can only see quizzes of their own courses + if is_professor(self.request): + queryset = queryset.filter(course__professor_id=self.request.user.id) + # Handle Search search_query = self.request.query_params.get('search', None) if search_query: @@ -110,16 +119,20 @@ class AdminQuizViewSet(ModelViewSet): class AdminQuestionViewSet(ModelViewSet): """ Admin CRUD ViewSet for Question management. - Usually filtered by quiz_id. + Professors can only access questions of quizzes belonging to their own courses. """ serializer_class = AdminQuestionSerializer - permission_classes = [IsAuthenticated, IsAdminUser] + permission_classes = [IsAuthenticated, IsPanelUser] authentication_classes = [TokenAuthentication] pagination_class = None def get_queryset(self): queryset = Question.objects.all() + # Professors can only see questions of their own courses' quizzes + if is_professor(self.request): + queryset = queryset.filter(quiz__course__professor_id=self.request.user.id) + quiz_id = self.request.query_params.get('quiz_id', None) if not quiz_id: # Fallback parameter name @@ -162,25 +175,39 @@ class AdminQuestionViewSet(ModelViewSet): class AdminCourseListView(ListAPIView): """ Dropdown listing helper for all courses. + Professors only see their own courses. """ - queryset = Course.objects.all().order_by('-id') serializer_class = AdminCourseSerializer - permission_classes = [IsAuthenticated, IsAdminUser] + permission_classes = [IsAuthenticated, IsPanelUser] authentication_classes = [TokenAuthentication] pagination_class = None # Return all courses for dropdown usage + def get_queryset(self): + queryset = Course.objects.all().order_by('-id') + if is_professor(self.request): + queryset = queryset.filter(professor_id=self.request.user.id) + return queryset + class AdminLessonListView(ListAPIView): """ Dropdown listing helper for lessons. Filtered by course_id. + Professors only see lessons of their own courses. """ serializer_class = AdminCourseLessonSerializer - permission_classes = [IsAuthenticated, IsAdminUser] + permission_classes = [IsAuthenticated, IsPanelUser] authentication_classes = [TokenAuthentication] pagination_class = None # Return all matching lessons for dropdown usage def get_queryset(self): course_id = self.request.query_params.get('course_id', None) if course_id: - return CourseLesson.objects.filter(course_id=course_id).select_related('lesson').order_by('priority') - return CourseLesson.objects.all().select_related('lesson').order_by('-id') + qs = CourseLesson.objects.filter(course_id=course_id).select_related('lesson').order_by('priority') + else: + qs = CourseLesson.objects.all().select_related('lesson').order_by('-id') + + # Professors only see lessons of their own courses + if is_professor(self.request): + qs = qs.filter(course__professor_id=self.request.user.id) + + return qs diff --git a/apps/transaction/views.py b/apps/transaction/views.py index 168a08c..d270a68 100644 --- a/apps/transaction/views.py +++ b/apps/transaction/views.py @@ -417,19 +417,25 @@ class TransactionReceiptsListView(generics.ListAPIView): # ============================================================================= from rest_framework.viewsets import ModelViewSet -from rest_framework.permissions import IsAdminUser from apps.transaction.serializers import AdminTransactionSerializer +from apps.account.permissions import IsPanelUser from django.db.models import Q from utils.pagination import StandardResultsSetPagination + +def is_professor(request): + return getattr(request.user, 'user_type', None) == 'professor' + + class AdminTransactionViewSet(ModelViewSet): """ Admin-only viewset for viewing and updating transactions. Only GET (list/retrieve) and PATCH (update status) are allowed. + Professors only see transactions for their own courses. """ queryset = TransactionParticipant.objects.all() serializer_class = AdminTransactionSerializer - permission_classes = [IsAuthenticated, IsAdminUser] + permission_classes = [IsAuthenticated, IsPanelUser] authentication_classes = [TokenAuthentication] pagination_class = StandardResultsSetPagination http_method_names = ['get', 'patch', 'head', 'options'] @@ -437,6 +443,10 @@ class AdminTransactionViewSet(ModelViewSet): def get_queryset(self): queryset = TransactionParticipant.objects.all().select_related('user', 'course').prefetch_related('receipts', 'participant_infos') + # Professors can only see transactions for their own courses + if is_professor(self.request): + queryset = queryset.filter(course__professor_id=self.request.user.id) + # Search search_query = self.request.query_params.get('search', None) if search_query: