Compare commits
2 Commits
ed84b97f58
...
2a11a30cd5
| Author | SHA1 | Date | |
|---|---|---|---|
| 2a11a30cd5 | |||
| 9a376b0961 |
@ -1,9 +1,9 @@
|
|||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from .models import Publication, Rating
|
from .models import Publication, Rating, Category
|
||||||
|
|
||||||
# Register your models here.
|
# Register your models here.
|
||||||
class PublicationAdmin(admin.ModelAdmin):
|
class PublicationAdmin(admin.ModelAdmin):
|
||||||
list_display = ('pk', 'content_type', 'is_pinned', 'user', 'time_created', 'time_updated')
|
list_display = ('pk', 'content_type', 'category', 'is_pinned', 'user', 'time_created', 'time_updated')
|
||||||
list_display_links = ('pk', 'content_type')
|
list_display_links = ('pk', 'content_type')
|
||||||
list_filter = ('content_type', 'is_pinned')
|
list_filter = ('content_type', 'is_pinned')
|
||||||
readonly_fields = ('time_created', 'time_updated')
|
readonly_fields = ('time_created', 'time_updated')
|
||||||
@ -17,3 +17,4 @@ class RatingAdmin(admin.ModelAdmin):
|
|||||||
|
|
||||||
admin.site.register(Publication, PublicationAdmin)
|
admin.site.register(Publication, PublicationAdmin)
|
||||||
admin.site.register(Rating, RatingAdmin)
|
admin.site.register(Rating, RatingAdmin)
|
||||||
|
admin.site.register(Category)
|
||||||
@ -1,5 +1,6 @@
|
|||||||
from typing import TypeVar
|
from typing import TypeVar
|
||||||
from django.db.models import Avg
|
from django.db.models import Avg
|
||||||
|
from django.db.models.functions import Coalesce
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
|
||||||
T = TypeVar('T')
|
T = TypeVar('T')
|
||||||
@ -7,7 +8,7 @@ T = TypeVar('T')
|
|||||||
class PublicationQuerySet(models.QuerySet[T]):
|
class PublicationQuerySet(models.QuerySet[T]):
|
||||||
def with_score(self):
|
def with_score(self):
|
||||||
qs = self
|
qs = self
|
||||||
qs = qs.annotate(average_score=Avg("ratings__score"))
|
qs = qs.annotate(average_score=Coalesce(Avg("ratings__score"), 0.0))
|
||||||
return qs
|
return qs
|
||||||
|
|
||||||
def only_publish(self):
|
def only_publish(self):
|
||||||
|
|||||||
@ -48,10 +48,6 @@ class Publication(models.Model):
|
|||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f'Content #{self.pk}'
|
return f'Content #{self.pk}'
|
||||||
|
|
||||||
@property
|
|
||||||
def average_score(self) -> int:
|
|
||||||
return getattr(self, "average_score", None)
|
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
errors = []
|
errors = []
|
||||||
|
|
||||||
|
|||||||
@ -12,7 +12,31 @@ class CategorySerializer(serializers.ModelSerializer):
|
|||||||
class PublicationSerializer(serializers.ModelSerializer):
|
class PublicationSerializer(serializers.ModelSerializer):
|
||||||
category_detail = CategorySerializer(source="category", read_only=True)
|
category_detail = CategorySerializer(source="category", read_only=True)
|
||||||
user_detail = PublicUserSerializer(source="user", read_only=True)
|
user_detail = PublicUserSerializer(source="user", read_only=True)
|
||||||
average_score = serializers.IntegerField(read_only=True)
|
average_score = serializers.SerializerMethodField(read_only=True)
|
||||||
|
|
||||||
|
def get_average_score(self, obj):
|
||||||
|
return getattr(obj, 'average_score', 0)
|
||||||
|
|
||||||
|
def validate(self, attrs):
|
||||||
|
content_type = attrs.get('content_type')
|
||||||
|
image = attrs.get('image')
|
||||||
|
video = attrs.get('video')
|
||||||
|
|
||||||
|
errors = {}
|
||||||
|
|
||||||
|
if content_type == 'image' and not image:
|
||||||
|
errors['image'] = 'Required image file'
|
||||||
|
|
||||||
|
if content_type == 'video' and not video:
|
||||||
|
errors['video'] = 'Required video file'
|
||||||
|
|
||||||
|
if image and video:
|
||||||
|
errors['non_field_errors'] = 'You must upload either a video or an image, not both'
|
||||||
|
|
||||||
|
if errors:
|
||||||
|
raise serializers.ValidationError(errors)
|
||||||
|
|
||||||
|
return attrs
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Publication
|
model = Publication
|
||||||
|
|||||||
@ -40,6 +40,9 @@ class PublicationsAPIView(ListCreateAPIView):
|
|||||||
|
|
||||||
return super().get_permissions()
|
return super().get_permissions()
|
||||||
|
|
||||||
|
def perform_create(self, serializer: PublicationSerializer):
|
||||||
|
serializer.save(user=self.request.user)
|
||||||
|
|
||||||
@extend_schema(
|
@extend_schema(
|
||||||
tags=['Publications'],
|
tags=['Publications'],
|
||||||
summary='Publication details',
|
summary='Publication details',
|
||||||
@ -67,6 +70,9 @@ class AdminPublicationsAPIView(PublicationsAPIView):
|
|||||||
def get_permissions(self):
|
def get_permissions(self):
|
||||||
return [IsProfessorOnly()]
|
return [IsProfessorOnly()]
|
||||||
|
|
||||||
|
def perform_create(self, serializer: AdminPublicationSerializer):
|
||||||
|
serializer.save(user=self.request.user)
|
||||||
|
|
||||||
@extend_schema(
|
@extend_schema(
|
||||||
tags=['Publications'],
|
tags=['Publications'],
|
||||||
summary='CRUD for publication (only admin or professor)',
|
summary='CRUD for publication (only admin or professor)',
|
||||||
@ -92,10 +98,11 @@ class CategoryListAPIView(ListCreateAPIView):
|
|||||||
filter_backends = [filters.SearchFilter, filters.OrderingFilter]
|
filter_backends = [filters.SearchFilter, filters.OrderingFilter]
|
||||||
ordering = ['name']
|
ordering = ['name']
|
||||||
search_fields = ['name']
|
search_fields = ['name']
|
||||||
|
pagination_class = None
|
||||||
|
|
||||||
def get_permissions(self):
|
def get_permissions(self):
|
||||||
if self.request.method == 'POST':
|
if self.request.method == 'POST':
|
||||||
return [IsAdminUser]
|
return [IsAdminUser()]
|
||||||
return super().get_permissions()
|
return super().get_permissions()
|
||||||
|
|
||||||
@extend_schema(
|
@extend_schema(
|
||||||
|
|||||||
@ -12,11 +12,14 @@ django-rest-framework==0.1.0
|
|||||||
djangorestframework==3.16.1
|
djangorestframework==3.16.1
|
||||||
djangorestframework_simplejwt==5.5.1
|
djangorestframework_simplejwt==5.5.1
|
||||||
drf-spectacular==0.29.0
|
drf-spectacular==0.29.0
|
||||||
|
gunicorn==23.0.0
|
||||||
idna==3.11
|
idna==3.11
|
||||||
inflection==0.5.1
|
inflection==0.5.1
|
||||||
jsonschema==4.25.1
|
jsonschema==4.25.1
|
||||||
jsonschema-specifications==2025.9.1
|
jsonschema-specifications==2025.9.1
|
||||||
oauthlib==3.3.1
|
oauthlib==3.3.1
|
||||||
|
packaging==25.0
|
||||||
|
pillow==12.0.0
|
||||||
pycparser==2.23
|
pycparser==2.23
|
||||||
PyJWT==2.10.1
|
PyJWT==2.10.1
|
||||||
python-dotenv==1.2.1
|
python-dotenv==1.2.1
|
||||||
|
|||||||
@ -5,7 +5,7 @@ from rest_framework.request import Request
|
|||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework import status
|
from rest_framework import status
|
||||||
from rest_framework.views import APIView
|
from rest_framework.views import APIView
|
||||||
from rest_framework.generics import RetrieveUpdateDestroyAPIView, ListAPIView, RetrieveAPIView
|
from rest_framework.generics import RetrieveUpdateDestroyAPIView, ListAPIView, RetrieveAPIView, ListCreateAPIView
|
||||||
from rest_framework.permissions import IsAuthenticated, IsAdminUser
|
from rest_framework.permissions import IsAuthenticated, IsAdminUser
|
||||||
from rest_framework.authtoken.models import Token
|
from rest_framework.authtoken.models import Token
|
||||||
|
|
||||||
@ -87,9 +87,17 @@ class UserAPIView(RetrieveUpdateDestroyAPIView):
|
|||||||
serializer_class = UserForAdminSerializer
|
serializer_class = UserForAdminSerializer
|
||||||
permission_classes = [IsAdminUser]
|
permission_classes = [IsAdminUser]
|
||||||
|
|
||||||
@extend_schema(tags=['Users'],
|
@extend_schema(
|
||||||
summary='List of all available school ids (only admin)')
|
tags=['Users'],
|
||||||
class SchoolListAPIView(ListAPIView):
|
methods=['GET'],
|
||||||
|
summary='List school ids',
|
||||||
|
)
|
||||||
|
@extend_schema(
|
||||||
|
tags=['Users'],
|
||||||
|
methods=['POST'],
|
||||||
|
summary='Create new school id (only admin)',
|
||||||
|
)
|
||||||
|
class SchoolListAPIView(ListCreateAPIView):
|
||||||
queryset = SchoolID.objects.all()
|
queryset = SchoolID.objects.all()
|
||||||
serializer_class = SchoolIDSerializer
|
serializer_class = SchoolIDSerializer
|
||||||
permission_classes = [IsAdminUser]
|
permission_classes = [IsAdminUser]
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user