Initial work on a more lean infodesk.

This commit is contained in:
Víðir Valberg Guðmundsson 2019-07-17 22:02:47 +02:00
parent 24c95a82fd
commit 5911d2042b
8 changed files with 200 additions and 14 deletions

View file

@ -16,17 +16,14 @@
<div class="list-group">
{% if perms.camps.infoteam_permission %}
<h3>Info Team</h3>
<a href="{% url 'backoffice:product_handout' camp_slug=camp.slug %}" class="list-group-item">
<h4 class="list-group-item-heading">Hand Out Products</h4>
<p class="list-group-item-text">Use this view to mark products such as merchandise, cabins, fridges and so on as handed out.</p>
</a>
<a href="{% url 'backoffice:ticket_checkin' camp_slug=camp.slug %}" class="list-group-item">
<h4 class="list-group-item-heading">Check-In Tickets</h4>
<p class="list-group-item-text">Use this view to check-in tickets when participants arrive.</p>
</a>
<a href="{% url 'backoffice:badge_handout' camp_slug=camp.slug %}" class="list-group-item">
<h4 class="list-group-item-heading">Hand Out Badges</h4>
<p class="list-group-item-text">Use this view to mark badges as handed out.</p>
<a href="{% url 'backoffice:user_interaction' camp_slug=camp.slug %}"
class="list-group-item">
<h4 class="list-group-item-heading">
User stuff
</h4>
<p class="list-group-item-text">
Use this to get everything related to a user
</p>
</a>
{% endif %}

View file

@ -0,0 +1,13 @@
{% extends 'base.html' %}
{% load static from staticfiles %}
{% block content %}
Orders
Tickets (incl. badges)
Merchandise
{% endblock content %}

View file

@ -0,0 +1,90 @@
{% extends 'base.html' %}
{% load static from staticfiles %}
{% load qrcode %}
{% block content %}
<form id="search_form" method="POST" action="">
{% csrf_token %}
<div class="row">
<div class="col-md-12">
<h3>Scan the ticket!</h3>
<input type="text" name="ticket_token" id="ticket_token_input" autocomplete="off" style="color: #fff; background: #fff; border: 0; height: 0; width: 0;"/>
<span id="scan_again" hidden>Scan again!</span>
</div>
</div>
</form>
{% if ticket %}
{{ ticket }}<br />
<br />
Checked in?: {{ ticket.checked_in }}
<hr />
<form id="check_in_form" method="POST" action="">{% csrf_token %}
<input type="input" name="check_in_ticket_id" id="check_in_input" value="{{ ticket.pk }}" style="color: #fff; background: #fff; border: 0; height: 0; width: 0;"/>
</form>
<div class="row">
<div class="col-md-6">
{% qr_code "clear" %}
</div>
<div class="col-md-6">
{% qr_code "check-in" %}
</div>
</divc>
{% endif %}
<script>
document.addEventListener('DOMContentLoaded', () => {
'use strict';
const search_form = document.getElementById('search_form');
const ticket_token_input = document.getElementById('ticket_token_input');
const scan_again = document.getElementById('scan_again');
const check_in_input = document.getElementById('check_in_input');
const check_in_form = document.getElementById('check_in_form');
search_form.onsubmit = submit;
function submit(e) {
e.preventDefault();
console.log(ticket_token_input.value);
if(ticket_token_input.value === "#clear") {
window.location.replace(window.location.pathname);
} else if(ticket_token_input.value === "#check-in") {
check_in_input.checked = true;
check_in_form.submit();
} else if(ticket_token_input.value.length === 65) {
search_form.submit();
} else {
scan_again.removeAttribute('hidden');
}
};
document.addEventListener('keydown', event => {
if(event.key === '#') {
ticket_token_input.value = "";
ticket_token_input.focus();
}
});
});
</script>
{% endblock content %}

View file

