Skip to content

Commit d4c8768

Browse files
authored
Merge pull request #98 from HackAssistant/meals
Modified meals permissions and added checkin admin to change QR codes
2 parents 7feeab1 + 016a745 commit d4c8768

File tree

9 files changed

+110
-35
lines changed

9 files changed

+110
-35
lines changed

event/apps.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,21 @@ def create_new_permissions(self):
1212
from application.models import ApplicationTypeConfig
1313

1414
permissions = ['can_checkin', ]
15+
not_type_permissions = ['can_checkin_meal', ]
1516

1617
content_type = ContentType.objects.get_or_create(app_label='event', model='event')[0]
1718

19+
for permission in not_type_permissions:
20+
name = permission.replace('_', ' ').capitalize()
21+
Permission.objects.get_or_create(codename=permission, defaults={'name': name,
22+
'content_type': content_type})
23+
24+
application_types = ApplicationTypeConfig.objects.all()
25+
1826
for permission in permissions:
1927
name = permission.replace('_', ' ').capitalize()
20-
Permission.objects.get_or_create(codename=permission, defaults={'name': 'Can checkin',
28+
Permission.objects.get_or_create(codename=permission, defaults={'name': name,
2129
'content_type': content_type})
22-
application_types = ApplicationTypeConfig.objects.all()
2330
for application_type in application_types:
2431
Permission.objects.get_or_create(
2532
codename='%s_%s' % (permission, application_type.name.lower()),
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Generated by Django 4.0.8 on 2022-12-29 11:01
2+
3+
from django.db import migrations
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
('meals', '0002_alter_meal_options'),
10+
]
11+
12+
operations = [
13+
migrations.AlterModelOptions(
14+
name='meal',
15+
options={},
16+
),
17+
]

event/meals/models.py

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
from django.db import models
22
from user.models import User
33

4-
from django.utils.translation import gettext_lazy as _
54

65
MEAL_TYPE = (
76
('B', 'Breakfast'),
@@ -32,11 +31,6 @@ def __str__(self):
3231
def eaten(self):
3332
return self.eaten_set.count()
3433

35-
class Meta:
36-
permissions = (
37-
('can_checkin_meals', _('Can checkin meals')),
38-
)
39-
4034

4135
class Eaten(models.Model):
4236
"""Represents when a hacker has eatean a meal"""

event/meals/views.py

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,8 @@
1515
from user.models import User
1616

1717

18-
# TODO: create --> meal list
19-
# First meal, closest to time
20-
# API amb python
21-
# TODO: QR view (que el meal seleccionat no és actual)
22-
# TODO: optional: meal edit i meal create (UI)
23-
24-
2518
class MealsList(PermissionRequiredMixin, SingleTableMixin, TemplateView):
26-
permission_required = 'meals.can_checkin_meals'
19+
permission_required = 'event.can_checkin_meal'
2720
template_name = 'meals_list.html'
2821
table_class = MealsTable
2922

@@ -33,12 +26,16 @@ def get_queryset(self):
3326

3427
class CheckinMeal(PermissionRequiredMixin, SingleTableMixin, FilterView):
3528
template_name = 'checkin_meal.html'
36-
permission_required = 'meals.can_checkin_meals'
29+
permission_required = 'event.can_checkin_meal'
3730
table_class = CheckinMealTable
3831
filterset_class = MealsTableFilter
3932

40-
def dispatch(self, request, *args, **kwargs):
41-
return super().dispatch(request, *args, **kwargs)
33+
def handle_no_permission(self):
34+
if self.request.method == 'POST':
35+
if not self.request.user.is_authenticated:
36+
return JsonResponse({'errors': ['Unauthorized, you are not logged in.']}, status=401)
37+
return JsonResponse({'errors': ['You have no permissions.']}, status=403)
38+
return super().handle_no_permission()
4239

4340
def get_queryset(self):
4441
return get_user_model().objects.filter(application__status=Application.STATUS_ATTENDED,

event/templates/checkin_list.html

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,21 @@
1010

1111
{% block content %}
1212

13-
<h1 class="mt-3">{% translate 'Checkin users' %}</h1>
13+
<div class="row m-0 mt-3">
14+
<div class="col-lg-9">
15+
<h1 class="">{% translate 'Checkin users' %}{% if admin %} {% translate 'admin' %}{% endif %}</h1>
16+
</div>
17+
{% if request.user.is_staff and not admin %}
18+
<div class="col-lg-3">
19+
<a class="btn btn-secondary col-12" href="{% url 'checkin_list_admin' %}">{% translate 'Admin checkin' %}</a>
20+
</div>
21+
{% else %}
22+
<div class="col-lg-3">
23+
<a class="btn btn-secondary col-12" href="{% url 'checkin_list' %}"><i class="bi bi-caret-left-fill"></i> {% translate 'Back' %}</a>
24+
</div>
25+
{% endif %}
26+
</div>
27+
1428

1529
<div class="mt-2" style="display: flex">
1630
<form method="get" id="filter-form" style="width: 100%" class="mr-2">

event/templates/checkin_user.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@
4242
<div class="mb-3 ms-3 me-3">
4343
<video id="qr-video" style="width: 100%; height: auto; display: none"></video>
4444
<div style="position: relative">
45-
<input type="text" name="qr_code" class="form-control bg-{{ theme }} text-white" placeholder="{% translate 'QR code' %}" id="id_qr_code">
45+
<input type="text" name="qr_code" class="form-control bg-{{ theme }} text-{% if theme == 'dark' %}white{% else %}black{% endif %}" placeholder="{% translate 'QR code' %}" id="id_qr_code">
4646
<i id="button-rescan" class="bi bi-arrow-repeat fs-4" style="display: none; position: absolute; z-index: 1; right: 3%; top: 7%; cursor: pointer"></i>
4747
</div>
4848
</div>
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
<a class="btn btn-primary btn-sm" href='{% url 'checkin_user' record.get_encoded_pk %}'>Check-in</a>
1+
<a class="btn btn-primary btn-sm" href='{% url 'checkin_user' record.get_encoded_pk %}?next={{ request.get_full_path|urlencode }}'>Check-in</a>

event/urls.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
urlpatterns = [
77
path('checkin/', views.CheckinList.as_view(), name='checkin_list'),
8+
path('checkin/admin/', views.CheckinAdminList.as_view(), name='checkin_list_admin'),
89
path('checkin/<str:uid>/', views.CheckinUser.as_view(), name='checkin_user'),
910
]
1011

event/views.py

Lines changed: 58 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from django.contrib.auth import get_user_model
66
from django.contrib.auth.models import Group
77
from django.db import transaction
8+
from django.db.models import Q
89
from django.shortcuts import redirect
910
from django.urls import reverse
1011
from django.views.generic import TemplateView
@@ -25,8 +26,9 @@ class CheckinList(AnyApplicationPermissionRequiredMixin, SingleTableMixin, Filte
2526
filterset_class = CheckinTableFilter
2627

2728
def get_queryset(self):
28-
return get_user_model().objects.filter(application__status=Application.STATUS_CONFIRMED,
29-
application__edition=Edition.get_default_edition()).distinct()
29+
return get_user_model().objects.filter(Q(application__status=Application.STATUS_CONFIRMED,
30+
application__edition=Edition.get_default_edition()) |
31+
Q(groups__name='Organizer', qr_code='')).distinct()
3032

3133

3234
class CheckinUser(TemplateView):
@@ -41,14 +43,23 @@ def has_permission(self, types):
4143
return False
4244
return True
4345

46+
def get_accepted_status_to_checkin(self):
47+
accepted_status = [Application.STATUS_CONFIRMED]
48+
if self.request.user.is_staff:
49+
accepted_status.append(Application.STATUS_ATTENDED)
50+
return accepted_status
51+
4452
def get_context_data(self, **kwargs):
4553
context = super().get_context_data(**kwargs)
4654
User = get_user_model()
4755
try:
4856
uid = User.decode_encoded_pk(kwargs.get('uid'))
4957
user = User.objects.get(pk=uid)
50-
application_types = user.application_set.actual().filter(status=Application.STATUS_CONFIRMED)\
51-
.values_list('type__name', flat=True)
58+
accepted_status = self.get_accepted_status_to_checkin()
59+
application_types = list(user.application_set.actual().filter(status__in=accepted_status)
60+
.values_list('type__name', flat=True))
61+
if user.is_organizer():
62+
application_types.append('Organizer')
5263
context.update({'app_user': user, 'types': application_types,
5364
'has_permission': self.has_permission(types=application_types)})
5465
except (User.DoesNotExist, ValueError):
@@ -61,26 +72,60 @@ def get_code(self):
6172
return ''.join([choice(string.ascii_letters + string.digits + string.punctuation) for i in range(12)])
6273
return qr_code
6374

75+
def manage_application_confirm(self, application):
76+
application.set_status(Application.STATUS_ATTENDED)
77+
application.save()
78+
application_log = ApplicationLog(application=application, user=self.request.user, comment='',
79+
name='Checked-in')
80+
application_log.changes = {'status': {'new': Application.STATUS_ATTENDED,
81+
'old': Application.STATUS_CONFIRMED}}
82+
application_log.save()
83+
84+
def manage_application_attended(self, application):
85+
application_log = ApplicationLog(application=application, user=self.request.user, comment='',
86+
name='Changed QR code')
87+
application_log.save()
88+
89+
def redirect_successful(self):
90+
next_ = self.request.GET.get('next', reverse('checkin_list'))
91+
if next_[0] != '/':
92+
next_ = reverse('checkin_list')
93+
return redirect(next_)
94+
6495
def post(self, request, *args, **kwargs):
6596
context = self.get_context_data(**kwargs)
6697
qr_code = self.get_code()
6798
if context['has_permission'] and len(context['types']) > 0 and qr_code is not None:
6899
user = context['app_user']
69100
user.qr_code = qr_code
70-
applications = user.application_set.actual().filter(status=Application.STATUS_CONFIRMED)\
101+
accepted_status = self.get_accepted_status_to_checkin()
102+
applications = user.application_set.actual().filter(status__in=accepted_status)\
71103
.select_related('type')
72104
groups = Group.objects.filter(name__in=context['types'])
73105
with transaction.atomic():
74106
user.save()
75107
user.groups.add(*groups)
76108
for application in applications:
77-
application.set_status(Application.STATUS_ATTENDED)
78-
application.save()
79-
application_log = ApplicationLog(application=application, user=request.user, comment='',
80-
name='Checked-in')
81-
application_log.changes = {'status': {'new': Application.STATUS_ATTENDED,
82-
'old': Application.STATUS_CONFIRMED}}
83-
application_log.save()
84-
return redirect(reverse('checkin_list'))
109+
if application.status == Application.STATUS_CONFIRMED:
110+
self.manage_application_confirm(application)
111+
else:
112+
self.manage_application_attended(application)
113+
messages.success(request, _('User checked in!'))
114+
return self.redirect_successful()
85115
messages.error(request, _('Permission denied'))
86116
return self.render_to_response(context)
117+
118+
119+
class CheckinAdminList(CheckinList):
120+
def get_queryset(self):
121+
if self.request.user.is_staff:
122+
return get_user_model().objects.filter(Q(application__status__in=[Application.STATUS_CONFIRMED,
123+
Application.STATUS_ATTENDED],
124+
application__edition=Edition.get_default_edition()) |
125+
Q(groups__name='Organizer')).distinct()
126+
return get_user_model().objects.none()
127+
128+
def get_context_data(self, **kwargs):
129+
context = super().get_context_data(**kwargs)
130+
context.update({'admin': True})
131+
return context

0 commit comments

Comments
 (0)