Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Offline checkin feature for a smooth registration workflow during internet outage at events. #151

Closed
wants to merge 172 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
172 commits
Select commit Hold shift + click to select a range
b1311e7
Offline support for attendee check-in
vidya-ram Nov 16, 2015
d272421
Add CSRF token to check-in form.
vidya-ram Nov 17, 2015
4043bb5
Removed global enable of CSRF.
vidya-ram Nov 17, 2015
1eb3ce0
merge event_views, resolved conflicts. Added call to footable redraw …
vidya-ram Nov 18, 2015
a0e3eb9
merge event_views, resolved conflicts. Added a call to footable redra…
vidya-ram Nov 18, 2015
15f0b54
Fixed JS warnings.
vidya-ram Nov 19, 2015
20d7454
Merge branch 'offline_checkin' of https://github.com/hasgeek/funnel i…
vidya-ram Nov 19, 2015
a00963a
Added description meta tag.
vidya-ram Nov 23, 2015
25e3a63
Moved the endif block.
vidya-ram Nov 23, 2015
cc491e3
Moved entire string inside trans block and moved dash inside if block.
vidya-ram Nov 24, 2015
2832b9e
Merge pull request #152 from hasgeek/add-description
jace Nov 26, 2015
2586850
Using url_for for show attendee badge, edit attendee detail and check…
vidya-ram Dec 4, 2015
4812d85
resolved conflict
shreyas-satish Dec 11, 2015
41bbb84
Merge pull request #147 from hasgeek/event_views
shreyas-satish Dec 11, 2015
8d9da0b
fix commit
shreyas-satish Dec 11, 2015
b40bb2c
Merge branch 'event_views'
shreyas-satish Dec 11, 2015
718dac8
use add, commit
shreyas-satish Dec 11, 2015
aba4850
Merge branch 'event_views' into offline_checkin
vidya-ram Dec 11, 2015
dc54240
Fixed merge error.
vidya-ram Dec 11, 2015
6586200
Merge branch 'master' into offline_checkin
vidya-ram Dec 11, 2015
0a6ecdf
fixed indent
shreyas-satish Dec 11, 2015
ccf326b
fixed indent, set UI timer to 10s
shreyas-satish Dec 11, 2015
e2175d1
improved perf
shreyas-satish Dec 11, 2015
286ff2b
using enqueue instead of job
shreyas-satish Dec 11, 2015
9b56975
Merge branch 'master' into offline_checkin
shreyas-satish Dec 11, 2015
6dfb3b3
changed to extract_twitter_handle
shreyas-satish Dec 14, 2015
d0e786d
fixed tests to reflect apis
shreyas-satish Dec 14, 2015
f507fe0
update with master
shreyas-satish Dec 14, 2015
02d2883
added whoami to api
shreyas-satish Dec 14, 2015
62da461
Merge branch 'master' into offline_checkin
shreyas-satish Dec 14, 2015
678fe8f
added id
shreyas-satish Dec 14, 2015
26b35eb
Merge branch 'master' into offline_checkin
shreyas-satish Dec 14, 2015
2f9bc5d
Restructured offline checkin as components.
vidya-ram Dec 15, 2015
e045256
Fixed localstorage check.
vidya-ram Dec 15, 2015
056155b
added ui for adding a parent space
shreyas-satish Dec 15, 2015
d4bd727
sentence case
shreyas-satish Dec 15, 2015
4d171b6
Merge pull request #155 from hasgeek/ui_for_parent_space
shreyas-satish Dec 15, 2015
38687c2
Merge branch 'master' into offline_checkin
shreyas-satish Dec 15, 2015
5fe33ce
fixed badge key render
shreyas-satish Dec 15, 2015
1c2a460
Merge branch 'master' into offline_checkin
shreyas-satish Dec 15, 2015
550f086
make none option explicit
shreyas-satish Dec 15, 2015
e21fb21
Merge branch 'master' into offline_checkin
shreyas-satish Dec 15, 2015
2d3aac3
let parent sections be accessible in subspaces
shreyas-satish Dec 15, 2015
4b8aa17
Merge branch 'master' into offline_checkin
shreyas-satish Dec 15, 2015
620df71
droidconIN badge template.
vidya-ram Dec 15, 2015
8353f14
Merge branch 'master' of https://github.com/hasgeek/funnel
vidya-ram Dec 15, 2015
6d92f1f
Merge branch 'master' into offline_checkin
vidya-ram Dec 15, 2015
e9f656c
Merge branch 'offline_checkin' of https://github.com/hasgeek/funnel i…
vidya-ram Dec 15, 2015
2a62c8c
only show parent spaces on the homepage
shreyas-satish Dec 15, 2015
e7d5420
Merge branch 'master' of github.com:hasgeek/funnel
shreyas-satish Dec 15, 2015
6ae23f3
Merge branch 'master' into offline_checkin
shreyas-satish Dec 15, 2015
409d575
Merge branch 'offline_checkin' of github.com:hasgeek/funnel into offl…
shreyas-satish Dec 15, 2015
83b6fbb
Removed for localstorage feature check and fixed spacing before if.
vidya-ram Dec 24, 2015
b0fad37
Merge branch 'offline_checkin' of https://github.com/hasgeek/funnel i…
vidya-ram Dec 24, 2015
99483b0
Removed localStorage feature check.
vidya-ram Dec 24, 2015
5a71cec
allow subspaces to maintain exclusive sections
shreyas-satish Dec 29, 2015
68f4147
Merge pull request #159 from hasgeek/subspace_sections
shreyas-satish Dec 29, 2015
9302cf8
distinct check-in and cancel check-in queues in local storage.
vidya-ram Jan 4, 2016
ac62cd7
Removed unwanted script tag.
vidya-ram Jan 4, 2016
cf8a405
updated company name
shreyas-satish Jan 22, 2016
7d6f492
fixed year
shreyas-satish Jan 22, 2016
5ce7084
Merge pull request #162 from hasgeek/update_license
shreyas-satish Jan 22, 2016
bfa941c
use query_factory
shreyas-satish Jan 27, 2016
284ba99
customizable labels for objective and description
shreyas-satish Feb 2, 2016
3c525cf
rm server_default. add defensive dict lookups. add set_labels
shreyas-satish Feb 2, 2016
ef07b83
fixed typo
shreyas-satish Feb 2, 2016
3075fe8
use isinstance
shreyas-satish Feb 2, 2016
c4828f4
set server default to {}
shreyas-satish Feb 3, 2016
465e52a
set server default to {}
shreyas-satish Feb 3, 2016
9a4f618
Merge pull request #164 from hasgeek/proposal_labels
shreyas-satish Feb 3, 2016
bca1865
let form fields use objective and description
shreyas-satish Feb 3, 2016
437f1bc
Merge branch 'proposal_labels'
shreyas-satish Feb 3, 2016
771fecd
show label in proposal.html
shreyas-satish Feb 3, 2016
12ff137
fix perms
shreyas-satish Feb 4, 2016
b329003
rm add_and_commit
shreyas-satish Feb 19, 2016
9ea4dae
added exception, fixed case for ungeocoded location
shreyas-satish Feb 29, 2016
9790273
Merge pull request #166 from hasgeek/outstation
jace Feb 29, 2016
d11b953
added support for boxoffice as a ticket client
shreyas-satish Apr 13, 2016
0e11cc0
added boxoffice ext
shreyas-satish Apr 13, 2016
25262cc
fixed syntax, comment and url
shreyas-satish Apr 13, 2016
8e6cf03
fixed name
shreyas-satish Apr 13, 2016
3927540
Add missing dependency
braunmagrin Apr 18, 2016
a0303cd
Merge pull request #167 from braunmagrin/patch-1
jace Apr 19, 2016
d2192ea
cleaned up boxoffice api integration
shreyas-satish Apr 22, 2016
d0c420b
lowercase name
shreyas-satish Apr 22, 2016
2971974
fixing travis settings
shreyas-satish Apr 22, 2016
e0820b6
Merge branch 'master' of github.com:hasgeek/funnel
shreyas-satish Apr 22, 2016
88e6d7a
rm test_requirements
shreyas-satish Apr 22, 2016
36af5dc
psycopg2
shreyas-satish Apr 22, 2016
8e1338e
Merge branch 'master' into boxoffice
shreyas-satish Apr 22, 2016
d26b528
Merge branch 'master' into offline_checkin
vidya-ram Jun 17, 2016
da602c8
Deep Learning badge and sticker template.
vidya-ram Jun 24, 2016
ece48cd
Merge branch 'boxoffice' into offline_checkin
vidya-ram Jun 24, 2016
36958a3
Fix margins in badge template.
vidya-ram Jun 27, 2016
5203ab1
no default hash
shreyas-satish Jul 1, 2016
09c3d81
added badge printed field
shreyas-satish Jul 1, 2016
1f5f066
Merge branch 'offline_checkin' of github.com:hasgeek/funnel into offl…
shreyas-satish Jul 1, 2016
728cf5c
Change asset name to baseframe-footable.
vidya-ram Jul 22, 2016
24509b7
Fifth Elephant badge template.
vidya-ram Jul 23, 2016
5805f3c
Change to baseframe-footable.
vidya-ram Jul 23, 2016
9868cd4
truncate long title
shreyas-satish Jul 24, 2016
dcd0d41
Merge branch 'boxoffice' into offline_checkin
shreyas-satish Jul 24, 2016
e01db48
Template for blank badge.
vidya-ram Jul 24, 2016
ee95b53
Merge branch 'offline_checkin' of https://github.com/hasgeek/funnel i…
vidya-ram Jul 24, 2016
725e2ab
Footable pagination changes.
vidya-ram Jul 24, 2016
f6780bf
Change page size to 100.
vidya-ram Jul 26, 2016
4b05eb8
Remove pagination
vidya-ram Jul 26, 2016
59363e4
Remove footable
vidya-ram Jul 26, 2016
bfa7bb8
CSS changes.
vidya-ram Jul 26, 2016
a5c6770
Change badge type.
vidya-ram Jul 27, 2016
a2c88b3
Checkin API endpoints
karthikb351 Jul 27, 2016
3bd15dd
include PUK in checkin response
karthikb351 Jul 27, 2016
7b525b7
Rename method
karthikb351 Jul 27, 2016
1230bd5
Merge pull request #171 from hasgeek/checkin_api
shreyas-satish Jul 27, 2016
0259f22
Add container for JSON response
karthikb351 Jul 27, 2016
d340d7b
added not found condition
shreyas-satish Jul 27, 2016
45269a5
Update .travis.yml
jace Aug 31, 2016
952d48c
Badge template for JSFoo - Meta Refresh.
vidya-ram Sep 6, 2016
8a025d3
Merge branch 'offline_checkin' of https://github.com/hasgeek/funnel i…
vidya-ram Sep 6, 2016
91081fe
fixed cancellation logic on explara and boxoffice
shreyas-satish Sep 8, 2016
27167d4
Merge branch 'boxoffice' into offline_checkin
shreyas-satish Sep 8, 2016
2f11890
fixed tests
shreyas-satish Sep 8, 2016
6c906c9
fixed tests
shreyas-satish Sep 8, 2016
53220c6
Merge branch 'boxoffice' into offline_checkin
shreyas-satish Sep 8, 2016
1c7a2b3
Change badge template.
vidya-ram Sep 9, 2016
4443030
Fix template.
vidya-ram Sep 14, 2016
c4a8a8c
added more statuses
shreyas-satish Oct 4, 2016
c8457be
Merge pull request #173 from hasgeek/add_status
shreyas-satish Oct 5, 2016
29c7daf
sync with master
shreyas-satish Oct 5, 2016
de8ee66
Add droidcon2016 badge template.
vidya-ram Nov 3, 2016
8249605
Add blank badge template.
vidya-ram Nov 9, 2016
c820e70
remove unused attribute.
vidya-ram Nov 9, 2016
c98a562
add ticket types to participant table
shreyas-satish Nov 9, 2016
43fc8c0
Merge branch 'offline_checkin' of github.com:hasgeek/funnel into offl…
shreyas-satish Nov 9, 2016
a3b1681
fixed reference to id included id in json
shreyas-satish Nov 9, 2016
2420930
Update blank badge template.
vidya-ram Nov 10, 2016
7755003
Merge branch 'offline_checkin' of https://github.com/hasgeek/funnel i…
vidya-ram Nov 10, 2016
b22610f
show participants without tickets
shreyas-satish Nov 10, 2016
cee20d7
use post instead of get when changing data on server
shreyas-satish Nov 16, 2016
f8823df
added newline
shreyas-satish Nov 16, 2016
476a678
added csrf protection
shreyas-satish Nov 16, 2016
3e7a55c
use 403 for invalid form token, return json response for xhr request
shreyas-satish Nov 17, 2016
50bbf3d
Merge pull request #176 from hasgeek/fix_175
shreyas-satish Nov 18, 2016
24a2561
Add missing argument csrf_form.
vidya-ram Nov 18, 2016
a1c6257
50p badge template.
vidya-ram Jan 19, 2017
9380dfd
Merge branch 'offline_checkin' of https://github.com/hasgeek/funnel i…
vidya-ram Jan 19, 2017
5c8997c
sync with master, resolved conflicts
shreyas-satish Jan 19, 2017
5437b51
Merge branch 'offline_checkin' of github.com:hasgeek/funnel into offl…
shreyas-satish Jan 19, 2017
98546ad
Add speaker name to schedule.
vidya-ram Feb 15, 2017
ceeff59
Merge pull request #178 from hasgeek/add-speaker
shreyas-satish Feb 16, 2017
aa49fa4
Changed error message to reflect form field.
harshalbhatia Mar 10, 2017
daf7072
Switched to a generic error message while entering dates in the new p…
harshalbhatia Mar 11, 2017
45c1879
generic error message - this is required
harshalbhatia Mar 11, 2017
67b9d2b
load space with profile scope
shreyas-satish Mar 13, 2017
297a0a0
Merge master.
vidya-ram Mar 28, 2017
5f055e0
Update kilter badge template.
vidya-ram Mar 28, 2017
0c5d35a
Fix merge conflicts.
vidya-ram Mar 28, 2017
e7df1c6
Use DMY dates
jace Apr 17, 2017
d9570af
Use DMY dates
jace Apr 17, 2017
608a72e
Initial version: Check-in status update by scan of QRcode on badge.
vidya-ram Apr 30, 2017
493c028
fix import syntax for latest flask
shreyas-satish May 3, 2017
9c7a289
Merge pull request #187 from hasgeek/flask-migrate
shreyas-satish May 3, 2017
106691e
Remove extra space
jace May 3, 2017
672c050
fix #188
shreyas-satish May 4, 2017
9730da5
rm init_for
shreyas-satish May 4, 2017
3e270bc
Merge pull request #189 from hasgeek/init_app
shreyas-satish May 4, 2017
bafe494
flask-alemic -> flask-migrate
shreyas-satish May 4, 2017
1c44021
Merge pull request #191 from hasgeek/flask-migrate
shreyas-satish May 5, 2017
6418855
Sync with master.
vidya-ram May 5, 2017
7fca1b7
Add rootconf2017 badge template.
vidya-ram May 5, 2017
4eafe8b
Fix merge conflict.
vidya-ram May 5, 2017
b94217b
Sync with master.
vidya-ram May 10, 2017
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 20 additions & 7 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,12 +1,25 @@
language: python
python:
- "2.7"
# command to install dependencies
- 2.7
cache:
directories:
- $HOME/.cache/pip
before_script:
- sudo -- sh -c "echo '127.0.0.1 funnel.travis.dev' >> /etc/hosts"
- psql -c 'create database funnel_testing;' -U postgres
install:
- pip install -r requirements.txt --use-mirrors
- pip install nose coverage
# command to run tests
script: nosetests funnel tests
- pip install -U pip wheel
- pip install -r requirements.txt
script:
- nosetests -v tests
- nohup python runtestserver.py &
- sleep 10
addons:
postgresql: "9.4"
services:
- redis-server
notifications:
email: false
irc: "irc.freenode.net#hasgeek-dev"
slack:
- hasgeek:HDCoMDj3T4ICB59qFFVorCG8
- friendsofhasgeek:3bLViYSzhfaThJovFYCVD3fX
2 changes: 1 addition & 1 deletion LICENSE.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Copyright (c) 2011-13, HasGeek Media LLP
Copyright (c) 2010-16, HasGeek
All rights reserved.

