Skip to content
Snippets Groups Projects
Verified Commit bb4aee45 authored by Jonathan Weth's avatar Jonathan Weth :keyboard:
Browse files

Implement API for getting and updating print jobs with all connected changes

parent ac51a9df
No related branches found
No related tags found
1 merge request!3Setup printer stuff
Pipeline #61913 passed
from django.contrib import admin
from django.shortcuts import get_object_or_404
from django.utils import timezone
from oauth2_provider.contrib.rest_framework import TokenHasScope
from rest_framework import generics, permissions, serializers
from rest_framework.permissions import BasePermission
from rest_framework.response import Response
from rest_framework.views import APIView
from aleksis.apps.kort.models import CardPrinter
from aleksis.apps.kort.models import Card, CardPrinter, CardPrintJob
admin.autodiscover()
......@@ -20,6 +23,16 @@ class CorrectPrinterPermission(BasePermission):
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
......@@ -35,6 +48,7 @@ class CardPrinterSerializer(serializers.ModelSerializer):
"status_text",
"last_seen_at",
"cups_printer",
"generate_number_on_server",
)
......@@ -44,6 +58,26 @@ class CardPrinterStatusSerializer(serializers.ModelSerializer):
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 CardPrinterDetails(generics.RetrieveAPIView):
"""Show details about the card printer."""
......@@ -71,3 +105,32 @@ class CardPrinterUpdateStatus(generics.UpdateAPIView):
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()
......@@ -26,12 +26,12 @@ class CardForm(forms.ModelForm):
class CardPrinterForm(forms.ModelForm):
layout = Layout(
Fieldset(_("Generic attributes"), "name", "location", "description"),
Fieldset(_("Printer settings"), "cups_printer"),
Fieldset(_("Printer settings"), "cups_printer", "generate_number_on_server"),
)
class Meta:
model = CardPrinter
fields = ["name", "location", "description", "cups_printer"]
fields = ["name", "location", "description", "cups_printer", "generate_number_on_server"]
class PrinterSelectForm(forms.Form):
......
# Generated by Django 3.2.12 on 2022-03-26 16:59
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('kort', '0008_auto_20220319_2018'),
]
operations = [
migrations.AddField(
model_name='cardprinter',
name='generate_number_on_server',
field=models.BooleanField(default=True, verbose_name='Generate card number on server'),
),
]
# Generated by Django 3.2.12 on 2022-03-26 20:23
from django.db import migrations
import django.utils.timezone
import model_utils.fields
class Migration(migrations.Migration):
dependencies = [
('kort', '0009_cardprinter_generate_number_on_server'),
]
operations = [
migrations.AddField(
model_name='cardprintjob',
name='created',
field=model_utils.fields.AutoCreatedField(default=django.utils.timezone.now, editable=False, verbose_name='created'),
),
migrations.AddField(
model_name='cardprintjob',
name='modified',
field=model_utils.fields.AutoLastModifiedField(default=django.utils.timezone.now, editable=False, verbose_name='modified'),
),
]
import uuid
from datetime import timedelta
from typing import Any, Union
from typing import Any, Optional, Union
from django.conf import settings
from django.core.exceptions import ValidationError
......@@ -10,6 +11,7 @@ from django.utils import timezone
from django.utils.translation import gettext as _
from celery.result import AsyncResult
from model_utils.models import TimeStampedModel
from aleksis.core.mixins import ExtensibleModel
from aleksis.core.models import OAuthApplication, Person
......@@ -80,6 +82,9 @@ class CardPrinter(ExtensibleModel):
# Settings
cups_printer = models.CharField(max_length=255, verbose_name=_("CUPS printer"), blank=True)
generate_number_on_server = models.BooleanField(
default=True, verbose_name=_("Generate card number on server")
)
def save(self, *args, **kwargs):
if not self.oauth2_application:
......@@ -141,6 +146,14 @@ class CardPrinter(ExtensibleModel):
for printer in cls.objects.all():
printer.check_online_status()
def get_next_print_job(self) -> Optional["CardPrintJob"]:
jobs = self.jobs.order_by("created").filter(status=PrintStatus.REGISTERED)
if self.generate_number_on_server:
jobs = jobs.filter(card__pdf_file__isnull=False)
if jobs.exists():
return jobs.first()
return None
class Meta:
verbose_name = _("Card printer")
verbose_name_plural = _("Card printers")
......@@ -210,10 +223,16 @@ class Card(ExtensibleModel):
def print_card(self, printer: CardPrinter):
job = CardPrintJob(card=self, printer=printer)
job.save()
if not self.chip_number and printer.generate_number_on_server:
self.chip_number = str(self.generate_number())
self.save()
if self.chip_number:
self.generate_pdf()
return job
def generate_number(self) -> int:
return uuid.uuid1().int >> 32
def __str__(self):
if self.chip_number:
return f"{self.person} ({self.chip_number})"
......@@ -224,7 +243,7 @@ class Card(ExtensibleModel):
verbose_name_plural = _("Cards")
class CardPrintJob(ExtensibleModel):
class CardPrintJob(TimeStampedModel, ExtensibleModel):
printer = models.ForeignKey(
CardPrinter, on_delete=models.CASCADE, verbose_name=_("Printer"), related_name="jobs"
)
......
......@@ -83,6 +83,9 @@
<th>
{% trans "Card" %}
</th>
<th>
{% trans "Created at" %}
</th>
<th>
{% trans "Status" %}
</th>
......@@ -95,7 +98,10 @@
</a>
</td>
<td>
{{ job.status }}
{{ job.created }}
</td>
<td>
{{ job.status }} {% if job.status_text %}({{ job.status_text }}){% endif %}
</td>
</tr>
{% endfor %}
......
......@@ -35,4 +35,14 @@ urlpatterns = [
api.CardPrinterUpdateStatus.as_view(),
name="api_card_printer_status",
),
path(
"api/v1/printers/<int:pk>/jobs/next/",
api.GetNextPrintJob.as_view(),
name="api_get_next_print_job",
),
path(
"api/v1/jobs/<int:pk>/status/",
api.CardPrintJobUpdateStatusView.as_view(),
name="api_update_job_status",
),
]
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment