from django.contrib import admin from django.shortcuts import get_object_or_404 from django.utils import timezone from celery.result import allow_join_result from celery.states import SUCCESS from oauth2_provider.contrib.rest_framework import TokenHasScope from rest_framework import generics, permissions, serializers from rest_framework.exceptions import APIException, ValidationError from rest_framework.permissions import BasePermission from rest_framework.response import Response from rest_framework.views import APIView from aleksis.apps.kort.models import Card, CardPrinter, CardPrintJob admin.autodiscover() class CorrectPrinterPermission(BasePermission): """Check whether the OAuth2 application belongs to the printer.""" def has_object_permission(self, request, view, obj) -> bool: token = request.auth if token.application == obj.oauth2_application: return True return False class CorrectJobPrinterPermission(BasePermission): """Check whether the OAuth2 application belongs to the job's printer.""" def has_object_permission(self, request, view, obj) -> bool: token = request.auth if token.application == obj.printer.oauth2_application: return True return False class CardPrinterSerializer(serializers.ModelSerializer): class Meta: model = CardPrinter fields = ( "id", "name", "description", "location", "status", "status_label", "status_color", "status_icon", "status_text", "last_seen_at", "cups_printer", "generate_number_on_server", "card_detector", ) class CardPrinterStatusSerializer(serializers.ModelSerializer): class Meta: model = CardPrinter fields = ("status", "status_text") class CardSerializer(serializers.ModelSerializer): class Meta: model = Card fields = ("id", "chip_number", "valid_until", "deactivated", "person", "pdf_file") class CardPrintJobSerializer(serializers.ModelSerializer): card = CardSerializer() class Meta: model = CardPrintJob fields = ("id", "printer", "card", "status", "status_text") class CardPrintJobStatusSerializer(serializers.ModelSerializer): class Meta: model = CardPrintJob fields = ("id", "status", "status_text") class CardChipNumberSerializer(serializers.ModelSerializer): class Meta: model = Card fields = ("chip_number",) class CardPrinterDetails(generics.RetrieveAPIView): """Show details about the card printer.""" permission_classes = [permissions.IsAuthenticated, TokenHasScope, CorrectPrinterPermission] required_scopes = ["card_printer"] serializer_class = CardPrinterSerializer queryset = CardPrinter.objects.all() def get_object(self): token = self.request.auth return token.application.card_printers.all().first() class CardPrinterUpdateStatus(generics.UpdateAPIView): """Update the status of the card printer.""" permission_classes = [permissions.IsAuthenticated, TokenHasScope, CorrectPrinterPermission] required_scopes = ["card_printer"] serializer_class = CardPrinterStatusSerializer queryset = CardPrinter.objects.all() def update(self, request, *args, **kwargs): r = super().update(request, *args, **kwargs) instance = self.get_object() instance.last_seen_at = timezone.now() instance.save() return r class GetNextPrintJob(APIView): """Get the next print job.""" permission_classes = [permissions.IsAuthenticated, TokenHasScope, CorrectPrinterPermission] required_scopes = ["card_printer"] serializer_class = CardPrinterSerializer queryset = CardPrinter.objects.all() def get_object(self, pk): return get_object_or_404(CardPrinter, pk=pk) def get(self, request, pk, *args, **kwargs): printer = self.get_object(pk) job = printer.get_next_print_job() if not job: return Response({"status": "no_job"}) serializer = CardPrintJobSerializer(job) return Response(serializer.data) class CardPrintJobUpdateStatusView(generics.UpdateAPIView): """Update the status of the card printer.""" permission_classes = [permissions.IsAuthenticated, TokenHasScope, CorrectJobPrinterPermission] required_scopes = ["card_printer"] serializer_class = CardPrintJobStatusSerializer queryset = CardPrintJob.objects.all() class CardPrintJobSetChipNumberView(generics.UpdateAPIView): """Update the status of the card printer.""" permission_classes = [permissions.IsAuthenticated, TokenHasScope, CorrectJobPrinterPermission] required_scopes = ["card_printer"] serializer_class = CardChipNumberSerializer queryset = CardPrintJob.objects.all() def update(self, request, *args, **kwargs): instance = self.get_object() card = instance.card if card.chip_number: raise ValidationError serializer = self.get_serializer(card, data=request.data) serializer.is_valid(raise_exception=True) self.perform_update(serializer) result = instance.card.generate_pdf() with allow_join_result(): result.wait() card.refresh_from_db() if result.status == SUCCESS and card.pdf_file: serializer = CardPrintJobSerializer(instance) instance.refresh_from_db() return Response(serializer.data) else: card.chip_number = None card.save() raise APIException("Error while generating PDF file")