Redistribution and use in source and binary forms, with or without
Expand Down
58 changes: 31 additions & 27 deletions funnel/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@
from __future__ import absolute_import
import coaster.app
from flask import Flask
from flask.ext.mail import Mail
from flask.ext.lastuser import Lastuser
from flask.ext.lastuser.sqlalchemy import UserManager
from flask_migrate import Migrate
from flask_mail import Mail
from flask_lastuser import Lastuser
from flask_lastuser.sqlalchemy import UserManager
from baseframe import baseframe, assets, Version, Bundle
from ._version import __version__

Expand All @@ -26,6 +27,8 @@
assets['schedules.css'][version] = 'css/schedules.css'
assets['schedules.js'][version] = 'js/schedules.js'
assets['screens.css'][version] = 'css/screens.css'
assets['html5-qrcode.js'][version] = 'js/libs/html5-qrcode.js'
assets['jsqrcode-combined.js'][version] = 'js/libs/jsqrcode-combined.js'


# --- Import rest of the app --------------------------------------------------
Expand All @@ -35,27 +38,28 @@


# --- Configuration------------------------------------------------------------

def init_for(env):
coaster.app.init_app(app, env)
db.init_app(app)
db.app = app

mail.init_app(app)
lastuser.init_app(app)
lastuser.init_usermanager(UserManager(db, models.User, models.Team))
baseframe.init_app(app, requires=['funnel'], ext_requires=[
('codemirror-markdown', 'pygments'), 'toastr', 'baseframe-bs3', 'fontawesome>=4.0.0',
'footable'])
app.assets.register('js_fullcalendar',
Bundle(assets.require('!jquery.js', 'jquery.fullcalendar.js', 'spectrum.js'),
output='js/fullcalendar.packed.js', filters='uglipyjs'))
app.assets.register('css_fullcalendar',
Bundle(assets.require('jquery.fullcalendar.css', 'spectrum.css', 'schedules.css'),
output='css/fullcalendar.packed.css', filters='cssmin'))
app.assets.register('js_schedules',
Bundle(assets.require('schedules.js'),
output='js/schedules.packed.js', filters='uglipyjs'))
app.assets.register('css_screens',
Bundle(assets.require('screens.css'),
output='css/screens.packed.css', filters='cssmin'))
coaster.app.init_app(app)
db.init_app(app)
db.app = app
migrate = Migrate(app, db)
mail.init_app(app)
lastuser.init_app(app)
lastuser.init_usermanager(UserManager(db, models.User, models.Team))
baseframe.init_app(app, requires=['funnel'], ext_requires=[
('codemirror-markdown', 'pygments'), 'toastr', 'baseframe-bs3', 'fontawesome>=4.0.0',
'baseframe-footable', 'ractive'])
app.assets.register('js_fullcalendar',
Bundle(assets.require('!jquery.js', 'jquery.fullcalendar.js', 'spectrum.js'),
output='js/fullcalendar.packed.js', filters='uglipyjs'))
app.assets.register('css_fullcalendar',
Bundle(assets.require('jquery.fullcalendar.css', 'spectrum.css', 'schedules.css'),
output='css/fullcalendar.packed.css', filters='cssmin'))
app.assets.register('js_schedules',
Bundle(assets.require('schedules.js'),
output='js/schedules.packed.js', filters='uglipyjs'))
app.assets.register('css_screens',
Bundle(assets.require('screens.css'),
output='css/screens.packed.css', filters='cssmin'))
app.assets.register('js_html5-qrcode',
Bundle(assets.require('html5-qrcode.js', 'jsqrcode-combined.js'),
output='js/html5-qrcode.packed.js', filters='uglipyjs'))
50 changes: 50 additions & 0 deletions funnel/extapi/boxoffice.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# -*- coding: utf-8 -*-

