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"> <div class="list-group">
{% if perms.camps.infoteam_permission %} {% if perms.camps.infoteam_permission %}
<h3>Info Team</h3> <h3>Info Team</h3>
<a href="{% url 'backoffice:product_handout' camp_slug=camp.slug %}" class="list-group-item"> <a href="{% url 'backoffice:user_interaction' camp_slug=camp.slug %}"
<h4 class="list-group-item-heading">Hand Out Products</h4> class="list-group-item">
<p class="list-group-item-text">Use this view to mark products such as merchandise, cabins, fridges and so on as handed out.</p> <h4 class="list-group-item-heading">
</a> User stuff
<a href="{% url 'backoffice:ticket_checkin' camp_slug=camp.slug %}" class="list-group-item"> </h4>
<h4 class="list-group-item-heading">Check-In Tickets</h4> <p class="list-group-item-text">
<p class="list-group-item-text">Use this view to check-in tickets when participants arrive.</p> Use this to get everything related to a user
</a> </p>
<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> </a>
{% endif %} {% 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 = [ urlpatterns = [
path("", BackofficeIndexView.as_view(), name="index"), path("", BackofficeIndexView.as_view(), name="index"),
# infodesk # infodesk
path(
"user/", include([path("", SearchForUser.as_view(), name="user_interaction")])
),
path("product_handout/", ProductHandoutView.as_view(), name="product_handout"), path("product_handout/", ProductHandoutView.as_view(), name="product_handout"),
path("badge_handout/", BadgeHandoutView.as_view(), name="badge_handout"), path("badge_handout/", BadgeHandoutView.as_view(), name="badge_handout"),
path("ticket_checkin/", TicketCheckinView.as_view(), name="ticket_checkin"), path("ticket_checkin/", TicketCheckinView.as_view(), name="ticket_checkin"),

View file

@ -1,6 +1,7 @@
import logging, os import logging, os
from itertools import chain from itertools import chain
import qrcode
from django.contrib.auth.mixins import PermissionRequiredMixin, UserPassesTestMixin from django.contrib.auth.mixins import PermissionRequiredMixin, UserPassesTestMixin
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.views.generic import TemplateView, ListView, DetailView from django.views.generic import TemplateView, ListView, DetailView
@ -14,7 +15,7 @@ from django.conf import settings
from django.core.files import File from django.core.files import File
from camps.mixins import CampViewMixin 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 tickets.models import ShopTicket, SponsorTicket, DiscountTicket
from profiles.models import Profile from profiles.models import Profile
from program.models import SpeakerProposal, EventProposal from program.models import SpeakerProposal, EventProposal
@ -345,7 +346,6 @@ class ReimbursementCreateView(CampViewMixin, EconomyTeamPermissionMixin, CreateV
def dispatch(self, request, *args, **kwargs): def dispatch(self, request, *args, **kwargs):
""" Get the user from 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"]) self.reimbursement_user = get_object_or_404(User, pk=kwargs["user_id"])
# get response now so we have self.camp available below # get response now so we have self.camp available below
@ -544,3 +544,30 @@ class RevenueDetailView(CampViewMixin, EconomyTeamPermissionMixin, UpdateView):
return redirect( return redirect(
reverse("backoffice:revenue_list", kwargs={"camp_slug": self.camp.slug}) 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) ticket_type = models.ForeignKey("TicketType", on_delete=models.PROTECT)
checked_in = models.BooleanField(default=False) checked_in = models.BooleanField(default=False)
badge_handed_out = models.BooleanField(default=False) badge_handed_out = models.BooleanField(default=False)
token = models.CharField(max_length=64)
class Meta: class Meta:
abstract = True abstract = True
@ -34,10 +35,14 @@ class BaseTicket(CampRelatedModel, UUIDModel):
def camp(self): def camp(self):
return self.ticket_type.camp return self.ticket_type.camp
def save(self, **kwargs):
self.token = self._get_token()
super().save(**kwargs)
def _get_token(self): def _get_token(self):
return hashlib.sha256( return hashlib.sha256(
"{_id}{secret_key}".format( "{_id}{secret_key}".format(
_id=self.pk, secret_key=settings.SECRET_KEY _id=self.uuid, secret_key=settings.SECRET_KEY
).encode("utf-8") ).encode("utf-8")
).hexdigest() ).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)
)