From 7c77001d5e7db6fb37182d1960db432ebced52a5 Mon Sep 17 00:00:00 2001 From: Joel Addison Date: Sat, 9 Jan 2021 01:26:13 +1000 Subject: [PATCH] Add Venueless integration to Regidesk Create Venueless login token as part of checkin to allow attendee to join an online event from the dashboard. --- pinaxcon/settings.py | 7 +++ .../symposion/dashboard/_categories.html | 12 +++++ pinaxcon/templatetags/lca2018_tags.py | 8 ++++ requirements.txt | 1 + .../regidesk/migrations/0001_initial.py | 8 ++-- .../migrations/0005_auto_20210108_2347.py | 28 +++++++++++ vendor/regidesk/regidesk/models.py | 46 ++++++++++++++++++- .../templates/regidesk/boardingpass.html | 6 +-- .../regidesk/boardingpass_overview.html | 6 +-- .../regidesk/boardingpass_prepare.html | 6 +-- .../templates/regidesk/ci_overview.html | 32 +++++++++++-- 11 files changed, 142 insertions(+), 18 deletions(-) create mode 100644 vendor/regidesk/regidesk/migrations/0005_auto_20210108_2347.py diff --git a/pinaxcon/settings.py b/pinaxcon/settings.py index 8eee3063..a7c32f96 100644 --- a/pinaxcon/settings.py +++ b/pinaxcon/settings.py @@ -570,3 +570,10 @@ SPEAKERS_DINNER_ADULT = SpeakersDinnerCat.create( # "Infant must be seated in an adult's lap. " # "No food or beverage service.", # timedelta(hours=1)) + + +# Venueless integration +VENUELESS_URL = os.environ.get('VENUELESS_URL', None) +VENUELESS_AUDIENCE = os.environ.get('VENUELESS_AUDIENCE', "venueless") +VENUELESS_TOKEN_ISSUER = os.environ.get('VENUELESS_TOKEN_ISSUER', "any") +VENUELESS_SECRET = os.environ.get('VENUELESS_SECRET', SECRET_KEY) diff --git a/pinaxcon/templates/symposion/dashboard/_categories.html b/pinaxcon/templates/symposion/dashboard/_categories.html index e90e639f..d9639456 100644 --- a/pinaxcon/templates/symposion/dashboard/_categories.html +++ b/pinaxcon/templates/symposion/dashboard/_categories.html @@ -41,6 +41,18 @@ {% else %} + {% flag "venueless_dashboard" %} +
+
+

Join the Conference

+

The conference is now open. Please join us to watch talks and interact with the other delegates.

+
+ Launch Conference +
+
+
+ {% endflag %} +

Attendee Profile

diff --git a/pinaxcon/templatetags/lca2018_tags.py b/pinaxcon/templatetags/lca2018_tags.py index b6cc7a68..ee2ab073 100644 --- a/pinaxcon/templatetags/lca2018_tags.py +++ b/pinaxcon/templatetags/lca2018_tags.py @@ -8,6 +8,7 @@ from easy_thumbnails.files import get_thumbnailer from registrasion.templatetags import registrasion_tags from symposion.conference import models as conference_models from symposion.schedule.models import Track +from regidesk.models import CheckIn CONFERENCE_ID = settings.CONFERENCE_ID GST_RATE = settings.GST_RATE @@ -158,3 +159,10 @@ def ticket_type(context): # Default to product type return ticket_type + + +@register.simple_tag(takes_context=True) +def venueless_login_url(context): + user=context.request.user + checkin = CheckIn.objects.get_or_create(user=user)[0] + return f'{settings.VENUELESS_URL}/#token={checkin.venueless_token}' diff --git a/requirements.txt b/requirements.txt index db250ac0..ed39565c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -36,6 +36,7 @@ django-ical==1.7.1 # Registrasion reqs django-nested-admin==3.3.2 CairoSVG==2.4.2 +PyJWT==2.0.0 # Registripe django-countries>=6.1.3 diff --git a/vendor/regidesk/regidesk/migrations/0001_initial.py b/vendor/regidesk/regidesk/migrations/0001_initial.py index 4e66e721..e9e75d82 100644 --- a/vendor/regidesk/regidesk/migrations/0001_initial.py +++ b/vendor/regidesk/regidesk/migrations/0001_initial.py @@ -14,7 +14,7 @@ def create_lca2018_template(apps, schema_editor): BoardingPassTemplate = apps.get_model("regidesk", "BoardingPassTemplate") body = ("This is the plain text version of your boarding pass for " - "linux.conf.au 2018.\r\n\r\nWhen you check in at LCA, you'll " + "linux.conf.au 2021.\r\n\r\nWhen you check in at LCA, you'll " "need to show the QR code you can download from " "{{ qrcode_url }}, or quote registration code: {{ code }} ") html = ("\r\n \r\n

This is your boarding " @@ -23,9 +23,9 @@ def create_lca2018_template(apps, schema_editor): "phone or on a print out.

\r\n " "

\r\n" "

Backup Code: {{ code }}

\r\n \r\n") - template = BoardingPassTemplate(label="LCA2018", - from_address="team@lca2018.org", - subject="Your boarding pass for LCA2018, " + template = BoardingPassTemplate(label="LCA2021", + from_address="contact@lca2021.linux.org.au", + subject="Your boarding pass for LCA2021, " "{{ user.attendee.attendeeprofilebase.attendeeprofile.name }}", body=body, html_body=html) diff --git a/vendor/regidesk/regidesk/migrations/0005_auto_20210108_2347.py b/vendor/regidesk/regidesk/migrations/0005_auto_20210108_2347.py new file mode 100644 index 00000000..1b04cfd6 --- /dev/null +++ b/vendor/regidesk/regidesk/migrations/0005_auto_20210108_2347.py @@ -0,0 +1,28 @@ +# Generated by Django 2.2.17 on 2021-01-08 23:47 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('regidesk', '0004_auto_20180121_1140'), + ] + + operations = [ + migrations.AddField( + model_name='checkin', + name='venueless_traits', + field=models.TextField(blank=True, max_length=256, null=True), + ), + migrations.AddField( + model_name='checkin', + name='venueless_user_id', + field=models.TextField(blank=True, max_length=40, null=True), + ), + migrations.AddField( + model_name='checkin', + name='_venueless_token', + field=models.TextField(blank=True, null=True), + ), + ] diff --git a/vendor/regidesk/regidesk/models.py b/vendor/regidesk/regidesk/models.py index b8417ef2..87cce9b0 100644 --- a/vendor/regidesk/regidesk/models.py +++ b/vendor/regidesk/regidesk/models.py @@ -1,12 +1,14 @@ # -*- coding: utf-8 -*- import base64 -from datetime import datetime +import datetime from decimal import Decimal from io import BytesIO +import jwt from django.core.exceptions import ValidationError from django.utils import timezone +from django.conf import settings from django.db import models from django.db.models import Q, F from django.db.models import Case, When, Value @@ -79,6 +81,10 @@ class CheckIn(models.Model): needs_review = models.BooleanField(default=False) review_text = models.TextField(blank=True) + venueless_user_id = models.TextField(max_length=40, null=True, blank=True) + venueless_traits = models.TextField(max_length=256, null=True, blank=True) + _venueless_token = models.TextField(null=True, blank=True) + class Meta: permissions = ( ("view_checkin_details", "Can view the details of other user's checkins"), @@ -143,3 +149,41 @@ class CheckIn(models.Model): self.save() return self._checkin_code_png + + @property + def venueless_token(self): + """Returns the Venueless JWT token for this checkin's code.""" + updated = False + if not self.venueless_user_id: + self.venueless_user_id = self.checkin_code + updated = True + + if not self.venueless_traits: + self.venueless_traits = ".".join(("attendee",)) + updated = True + + if not self._venueless_token: + self._venueless_token = self._generate_venueless_token() + updated = True + + if updated: + self.save() + + return self._venueless_token + + def _generate_venueless_token(self): + """ Generate token for Venueless login """ + + issued_at = datetime.datetime.utcnow() + expiry = settings.LCA_END + datetime.timedelta(days=1) + + payload = { + "iss": settings.VENUELESS_TOKEN_ISSUER, + "aud": settings.VENUELESS_AUDIENCE, + "iat": issued_at, + "exp": expiry, + "uid": self.venueless_user_id, + "traits": self.venueless_traits.split(','), + } + token = jwt.encode(payload, settings.VENUELESS_SECRET, algorithm="HS256") + return token diff --git a/vendor/regidesk/regidesk/templates/regidesk/boardingpass.html b/vendor/regidesk/regidesk/templates/regidesk/boardingpass.html index 21adc3a3..e6e7ab04 100644 --- a/vendor/regidesk/regidesk/templates/regidesk/boardingpass.html +++ b/vendor/regidesk/regidesk/templates/regidesk/boardingpass.html @@ -6,9 +6,9 @@ {% items_purchased as purchased %} {% items_pending as pending %} {% items_purchased 1 as ticket %} -{% total_items_purchased 2 as penguin_dinner_count %} -{% total_items_purchased 3 as speakers_dinner_count %} -{% total_items_purchased 4 as pdns_count %} + + + {% ticket_type as ticket_type %} {% block header_title %} diff --git a/vendor/regidesk/regidesk/templates/regidesk/boardingpass_overview.html b/vendor/regidesk/regidesk/templates/regidesk/boardingpass_overview.html index 86f2f147..a43106f5 100644 --- a/vendor/regidesk/regidesk/templates/regidesk/boardingpass_overview.html +++ b/vendor/regidesk/regidesk/templates/regidesk/boardingpass_overview.html @@ -6,9 +6,9 @@ {% items_purchased as purchased %} {% items_pending as pending %} {% items_purchased 1 as ticket %} -{% total_items_purchased 2 as penguin_dinner_count %} -{% total_items_purchased 3 as speakers_dinner_count %} -{% total_items_purchased 4 as pdns_count %} + + + {% ticket_type as ticket_type %} {% block body_class %}{{ block.super }} review-results{% endblock %} diff --git a/vendor/regidesk/regidesk/templates/regidesk/boardingpass_prepare.html b/vendor/regidesk/regidesk/templates/regidesk/boardingpass_prepare.html index 147a5bf3..60ed7bdf 100644 --- a/vendor/regidesk/regidesk/templates/regidesk/boardingpass_prepare.html +++ b/vendor/regidesk/regidesk/templates/regidesk/boardingpass_prepare.html @@ -6,9 +6,9 @@ {% items_purchased as purchased %} {% items_pending as pending %} {% items_purchased 1 as ticket %} -{% total_items_purchased 2 as penguin_dinner_count %} -{% total_items_purchased 3 as speakers_dinner_count %} -{% total_items_purchased 4 as pdns_count %} + + + {% ticket_type as ticket_type %} diff --git a/vendor/regidesk/regidesk/templates/regidesk/ci_overview.html b/vendor/regidesk/regidesk/templates/regidesk/ci_overview.html index 4aba4d40..441dc632 100644 --- a/vendor/regidesk/regidesk/templates/regidesk/ci_overview.html +++ b/vendor/regidesk/regidesk/templates/regidesk/ci_overview.html @@ -9,10 +9,10 @@ {% items_purchased as purchased %} {% items_pending as pending %} {% items_purchased 1 as ticket %} -{% items_purchased 6 as shirts %} -{% total_items_purchased 3 as penguin_dinner_count %} -{% total_items_purchased 4 as speakers_dinner_count %} -{% total_items_purchased 5 as pdns_count %} + + + + {% ticket_type as ticket_type %} Return to scanning page @@ -36,6 +36,7 @@
Free Text 2
{{ user.attendee.attendeeprofilebase.attendeeprofile.free_text_2 }}
+ {% comment "Not needed for LCA2021 online" %}
Penguin Dinner Tickets
{{ penguin_dinner_count }}
@@ -47,11 +48,13 @@
Over 18 years
{% if user.attendee.attendeeprofilebase.attendeeprofile.of_legal_age %}Yes{% else %}NO{% endif %}
+ {% endcomment %}
Username
{{ user.username }}
+ {% comment "Not needed for LCA2021 online" %}

Shirts ordered

{% for shirt in shirts%} @@ -61,6 +64,23 @@ {% endfor %}
+ {% endcomment %} +
+
+ +
+
Venueless
+
+
+
User Id
+
{{ check_in.venueless_user_id }}
+ +
Traits
+
{{ check_in.venueless_traits }}
+ +
Token
+
{{ check_in.venueless_token|truncatechars:20 }}
+
@@ -108,6 +128,7 @@ +{% comment "Not needed for LCA2021 online" %}
Schwag
@@ -121,6 +142,7 @@
+{% endcomment %}
Log Exception
@@ -133,6 +155,7 @@
+{% comment "Not needed for LCA2021 online" %}
Bulk actions
@@ -153,6 +176,7 @@
+{% endcomment %} Return to scanning page