import requests
from ..util import extract_twitter_handle
from .. import app

__all__ = ['Boxoffice']


class Boxoffice(object):
"""
An interface that enables data retrieval from Boxoffice.
"""
def __init__(self, access_token):
self.access_token = access_token
self.base_url = app.config['BOXOFFICE_SERVER']

def url_for(self, endpoint):
return self.base_url.format(endpoint)

def get_orders(self, ic):
resp = requests.get(self.base_url + '/ic/{ic}/orders?access_token={token}'.format(ic=ic, token=self.access_token))
return resp.json().get('orders')

def get_tickets(self, ic):
tickets = []
for order in self.get_orders(ic):
for line_item in order.get('line_items'):
if line_item.get('assignee'):
if line_item.get('line_item_status') == u'confirmed':
status = u'confirmed'
elif line_item.get('line_item_status') == u'cancelled':
status = u'cancelled'
else:
status = unicode(line_item.get('line_item_status'))
tickets.append({
'fullname': line_item.get('assignee').get('fullname', ''),
'email': line_item.get('assignee').get('email'),
'phone': line_item.get('assignee').get('phone', ''),
'twitter': extract_twitter_handle(line_item.get('assignee').get('twitter', '')),
'company': line_item.get('assignee').get('company'),
'city': line_item.get('assignee').get('city', ''),
'job_title': line_item.get('assignee').get('jobtitle', ''),
'ticket_no': unicode(line_item.get('line_item_seq')),
'ticket_type': line_item.get('item', {}).get('title', '')[:80],
'order_no': unicode(order.get('invoice_no')),
'status': status
})

