2015-03-14 11:12:57 +01:00
|
|
|
#!/usr/bin/env python
|
|
|
|
"""Event Man(ager)
|
|
|
|
|
2015-03-22 11:08:23 +01:00
|
|
|
Your friendly manager of attendees at an event.
|
2015-03-22 09:36:32 +01:00
|
|
|
|
|
|
|
Copyright 2015 Davide Alberani <da@erlug.linux.it>
|
|
|
|
RaspiBO <info@raspibo.org>
|
|
|
|
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
you may not use this file except in compliance with the License.
|
|
|
|
You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
See the License for the specific language governing permissions and
|
|
|
|
limitations under the License.
|
2015-03-14 11:12:57 +01:00
|
|
|
"""
|
|
|
|
|
|
|
|
import os
|
|
|
|
|
|
|
|
import tornado.httpserver
|
|
|
|
import tornado.ioloop
|
|
|
|
import tornado.options
|
|
|
|
from tornado.options import define, options
|
|
|
|
import tornado.web
|
2015-03-21 18:29:19 +01:00
|
|
|
from tornado import gen, escape
|
2015-03-14 11:12:57 +01:00
|
|
|
|
2015-03-30 21:39:12 +02:00
|
|
|
import utils
|
2015-03-21 09:29:01 +01:00
|
|
|
import backend
|
|
|
|
|
2015-03-20 23:08:21 +01:00
|
|
|
|
|
|
|
class BaseHandler(tornado.web.RequestHandler):
|
2015-03-22 08:58:25 +01:00
|
|
|
"""Base class for request handlers."""
|
2015-03-20 23:08:21 +01:00
|
|
|
def initialize(self, **kwargs):
|
2015-03-22 08:58:25 +01:00
|
|
|
"""Add every passed (key, value) as attributes of the instance."""
|
2015-03-20 23:08:21 +01:00
|
|
|
for key, value in kwargs.iteritems():
|
|
|
|
setattr(self, key, value)
|
|
|
|
|
|
|
|
|
|
|
|
class RootHandler(BaseHandler):
|
2015-03-22 08:58:25 +01:00
|
|
|
"""Handler for the / path."""
|
2015-03-15 15:47:04 +01:00
|
|
|
angular_app_path = os.path.join(os.path.dirname(__file__), "angular_app")
|
2015-03-22 08:58:25 +01:00
|
|
|
|
2015-03-14 13:05:04 +01:00
|
|
|
@gen.coroutine
|
2015-03-31 23:35:56 +02:00
|
|
|
def get(self, *args, **kwargs):
|
2015-03-22 08:58:25 +01:00
|
|
|
# serve the ./angular_app/index.html file
|
2015-03-15 15:47:04 +01:00
|
|
|
with open(self.angular_app_path + "/index.html", 'r') as fd:
|
|
|
|
self.write(fd.read())
|
2015-03-14 17:32:45 +01:00
|
|
|
|
2015-03-15 18:00:08 +01:00
|
|
|
|
2015-03-21 20:32:39 +01:00
|
|
|
class CollectionHandler(BaseHandler):
|
2015-03-22 08:58:25 +01:00
|
|
|
"""Base class for handlers that need to interact with the database backend.
|
|
|
|
|
|
|
|
Introduce basic CRUD operations."""
|
|
|
|
# set of documents we're managing (a collection in MongoDB or a table in a SQL database)
|
2015-03-21 20:32:39 +01:00
|
|
|
collection = None
|
2015-03-21 18:29:19 +01:00
|
|
|
|
2015-03-14 17:32:45 +01:00
|
|
|
@gen.coroutine
|
2015-03-31 23:35:56 +02:00
|
|
|
def get(self, id_=None, resource=None, **kwargs):
|
|
|
|
if resource:
|
|
|
|
method = getattr(self, 'handle_%s' % resource, None)
|
|
|
|
if method and callable(method):
|
|
|
|
try:
|
|
|
|
self.write(method(id_, **kwargs))
|
|
|
|
return
|
|
|
|
except:
|
|
|
|
pass
|
2015-03-15 23:05:59 +01:00
|
|
|
if id_ is not None:
|
2015-03-22 08:58:25 +01:00
|
|
|
# read a single document
|
2015-03-21 20:32:39 +01:00
|
|
|
self.write(self.db.get(self.collection, id_))
|
2015-03-21 18:29:19 +01:00
|
|
|
else:
|
2015-03-22 08:58:25 +01:00
|
|
|
# return an object containing the list of all objects in the collection;
|
|
|
|
# e.g.: {'events': [{'_id': 'obj1-id, ...}, {'_id': 'obj2-id, ...}, ...]}
|
2015-03-22 17:08:25 +01:00
|
|
|
# Please, never return JSON lists that are not encapsulated into an object,
|
2015-03-22 08:58:25 +01:00
|
|
|
# to avoid XSS vulnerabilities.
|
2015-03-21 20:32:39 +01:00
|
|
|
self.write({self.collection: self.db.query(self.collection)})
|
2015-03-21 18:29:19 +01:00
|
|
|
|
|
|
|
@gen.coroutine
|
|
|
|
def post(self, id_=None, **kwargs):
|
|
|
|
data = escape.json_decode(self.request.body or {})
|
|
|
|
if id_ is None:
|
2015-03-22 08:58:25 +01:00
|
|
|
# insert a new document
|
2015-03-21 18:29:19 +01:00
|
|
|
newData = self.db.add(self.collection, data)
|
|
|
|
else:
|
2015-03-22 08:58:25 +01:00
|
|
|
# update an existing document
|
2015-03-21 20:32:39 +01:00
|
|
|
newData = self.db.update(self.collection, id_, data)
|
2015-03-21 18:29:19 +01:00
|
|
|
self.write(newData)
|
2015-03-14 11:12:57 +01:00
|
|
|
|
2015-03-22 17:08:25 +01:00
|
|
|
# PUT (update an existing document) is handled by the POST (create a new document) method
|
2015-03-21 20:32:39 +01:00
|
|
|
put = post
|
2015-03-15 18:00:08 +01:00
|
|
|
|
2015-03-22 17:08:25 +01:00
|
|
|
@gen.coroutine
|
|
|
|
def delete(self, id_=None, **kwargs):
|
|
|
|
self.db.delete(self.collection, id_)
|
|
|
|
|
2015-03-22 08:58:25 +01:00
|
|
|
|
2015-03-21 20:32:39 +01:00
|
|
|
class PersonsHandler(CollectionHandler):
|
2015-03-22 08:58:25 +01:00
|
|
|
"""Handle requests for Persons."""
|
2015-03-21 20:32:39 +01:00
|
|
|
collection = 'persons'
|
2015-03-21 15:33:17 +01:00
|
|
|
|
2015-03-31 23:35:56 +02:00
|
|
|
def handle_events(self, _id, **kwds):
|
|
|
|
return {'events': []}
|
|
|
|
|
2015-03-22 08:58:25 +01:00
|
|
|
|
2015-03-21 20:32:39 +01:00
|
|
|
class EventsHandler(CollectionHandler):
|
2015-03-22 08:58:25 +01:00
|
|
|
"""Handle requests for Events."""
|
2015-03-21 20:32:39 +01:00
|
|
|
collection = 'events'
|
2015-03-21 15:33:17 +01:00
|
|
|
|
2015-03-15 18:00:08 +01:00
|
|
|
|
2015-03-31 23:35:56 +02:00
|
|
|
class ActionsHandler(CollectionHandler):
|
|
|
|
"""Handle requests for Actions."""
|
|
|
|
collection = 'actions'
|
|
|
|
|
|
|
|
def get(self, *args, **kwargs):
|
|
|
|
params = self.request.arguments or {}
|
|
|
|
if 'event_id' in params:
|
|
|
|
params['event_id'] = self.db.toID(params['event_id'][0])
|
|
|
|
if 'person_id' in params:
|
|
|
|
params['person_id'] = self.db.toID(params['person_id'][0])
|
|
|
|
data = self.db.query(self.collection, params)
|
|
|
|
self.write({'actions': data})
|
|
|
|
|
|
|
|
|
2015-03-30 21:39:12 +02:00
|
|
|
class EbCSVImportPersonsHandler(BaseHandler):
|
|
|
|
"""Importer for CSV files exported from eventbrite."""
|
2015-03-29 15:05:01 +02:00
|
|
|
csvRemap = {
|
|
|
|
'Nome evento': 'event_title',
|
|
|
|
'ID evento': 'event_id',
|
|
|
|
'N. codice a barre': 'ebqrcode',
|
|
|
|
'Cognome acquirente': 'surname',
|
|
|
|
'Nome acquirente': 'name',
|
|
|
|
'E-mail acquirente': 'email',
|
2015-03-29 21:17:47 +02:00
|
|
|
'Cognome': 'original_surname',
|
|
|
|
'Nome': 'original_name',
|
|
|
|
'E-mail': 'original_email',
|
2015-03-29 15:05:01 +02:00
|
|
|
'Tipologia biglietto': 'ticket_kind',
|
|
|
|
'Data partecipazione': 'attending_datetime',
|
|
|
|
'Data check-in': 'checkin_datetime',
|
|
|
|
'Ordine n.': 'order_nr',
|
|
|
|
}
|
|
|
|
@gen.coroutine
|
|
|
|
def post(self, **kwargs):
|
2015-03-29 15:50:36 +02:00
|
|
|
targetEvent = None
|
|
|
|
try:
|
|
|
|
targetEvent = self.get_body_argument('targetEvent')
|
|
|
|
except:
|
|
|
|
pass
|
2015-03-29 15:05:01 +02:00
|
|
|
reply = dict(total=0, valid=0, merged=0)
|
|
|
|
for fieldname, contents in self.request.files.iteritems():
|
|
|
|
for content in contents:
|
|
|
|
filename = content['filename']
|
2015-03-30 21:31:09 +02:00
|
|
|
parseStats, persons = utils.csvParse(content['body'], remap=self.csvRemap)
|
2015-03-29 15:05:01 +02:00
|
|
|
reply['total'] += parseStats['total']
|
|
|
|
reply['valid'] += parseStats['valid']
|
2015-03-29 23:47:59 +02:00
|
|
|
for person in persons:
|
2015-03-30 22:31:16 +02:00
|
|
|
merged, _id = self.db.merge('persons', person,
|
|
|
|
searchBy=[('email',), ('name', 'surname')])
|
|
|
|
if merged:
|
2015-03-29 23:47:59 +02:00
|
|
|
reply['merged'] += 1
|
2015-03-30 22:31:16 +02:00
|
|
|
if targetEvent and _id:
|
|
|
|
registered_data = {
|
|
|
|
'event_id': self.db.toID(targetEvent),
|
|
|
|
'person_id': self.db.toID(_id),
|
2015-03-31 23:35:56 +02:00
|
|
|
'action': 'registered',
|
2015-03-30 22:31:16 +02:00
|
|
|
'from_file': filename}
|
2015-03-31 23:35:56 +02:00
|
|
|
self.db.insertOne('actions', registered_data)
|
2015-03-29 15:05:01 +02:00
|
|
|
self.write(reply)
|
|
|
|
|
|
|
|
|
2015-03-22 08:58:25 +01:00
|
|
|
def run():
|
|
|
|
"""Run the Tornado web application."""
|
|
|
|
# command line arguments; can also be written in a configuration file,
|
|
|
|
# specified with the --config argument.
|
2015-03-14 11:12:57 +01:00
|
|
|
define("port", default=5242, help="run on the given port", type=int)
|
2015-03-14 17:32:45 +01:00
|
|
|
define("data", default=os.path.join(os.path.dirname(__file__), "data"),
|
|
|
|
help="specify the directory used to store the data")
|
2015-03-21 20:32:39 +01:00
|
|
|
define("mongodbURL", default=None,
|
2015-03-21 09:29:01 +01:00
|
|
|
help="URL to MongoDB server", type=str)
|
2015-03-21 20:32:39 +01:00
|
|
|
define("dbName", default='eventman',
|
|
|
|
help="Name of the MongoDB database to use", type=str)
|
2015-03-14 17:32:45 +01:00
|
|
|
define("debug", default=False, help="run in debug mode")
|
2015-03-14 11:12:57 +01:00
|
|
|
define("config", help="read configuration file",
|
|
|
|
callback=lambda path: tornado.options.parse_config_file(path, final=False))
|
|
|
|
tornado.options.parse_command_line()
|
|
|
|
|
2015-03-22 08:58:25 +01:00
|
|
|
# database backend connector
|
2015-03-21 20:32:39 +01:00
|
|
|
db_connector = backend.EventManDB(url=options.mongodbURL, dbName=options.dbName)
|
2015-03-21 09:29:01 +01:00
|
|
|
init_params = dict(db=db_connector)
|
|
|
|
|
2015-03-14 11:12:57 +01:00
|
|
|
application = tornado.web.Application([
|
2015-03-31 23:35:56 +02:00
|
|
|
(r"/persons/?(?P<id_>\w+)?/?(?P<resource>\w+)?", PersonsHandler, init_params),
|
2015-03-21 20:32:39 +01:00
|
|
|
(r"/events/?(?P<id_>\w+)?", EventsHandler, init_params),
|
2015-03-31 23:35:56 +02:00
|
|
|
(r"/actions/?.*", ActionsHandler, init_params),
|
2015-03-21 09:29:01 +01:00
|
|
|
(r"/(?:index.html)?", RootHandler, init_params),
|
2015-03-29 15:05:01 +02:00
|
|
|
(r"/ebcsvpersons", EbCSVImportPersonsHandler, init_params),
|
2015-03-14 17:32:45 +01:00
|
|
|
(r'/(.*)', tornado.web.StaticFileHandler, {"path": "angular_app"})
|
2015-03-14 11:12:57 +01:00
|
|
|
],
|
2015-03-14 13:05:04 +01:00
|
|
|
template_path=os.path.join(os.path.dirname(__file__), "templates"),
|
2015-03-14 11:12:57 +01:00
|
|
|
static_path=os.path.join(os.path.dirname(__file__), "static"),
|
2015-03-14 13:05:04 +01:00
|
|
|
debug=options.debug)
|
2015-03-14 11:12:57 +01:00
|
|
|
http_server = tornado.httpserver.HTTPServer(application)
|
2015-03-14 11:22:19 +01:00
|
|
|
http_server.listen(options.port)
|
2015-03-14 11:12:57 +01:00
|
|
|
tornado.ioloop.IOLoop.instance().start()
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
2015-03-22 08:58:25 +01:00
|
|
|
run()
|
2015-03-14 11:12:57 +01:00
|
|
|
|