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

Add models for print jobs and allow triggering them

parent 506f6c2d
No related branches found
No related tags found
1 merge request!3Setup printer stuff
Pipeline #59955 passed
from django import forms
from django.utils.translation import gettext as _
from django_select2.forms import ModelSelect2Widget
from material import Layout
from aleksis.apps.kort.models import Card, CardPrinter
......@@ -25,3 +27,15 @@ class CardPrinterForm(forms.ModelForm):
class Meta:
model = CardPrinter
fields = ["name", "location", "description"]
class PrinterSelectForm(forms.Form):
layout = Layout("printer")
printer = forms.ModelChoiceField(queryset=None, label=_("Card Printer"), required=True)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
printers = CardPrinter.objects.all()
self.fields["printer"].queryset = printers
if printers.count() == 1:
self.fields["printer"].initial = printers.first()
# Generated by Django 3.2.12 on 2022-03-15 18:57
from django.conf import settings
import django.contrib.sites.managers
import django.core.validators
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.OAUTH2_PROVIDER_APPLICATION_MODEL),
('sites', '0002_alter_domain_unique'),
('kort', '0006_auto_20220310_2003'),
]
operations = [
migrations.AlterField(
model_name='card',
name='pdf_file',
field=models.FileField(blank=True, default='', upload_to='cards/', validators=[django.core.validators.FileExtensionValidator(['pdf'])], verbose_name='PDF file'),
preserve_default=False,
),
migrations.AlterField(
model_name='cardprinter',
name='oauth2_application',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='card_printers', to=settings.OAUTH2_PROVIDER_APPLICATION_MODEL, verbose_name='OAuth2 application'),
),
migrations.CreateModel(
name='CardPrintJob',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('extended_data', models.JSONField(default=dict, editable=False)),
('status', models.CharField(choices=[('registered', 'Registered'), ('in_progress', 'In progress'), ('finished', 'Finished')], default='registered', max_length=255, verbose_name='Status')),
('status_text', models.TextField(blank=True, verbose_name='Status text')),
('card', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='kort.card', verbose_name='Card')),
('printer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='kort.cardprinter', verbose_name='Printer')),
('site', models.ForeignKey(default=1, editable=False, on_delete=django.db.models.deletion.CASCADE, to='sites.site')),
],
options={
'verbose_name': 'Card print job',
'verbose_name_plural': 'Card print jobs',
},
managers=[
('objects', django.contrib.sites.managers.CurrentSiteManager()),
],
),
]
......@@ -50,6 +50,7 @@ class PrintStatus(models.TextChoices):
REGISTERED = "registered", _("Registered")
IN_PROGRESS = "in_progress", _("In progress")
FINISHED = "finished", _("Finished")
FAILED = "failed", _("Failed")
class CardPrinter(ExtensibleModel):
......@@ -187,6 +188,13 @@ class Card(ExtensibleModel):
return True
return generate_card_pdf.delay(self.pk)
def print_card(self, printer: CardPrinter):
job = CardPrintJob(card=self, printer=printer)
job.save()
if self.chip_number:
self.generate_pdf()
return job
def __str__(self):
if self.chip_number:
return f"{self.person} ({self.chip_number})"
......@@ -195,3 +203,22 @@ class Card(ExtensibleModel):
class Meta:
verbose_name = _("Card")
verbose_name_plural = _("Cards")
class CardPrintJob(ExtensibleModel):
printer = models.ForeignKey(
CardPrinter, on_delete=models.CASCADE, verbose_name=_("Printer"), related_name="jobs"
)
card = models.ForeignKey(Card, on_delete=models.CASCADE, verbose_name=_("Card"))
status = models.CharField(
max_length=255,
verbose_name=_("Status"),
choices=PrintStatus.choices,
default=PrintStatus.REGISTERED,
)
status_text = models.TextField(verbose_name=_("Status text"), blank=True)
class Meta:
verbose_name = _("Card print job")
verbose_name_plural = _("Card print jobs")
......@@ -11,6 +11,8 @@ from django_tables2 import (
Table,
)
from aleksis.apps.kort.forms import PrinterSelectForm
class CardTable(Table):
"""Table to list cards."""
......@@ -34,7 +36,9 @@ class CardTable(Table):
)
def render_actions(self, value, record):
return render_to_string("kort/card/actions.html", dict(pk=value, card=record))
return render_to_string(
"kort/card/actions.html", dict(pk=value, card=record, printer_form=PrinterSelectForm())
)
class CardPrinterTable(Table):
......@@ -48,6 +52,8 @@ class CardPrinterTable(Table):
current_status = Column(verbose_name=_("Current status"), accessor=A("pk"))
last_seen_at = DateTimeColumn(verbose_name=_("Last seen at"))
jobs_count = Column(verbose_name=_("Running jobs"))
actions = Column(verbose_name=_("Actions"), accessor=A("pk"))
def render_current_status(self, value, record):
......
......@@ -39,6 +39,7 @@
</div>
</div>
<div class="col s12 m12 l6">
{% include "kort/card/print_form.html" %}
{% if card.pdf_file %}
<div id="card-pdf-{{ card.pk }}" style="height: 500px;"></div>
<script>PDFObject.embed("{{ card.pdf_file.url }}", "#card-pdf-{{ card.pk }}");</script>
......
{% load material_form i18n %}
<form action="{% url "print_card" card.pk %}" method="get">
<div class="card">
<div class="card-content">
<div class="card-title"><i class="material-icons left iconify"
data-icon="mdi:printer-outline"></i> {% trans "Print card" %}</div>
{% csrf_token %}
{% form form=printer_form %}{% endform %}
</div>
<div class="card-action-light">
<button type="submit" class="btn waves-effect waves-light">
<i class="material-icons left iconify" data-icon="mdi:printer-outline"></i>
{% trans "Print card" %}
</button>
</div>
</div>
</form>
......@@ -74,6 +74,34 @@
<code>kort-client setup {{ printer.config_filename }}</code>
</div>
</div>
{% else %}
<div class="card">
<div class="card-content">
<div class="card-title">{% trans "Print jobs" %}</div>
<table>
<tr>
<th>
{% trans "Card" %}
</th>
<th>
{% trans "Status" %}
</th>
</tr>
{% for job in printer.jobs.all %}
<tr>
<td>
<a href="{% url "card" job.card.pk %}">
{{ job.card }}
</a>
</td>
<td>
{{ job.status }}
</td>
</tr>
{% endfor %}
</table>
</div>
</div>
{% endif %}
</div>
</div>
\ No newline at end of file
......@@ -13,6 +13,7 @@ urlpatterns = [
name="generate_card_pdf",
),
path("cards/<int:pk>/deactivate/", views.CardDeactivateView.as_view(), name="deactivate_card"),
path("cards/<int:pk>/print/", views.CardPrintView.as_view(), name="print_card"),
path("cards/<int:pk>/delete/", views.CardDeleteView.as_view(), name="delete_card"),
path("printers/", views.CardPrinterListView.as_view(), name="card_printers"),
path("printers/create/", views.CardPrinterCreateView.as_view(), name="create_card_printer"),
......
import json
from django.contrib import messages
from django.db.models import Count, Q
from django.http import HttpRequest, HttpResponse
from django.shortcuts import redirect, render
from django.shortcuts import get_object_or_404, redirect, render
from django.urls import reverse, reverse_lazy
from django.utils.translation import gettext as _
from django.views import View
......@@ -12,8 +13,8 @@ from django_tables2 import SingleTableView
from reversion.views import RevisionMixin
from rules.contrib.views import PermissionRequiredMixin
from aleksis.apps.kort.forms import CardForm, CardPrinterForm
from aleksis.apps.kort.models import Card, CardPrinter
from aleksis.apps.kort.forms import CardForm, CardPrinterForm, PrinterSelectForm
from aleksis.apps.kort.models import Card, CardPrinter, PrintStatus
from aleksis.apps.kort.tables import CardPrinterTable, CardTable
from aleksis.core.mixins import AdvancedCreateView, AdvancedDeleteView, AdvancedEditView
from aleksis.core.util.celery_progress import render_progress_page
......@@ -33,7 +34,16 @@ class TestPDFView(RenderPDFView):
return context
class CardListView(PermissionRequiredMixin, RevisionMixin, SingleTableView):
class PrinterSelectMixin:
def get_context_data(self, **kwargs):
print("Called and used?")
context = super().get_context_data(**kwargs)
context["printers"] = CardPrinter.objects.all()
context["printer_form"] = PrinterSelectForm()
return context
class CardListView(PermissionRequiredMixin, RevisionMixin, PrinterSelectMixin, SingleTableView):
"""List view for all cards."""
permission_required = "core.view_cards_rule"
......@@ -77,7 +87,31 @@ class CardDeactivateView(PermissionRequiredMixin, RevisionMixin, SingleObjectMix
return redirect(self.success_url)
class CardDetailView(PermissionRequiredMixin, RevisionMixin, DetailView):
class CardPrintView(PermissionRequiredMixin, RevisionMixin, SingleObjectMixin, View):
"""View used to create a print job for a card."""
permission_required = "core.delete_card_rule"
model = Card
success_url = reverse_lazy("cards")
def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
self.object = self.get_object()
printer = self.request.GET.get("printer")
printer = get_object_or_404(CardPrinter, pk=printer)
self.object.print_card(printer)
messages.success(
request,
_(
"The print job for the card {} on the printer {} has been created successfully."
).format(self.object.person, printer.name),
)
return redirect(self.success_url)
class CardDetailView(PermissionRequiredMixin, RevisionMixin, PrinterSelectMixin, DetailView):
permission_required = "core.view_card_rule"
model = Card
template_name = "kort/card/detail.html"
......@@ -122,6 +156,13 @@ class CardPrinterListView(PermissionRequiredMixin, RevisionMixin, SingleTableVie
model = CardPrinter
table_class = CardPrinterTable
def get_queryset(self):
return CardPrinter.objects.all().annotate(
jobs_count=Count(
"jobs", filter=~Q(jobs__status__in=[PrintStatus.FINISHED, PrintStatus.FAILED])
)
)
class CardPrinterCreateView(PermissionRequiredMixin, RevisionMixin, AdvancedCreateView):
"""View used to create a card printer."""
......
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