Refactoring: new multi app structure

This commit is contained in:
2023-08-15 03:13:07 +09:00
parent 30b3efa5fc
commit e45d1af857
94 changed files with 634 additions and 1548 deletions

View File

@@ -0,0 +1,39 @@
from django_filters.rest_framework import (
CharFilter,
FilterSet,
NumberFilter,
)
from django_filters import widgets
from apps.collector.models import Archive, Ticket
from .utils import DateTimeFilterMixin
class ArchiveFilter(DateTimeFilterMixin, FilterSet):
class Meta:
model = Archive
fields = {
'id': ['exact', 'in', 'lte', 'gte'],
'ticket': ['exact', 'in', 'lte', 'gte'],
'time_create': ['exact', 'lte', 'gte']
}
class TicketFilter(DateTimeFilterMixin, FilterSet):
number = NumberFilter(
field_name='number',
widget=widgets.CSVWidget(),
)
user = CharFilter(
field_name='user__username'
)
class Meta:
model = Ticket
fields = {
'id': ['exact', 'in', 'lte', 'gte'],
'number': ['exact', 'contains', 'in', 'lte', 'gte'],
'resolved': ['exact'],
'user': ['exact']
}

View File

@@ -0,0 +1,13 @@
from rest_framework import permissions
class IsGuestUpload(permissions.BasePermission):
"""
Special permission class for the ability to upload attachments
to an unauthorized user using a ticket token
"""
def has_permission(self, request, view):
if request.method in ('HEAD', 'OPTIONS', 'POST',):
return True
return request.user.is_authenticated

View File

@@ -0,0 +1,60 @@
from rest_framework import serializers
from drf_spectacular.utils import extend_schema_field
from drf_spectacular.openapi import OpenApiTypes
from apps.collector.models import Archive, Platform, Ticket
@extend_schema_field(OpenApiTypes.NUMBER)
class TimestampField(serializers.Field):
def to_representation(self, value) -> int:
return value.timestamp()
@extend_schema_field(OpenApiTypes.NUMBER)
class JsTimestampField(serializers.Field):
def to_representation(self, value) -> int:
return round(value.timestamp()*1000)
class PublicArchiveUploadSerializer(serializers.ModelSerializer):
class Meta:
model = Archive
fields = ['file', 'ticket']
class ArchiveSerializer(serializers.ModelSerializer):
time_create = JsTimestampField(read_only=True)
class Meta:
model = Archive
fields = ['id', 'file', 'ticket', 'time_create']
class PlatformSerializer(serializers.ModelSerializer):
class Meta:
model = Platform
fields = ['id', 'name', 'pretty_name']
class TicketSerializer(serializers.ModelSerializer):
time_create = JsTimestampField(read_only=True)
time_update = JsTimestampField(read_only=True)
token = serializers.UUIDField(read_only=True)
user = serializers.ReadOnlyField(source='user.username')
class Meta:
model = Ticket
fields = [
'id',
'number',
'resolved',
'token',
'attempts',
'platform',
'time_create',
'time_update',
'user'
]

View File

@@ -0,0 +1,21 @@
from django.urls import path, include
from rest_framework import routers
from . import views
# ▄▀█ █▀█ █
# █▀█ █▀▀ █
# -- -- --
app_name = 'collector_api'
router = routers.DefaultRouter()
router.register(r'archives', views.ArchiveViewSet)
router.register(r'platforms', views.PlatformViewSet)
router.register(r'tickets', views.TicketViewSet)
urlpatterns = [
# CRUD:
path('v1/', include(router.urls)),
]

View File

@@ -0,0 +1,20 @@
from django_filters import NumberFilter
class DateTimeFilterMixin:
year__gte = NumberFilter(
field_name='time_create',
lookup_expr='year__gte'
)
year__lte = NumberFilter(
field_name='time_create',
lookup_expr='year__lte'
)
month__gte = NumberFilter(
field_name='time_create',
lookup_expr='month__gte'
)
month__lte = NumberFilter(
field_name='time_create',
lookup_expr='month__lte'
)

View File

@@ -0,0 +1,94 @@
from django.core.exceptions import ValidationError, ObjectDoesNotExist
from rest_framework import status
# from rest_framework.decorators import action
from rest_framework.parsers import FormParser, MultiPartParser
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework import viewsets
from rest_framework import filters
from django_filters.rest_framework import DjangoFilterBackend
from apps.collector.models import Archive, Ticket, Platform # ???????
from .filters import ArchiveFilter, TicketFilter
from .permissions import IsGuestUpload
from .serializers import (
PublicArchiveUploadSerializer,
ArchiveSerializer,
PlatformSerializer,
TicketSerializer
)
class ArchiveViewSet(viewsets.ModelViewSet):
queryset = Archive.objects.order_by('-time_create')
serializer_class = ArchiveSerializer
parser_classes = (MultiPartParser, FormParser)
permission_classes = (IsGuestUpload, )
filter_backends = [DjangoFilterBackend]
filterset_class = ArchiveFilter
def create(self, request, *args, **kwargs):
# ! upload-token protection:
upload_token = request.headers.get('upload-token', '')
if not request.user.is_authenticated and upload_token:
try:
bound_ticket = Ticket.objects.get(token=upload_token)
if bound_ticket.resolved:
return Response(
{'error': f'ticket {upload_token} already resolved'},
status=status.HTTP_423_LOCKED
)
if bound_ticket.attempts <= 0:
return Response(
{'error': f'token {upload_token} expired'},
status=status.HTTP_423_LOCKED
)
bound_ticket.attempts -= 1
bound_ticket.save()
# ? mixin bound ticket number to request.data from user
request.data['ticket'] = bound_ticket.number
# ? change serializer for guest user
self.serializer_class = PublicArchiveUploadSerializer
except (ValidationError, ObjectDoesNotExist,):
return Response(
{'error': f'token {upload_token} is not valid'},
status=status.HTTP_403_FORBIDDEN
)
elif not request.user.is_authenticated:
return Response(
{'error': 'Header Upload-Token is required'},
status=status.HTTP_401_UNAUTHORIZED
)
# ! default create method:
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
return Response(
serializer.data,
status=status.HTTP_201_CREATED,
headers=headers
)
class PlatformViewSet(viewsets.ModelViewSet):
queryset = Platform.objects.all()
lookup_field = 'name'
serializer_class = PlatformSerializer
permission_classes = (IsAuthenticated, )
class TicketViewSet(viewsets.ModelViewSet):
queryset = Ticket.objects.order_by('-time_create')
lookup_field = 'number'
serializer_class = TicketSerializer
permission_classes = (IsAuthenticated, )
filter_backends = [DjangoFilterBackend, filters.SearchFilter]
filterset_class = TicketFilter
search_fields = ['number']
def perform_create(self, serializer):
serializer.save(user=self.request.user)