return tickets
36 changes: 21 additions & 15 deletions funnel/extapi/explara.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-

import requests
from ..util import format_twitter_handle
from ..util import extract_twitter_handle

__all__ = ['ExplaraAPI']

Expand Down Expand Up @@ -55,18 +55,24 @@ def get_tickets(self, explara_eventid):
for attendee in order.get('attendee'):
# cancelled tickets are in this list too, hence the check
if attendee.get('status') == 'attending':
# we sometimes get an empty array for details
details = attendee.get('details') or {}
tickets.append({
'fullname': strip_or_empty(attendee.get('name')),
'email': strip_or_empty(attendee.get('email')),
'phone': strip_or_empty(details.get('Phone') or order.get('phoneNo')),
'twitter': format_twitter_handle(strip_or_empty(details.get('Twitter handle'))),
'job_title': strip_or_empty(details.get('Job title')),
'company': strip_or_empty(details.get('Company name')),
'city': strip_or_empty(order.get('city')),
'ticket_no': strip_or_empty(attendee.get('ticketNo')),
'ticket_type': strip_or_empty(attendee.get('ticketName')),
'order_no': strip_or_empty(order.get('orderNo')),
})
status = u'confirmed'
elif attendee.get('status') in [u'cancelled', u'lcancelled']:
status = u'cancelled'
else:
status = unicode(attendee.get('status'))
# we sometimes get an empty array for details
details = attendee.get('details') or {}
tickets.append({
'fullname': strip_or_empty(attendee.get('name')),
'email': strip_or_empty(attendee.get('email')),
'phone': strip_or_empty(details.get('Phone') or order.get('phoneNo')),
'twitter': extract_twitter_handle(strip_or_empty(details.get('Twitter handle'))),
'job_title': strip_or_empty(details.get('Job title')),
'company': strip_or_empty(details.get('Company name')),
'city': strip_or_empty(order.get('city')),
'ticket_no': strip_or_empty(attendee.get('ticketNo')),
'ticket_type': strip_or_empty(attendee.get('ticketName')),
'order_no': strip_or_empty(order.get('orderNo')),
'status': status
})
return tickets
1 change: 1 addition & 0 deletions funnel/forms/participant.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ class ParticipantForm(forms.Form):
company = forms.StringField(__("Company"), validators=[forms.validators.Length(max=80)])
job_title = forms.StringField(__("Job Title"), validators=[forms.validators.Length(max=80)])
twitter = forms.StringField(__("Twitter"), validators=[forms.validators.Length(max=15)])
badge_printed = forms.BooleanField(__("Badge Printed"))
events = QuerySelectMultipleField(__("Events"),
widget=ListWidget(), option_widget=CheckboxInput(),
get_label='title',
Expand Down
13 changes: 13 additions & 0 deletions funnel/forms/proposal.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,19 @@ class ProposalForm(forms.Form):
location = forms.StringField(__("Your location"), validators=[forms.validators.DataRequired(), forms.validators.Length(max=80)],
description=__("Your location, to help plan for your travel if required"))

def __init__(self, *args, **kwargs):
super(ProposalForm, self).__init__(*args, **kwargs)
space = kwargs.get('parent')
if space.proposal_part_a.get('title'):
self.objective.label.text = space.proposal_part_a.get('title')
if space.proposal_part_a.get('hint'):
self.objective.description = space.proposal_part_a.get('hint')
if space.proposal_part_b.get('title'):
self.description.label.text = space.proposal_part_b.get('title')
if space.proposal_part_b.get('hint'):
self.description.description = space.proposal_part_b.get('hint')



class ProposalStatusForm(forms.Form):
status = forms.SelectField(
Expand Down
11 changes: 8 additions & 3 deletions funnel/forms/space.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from .profile import profile_teams
from ..models import RSVP_STATUS

__all__ = ['ProposalSpaceForm', 'RsvpForm', 'EventForm', 'TicketTypeForm', 'TicketClientForm']
__all__ = ['ProposalSpaceForm', 'ProposalSubspaceForm', 'RsvpForm', 'EventForm', 'TicketTypeForm', 'TicketClientForm']


valid_color_re = re.compile("^[a-fA-F\d]{6}|[a-fA-F\d]{3}$")
Expand All @@ -21,9 +21,9 @@ class ProposalSpaceForm(forms.Form):
title = forms.StringField(__("Title"), validators=[forms.validators.DataRequired()])
datelocation = forms.StringField(__("Date and Location"), validators=[forms.validators.DataRequired(), forms.validators.Length(max=50)])
date = forms.DateField(__("Start date (for sorting)"),
validators=[forms.validators.DataRequired(__("Enter a valid date in YYYY-MM-DD format"))])
validators=[forms.validators.DataRequired(__("This is required"))])
date_upto = forms.DateField(__("End date (for sorting)"),
validators=[forms.validators.DataRequired(__("Enter a valid date in YYYY-MM-DD format"))])
validators=[forms.validators.DataRequired(__("This is required"))])
tagline = forms.StringField(__("Tagline"), validators=[forms.validators.DataRequired()],
description=__("This is displayed on the card on the homepage"))
website = forms.URLField(__("Website"),
Expand All @@ -42,6 +42,7 @@ class ProposalSpaceForm(forms.Form):
explore_url = forms.URLField(__("Explore tab URL"),
description=__(u"Page containing the explore tab’s contents, for the mobile app"),
validators=[forms.validators.Optional()])
parent_space = QuerySelectField(__(u"Parent space"), get_label='title', allow_blank=True, blank_text=__(u"None"))

status = forms.SelectField(__("Status"), coerce=int, choices=[
(0, __("Draft")),
Expand Down Expand Up @@ -74,6 +75,10 @@ def validate_bg_color(self, field):
raise forms.ValidationError("Please enter a valid color code")


class ProposalSubspaceForm(ProposalSpaceForm):
inherit_sections = forms.BooleanField(__("Inherit sections from parent space?"), default=True)


class RsvpForm(forms.Form):
status = forms.RadioField("Status", choices=[(k, RSVP_STATUS[k].title) for k in RSVP_STATUS.USER_CHOICES])

Expand Down
19 changes: 10 additions & 9 deletions funnel/jobs/jobs.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
from ..models import (db, TicketClient, SyncTicket)
from ..models import (db, TicketClient)
from ..extapi.explara import ExplaraAPI
from flask.ext.rq import job
from ..extapi.boxoffice import Boxoffice
from funnel import app


@job('funnel')
def import_tickets(ticket_client_id):
with app.test_request_context():
ticket_client = TicketClient.query.get(ticket_client_id)
if ticket_client and ticket_client.name == u'explara':
ticket_list = ExplaraAPI(access_token=ticket_client.client_access_token).get_tickets(ticket_client.client_eventid)
# cancelled tickets are excluded from the list returned by get_tickets
cancel_list = SyncTicket.exclude(ticket_client, [ticket.get('ticket_no') for ticket in ticket_list]).all()
ticket_client.import_from_list(ticket_list, cancel_list=cancel_list)
db.session.commit()
if ticket_client:
if ticket_client.name.lower() == u'explara':
ticket_list = ExplaraAPI(access_token=ticket_client.client_access_token).get_tickets(ticket_client.client_eventid)
ticket_client.import_from_list(ticket_list)
elif ticket_client.name.lower() == u'boxoffice':
ticket_list = Boxoffice(access_token=ticket_client.client_access_token).get_tickets(ticket_client.client_eventid)
ticket_client.import_from_list(ticket_list)
db.session.commit()
39 changes: 21 additions & 18 deletions funnel/models/event.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import os
import base64
from datetime import datetime
from sqlalchemy.sql import text
from . import db, BaseMixin, BaseScopedNameMixin
from .space import ProposalSpace
from .user import User
Expand Down Expand Up @@ -146,9 +147,17 @@ def remove_events(self, events):

@classmethod
def checkin_list(cls, event):
participant_attendee_join = db.join(Participant, Attendee, Participant.id == Attendee.participant_id)
stmt = db.select([Participant.id, Participant.fullname, Participant.email, Participant.company, Participant.twitter, Participant.puk, Participant.key, Attendee.checked_in, Participant.badge_printed]).select_from(participant_attendee_join).where(Attendee.event_id == event.id).order_by(Participant.fullname)
return db.session.execute(stmt).fetchall()
"""
Returns participant details along with their associated ticket types as a comma-separated string.
WARNING: This query uses `string_agg` and hence will only work in PostgreSQL >= 9.0
"""
participant_list = db.session.query('id', 'fullname', 'email', 'company', 'twitter', 'puk', 'key', 'checked_in', 'badge_printed', 'ticket_type_titles').from_statement(text('''
SELECT distinct(participant.id), participant.fullname, participant.email, participant.company, participant.twitter, participant.puk, participant.key, attendee.checked_in, participant.badge_printed, (select string_agg(title, ',') from sync_ticket INNER JOIN ticket_type ON sync_ticket.ticket_type_id = ticket_type.id where sync_ticket.participant_id = participant.id) AS ticket_type_titles
FROM participant INNER JOIN attendee ON participant.id = attendee.participant_id LEFT OUTER JOIN sync_ticket ON participant.id = sync_ticket.participant_id
WHERE attendee.event_id = {event_id}
ORDER BY participant.fullname
'''.format(event_id=event.id))).all()
return participant_list


class Attendee(BaseMixin, db.Model):
Expand Down Expand Up @@ -184,14 +193,11 @@ class TicketClient(BaseMixin, db.Model):
proposal_space = db.relationship(ProposalSpace,
backref=db.backref('ticket_clients', cascade='all, delete-orphan'))

def import_from_list(self, ticket_list, cancel_list=[]):
def import_from_list(self, ticket_list):
"""
Batch upserts the tickets and its associated ticket types and participants.
Cancels the tickets in cancel_list.
"""
for ticket in cancel_list:
ticket.participant.remove_events(ticket.ticket_type.events)

for ticket_dict in ticket_list:
ticket_type = TicketType.upsert(self.proposal_space, current_title=ticket_dict['ticket_type'])

Expand All @@ -200,19 +206,21 @@ def import_from_list(self, ticket_list, cancel_list=[]):
phone=ticket_dict['phone'],
twitter=ticket_dict['twitter'],
company=ticket_dict['company'],
job_title=ticket_dict['job_title'],
city=ticket_dict['city']
)

ticket = SyncTicket.get(self, ticket_dict.get('order_no'), ticket_dict.get('ticket_no'))
if ticket and ticket.participant is not participant:
# Ensure that the previous participant does not have access to
if ticket and (ticket.participant is not participant or ticket_dict.get('status') == u'cancelled'):
# Ensure that the participant of a transferred or cancelled ticket does not have access to
# this ticket's events
ticket.participant.remove_events(ticket_type.events)

ticket = SyncTicket.upsert(self, ticket_dict.get('order_no'), ticket_dict.get('ticket_no'),
participant=participant, ticket_type=ticket_type)
# Ensure that the new or updated participant has access to events
ticket.participant.add_events(ticket_type.events)
if ticket_dict.get('status') == u'confirmed':
ticket = SyncTicket.upsert(self, ticket_dict.get('order_no'), ticket_dict.get('ticket_no'),
participant=participant, ticket_type=ticket_type)
# Ensure that the new or updated participant has access to events
ticket.participant.add_events(ticket_type.events)


class SyncTicket(BaseMixin, db.Model):
Expand Down Expand Up @@ -253,8 +261,3 @@ def upsert(cls, ticket_client, order_no, ticket_no, **fields):
db.session.add(ticket)

return ticket

@classmethod
def exclude(cls, ticket_client, ticket_nos):
return cls.query.filter_by(ticket_client=ticket_client
).filter(~cls.ticket_no.in_(ticket_nos))
2 changes: 1 addition & 1 deletion funnel/models/profile.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-

from flask import url_for
from flask.ext.lastuser.sqlalchemy import ProfileBase
from flask_lastuser.sqlalchemy import ProfileBase
from . import db, MarkdownColumn
from .user import Team

Expand Down
Loading