@ -7,6 +7,9 @@ app_name = "backoffice"
urlpatterns = [
path("", BackofficeIndexView.as_view(), name="index"),
# infodesk
path(
"user/", include([path("", SearchForUser.as_view(), name="user_interaction")])
),
path("product_handout/", ProductHandoutView.as_view(), name="product_handout"),
path("badge_handout/", BadgeHandoutView.as_view(), name="badge_handout"),
path("ticket_checkin/", TicketCheckinView.as_view(), name="ticket_checkin"),

View file

@ -1,6 +1,7 @@
import logging, os
from itertools import chain
import qrcode
from django.contrib.auth.mixins import PermissionRequiredMixin, UserPassesTestMixin
from django.contrib.auth.models import User
from django.views.generic import TemplateView, ListView, DetailView
@ -14,7 +15,7 @@ from django.conf import settings
from django.core.files import File
from camps.mixins import CampViewMixin
from shop.models import OrderProductRelation
from shop.models import OrderProductRelation, Invoice, Order
from tickets.models import ShopTicket, SponsorTicket, DiscountTicket
from profiles.models import Profile
from program.models import SpeakerProposal, EventProposal
@ -345,7 +346,6 @@ class ReimbursementCreateView(CampViewMixin, EconomyTeamPermissionMixin, CreateV
def dispatch(self, request, *args, **kwargs):
""" Get the user from kwargs """
print("inside dispatch() with method %s" % request.method)
self.reimbursement_user = get_object_or_404(User, pk=kwargs["user_id"])
# get response now so we have self.camp available below
@ -544,3 +544,30 @@ class RevenueDetailView(CampViewMixin, EconomyTeamPermissionMixin, UpdateView):
return redirect(
reverse("backoffice:revenue_list", kwargs={"camp_slug": self.camp.slug})
)
class SearchForUser(TemplateView):
template_name = "user/search.html"
def post(self, request, **kwargs):
check_in_ticket_id = request.POST.get("check_in_ticket_id")
if check_in_ticket_id:
ticket_to_check_in = ShopTicket.objects.get(pk=check_in_ticket_id)
ticket_to_check_in.checked_in = True
ticket_to_check_in.save()
messages.info(request, "Ticket checked-in!")
return super().get(request, **kwargs)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
ticket_token = self.request.POST.get("ticket_token")
if ticket_token:
try:
ticket = ShopTicket.objects.get(token=ticket_token[1:])
context["ticket"] = ticket
except ShopTicket.DoesNotExist:
messages.warning(self.request, "Ticket not found!")
return context

View file

@ -0,0 +1,28 @@
# Generated by Django 2.2.2 on 2019-06-16 15:46
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('tickets', '0005_auto_20180318_0906'),
]
operations = [
migrations.AddField(
model_name='discountticket',
name='token',
field=models.CharField(max_length=64, null=True),
),
migrations.AddField(
model_name='shopticket',
name='token',
field=models.CharField(max_length=64, null=True),
),
migrations.AddField(
model_name='sponsorticket',
name='token',
field=models.CharField(max_length=64, null=True),
),
]

View file

@ -26,6 +26,7 @@ class BaseTicket(CampRelatedModel, UUIDModel):
ticket_type = models.ForeignKey("TicketType", on_delete=models.PROTECT)
checked_in = models.BooleanField(default=False)
badge_handed_out = models.BooleanField(default=False)
token = models.CharField(max_length=64)
class Meta:
abstract = True
@ -34,10 +35,14 @@ class BaseTicket(CampRelatedModel, UUIDModel):
def camp(self):
return self.ticket_type.camp
def save(self, **kwargs):
self.token = self._get_token()
super().save(**kwargs)
def _get_token(self):
return hashlib.sha256(
"{_id}{secret_key}".format(
_id=self.pk, secret_key=settings.SECRET_KEY
_id=self.uuid, secret_key=settings.SECRET_KEY
).encode("utf-8")
).hexdigest()

View file

@ -0,0 +1,23 @@
import base64
import io
import qrcode
from django import template
from django.utils.safestring import mark_safe
register = template.Library()
@register.simple_tag
def qr_code(value):
stream = io.BytesIO()
img = qrcode.make("#" + value, box_size=5)
img.save(stream, "PNG")
data = base64.b64encode(stream.getvalue())
return mark_safe(
"<figure>"
'<img src="data:image/png;base64,{}" alt="">'
"<figcaption>{}</figcaption>"
"</figure>".format(data.decode(), value)
)