Add Venueless integration to Regidesk
Create Venueless login token as part of checkin to allow attendee to join an online event from the dashboard.
This commit is contained in:
parent
ef148ea482
commit
7c77001d5e
11 changed files with 142 additions and 18 deletions
|
@ -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)
|
||||
|
|
|
@ -41,6 +41,18 @@
|
|||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
{% flag "venueless_dashboard" %}
|
||||
<div class="row">
|
||||
<div class="col-12 mb-3">
|
||||
<h3>Join the Conference</h3>
|
||||
<p>The conference is now open. Please join us to watch talks and interact with the other delegates.</p>
|
||||
<div>
|
||||
<a class="btn btn-lg btn-primary" role="button" href="{% venueless_login_url %}">Launch Conference</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endflag %}
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3 mb-md-0">
|
||||
<h3>Attendee Profile</h3>
|
||||
|
|
|
@ -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}'
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 = ("<html>\r\n <body>\r\n <p>This is your boarding "
|
||||
|
@ -23,9 +23,9 @@ def create_lca2018_template(apps, schema_editor):
|
|||
"phone or on a print out.</p>\r\n "
|
||||
"<p><img src=\"data:image/png;base64,{{ qrcode }}\" /></p>\r\n"
|
||||
" <p>Backup Code: {{ code }}</p>\r\n </body>\r\n</html>")
|
||||
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)
|
||||
|
|
28
vendor/regidesk/regidesk/migrations/0005_auto_20210108_2347.py
vendored
Normal file
28
vendor/regidesk/regidesk/migrations/0005_auto_20210108_2347.py
vendored
Normal file
|
@ -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),
|
||||
),
|
||||
]
|
46
vendor/regidesk/regidesk/models.py
vendored
46
vendor/regidesk/regidesk/models.py
vendored
|
@ -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
|
||||
|
|
|
@ -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 %}
|
||||
<!-- {% 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 %}
|
||||
|
|
|
@ -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 %}
|
||||
<!-- {% 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 %}
|
||||
|
|
|
@ -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 %}
|
||||
<!-- {% 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 %}
|
||||
|
||||
|
||||
|
|
|
@ -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 %}
|
||||
<!-- {% 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 %}
|
||||
|
||||
<a type="button" class="btn btn-outline-primary" href="{% url 'regidesk:check_in_scanner' %}">Return to scanning page</a>
|
||||
|
@ -36,6 +36,7 @@
|
|||
<dt class="col-sm-3">Free Text 2</dt>
|
||||
<dd class="col-sm-9">{{ user.attendee.attendeeprofilebase.attendeeprofile.free_text_2 }}</dd>
|
||||
|
||||
{% comment "Not needed for LCA2021 online" %}
|
||||
<dt class="col-sm-3">Penguin Dinner Tickets</dt>
|
||||
<dd class="col-sm-9">{{ penguin_dinner_count }}</dd>
|
||||
|
||||
|
@ -47,11 +48,13 @@
|
|||
|
||||
<dt class="col-sm-3">Over 18 years</dt>
|
||||
<dd class="col-sm-9">{% if user.attendee.attendeeprofilebase.attendeeprofile.of_legal_age %}Yes{% else %}<strong class="red">NO</strong>{% endif %}</dd>
|
||||
{% endcomment %}
|
||||
|
||||
<dt class="col-sm-3">Username</dt>
|
||||
<dd class="col-sm-9">{{ user.username }}</dd>
|
||||
</dl>
|
||||
|
||||
{% comment "Not needed for LCA2021 online" %}
|
||||
<h4>Shirts ordered</h4>
|
||||
<table class="table card-text">
|
||||
{% for shirt in shirts%}
|
||||
|
@ -61,6 +64,23 @@
|
|||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
{% endcomment %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card my-3">
|
||||
<div class="card-header">Venueless</div>
|
||||
<div class="card-body">
|
||||
<dl class="card-text row">
|
||||
<dt class="col-sm-3">User Id</dt>
|
||||
<dd class="col-sm-9">{{ check_in.venueless_user_id }}</dd>
|
||||
|
||||
<dt class="col-sm-3">Traits</dt>
|
||||
<dd class="col-sm-9">{{ check_in.venueless_traits }}</dd>
|
||||
|
||||
<dt class="col-sm-3">Token</dt>
|
||||
<dd class="col-sm-9">{{ check_in.venueless_token|truncatechars:20 }}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -108,6 +128,7 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
{% comment "Not needed for LCA2021 online" %}
|
||||
<div class="card {% if check_in.schwag_given %}border-success{% else %}border-danger{% endif %} my-3">
|
||||
<div class="card-header {% if check_in.schwag_given %}text-white bg-success{% endif %}">Schwag</div>
|
||||
<div class="card-body">
|
||||
|
@ -121,6 +142,7 @@
|
|||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{% endcomment %}
|
||||
|
||||
<div class="card my-3">
|
||||
<div class="card-header">Log Exception</div>
|
||||
|
@ -133,6 +155,7 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
{% comment "Not needed for LCA2021 online" %}
|
||||
<div class="card my-3 {% if check_in.checked_in_bool and check_in.schwag_given %}border-success{% elif check_in.checked_in_bool or check_in.schwag_given %}card-warning{% else %}card-danger{% endif %}">
|
||||
<div class="card-header {% if check_in.checked_in_bool and check_in.schwag_given %}text-white bg-success{% elif check_in.checked_in_bool or check_in.schwag_given %}bg-warning{% endif %}">Bulk actions</div>
|
||||
<div class="card-body">
|
||||
|
@ -153,6 +176,7 @@
|
|||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{% endcomment %}
|
||||
|
||||
<a type="button" class="btn btn-outline-primary" href="{% url 'regidesk:check_in_scanner' %}">Return to scanning page</a>
|
||||
|
||||
|
|
Loading…
Reference in a new issue