eventman_server.py 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. #!/usr/bin/env python
  2. """Event Man(ager)
  3. Your friendly manager of attendants at a conference.
  4. """
  5. import os
  6. import json
  7. import datetime
  8. import tornado.httpserver
  9. import tornado.ioloop
  10. import tornado.options
  11. from tornado.options import define, options
  12. import tornado.web
  13. from tornado import gen, escape
  14. import backend
  15. class ImprovedEncoder(json.JSONEncoder):
  16. """Enhance the default JSON encoder to serialize datetime objects."""
  17. def default(self, o):
  18. if isinstance(o, (datetime.datetime, datetime.date,
  19. datetime.time, datetime.timedelta)):
  20. return str(o)
  21. return json.JSONEncoder.default(self, o)
  22. json._default_encoder = ImprovedEncoder()
  23. class BaseHandler(tornado.web.RequestHandler):
  24. """Base class for request handlers."""
  25. def initialize(self, **kwargs):
  26. """Add every passed (key, value) as attributes of the instance."""
  27. for key, value in kwargs.iteritems():
  28. setattr(self, key, value)
  29. class RootHandler(BaseHandler):
  30. """Handler for the / path."""
  31. angular_app_path = os.path.join(os.path.dirname(__file__), "angular_app")
  32. @gen.coroutine
  33. def get(self):
  34. # serve the ./angular_app/index.html file
  35. with open(self.angular_app_path + "/index.html", 'r') as fd:
  36. self.write(fd.read())
  37. class CollectionHandler(BaseHandler):
  38. """Base class for handlers that need to interact with the database backend.
  39. Introduce basic CRUD operations."""
  40. # set of documents we're managing (a collection in MongoDB or a table in a SQL database)
  41. collection = None
  42. @gen.coroutine
  43. def get(self, id_=None):
  44. if id_ is not None:
  45. # read a single document
  46. self.write(self.db.get(self.collection, id_))
  47. else:
  48. # return an object containing the list of all objects in the collection;
  49. # e.g.: {'events': [{'_id': 'obj1-id, ...}, {'_id': 'obj2-id, ...}, ...]}
  50. # Please, never return JSON lists that are not encapsulated in an object,
  51. # to avoid XSS vulnerabilities.
  52. self.write({self.collection: self.db.query(self.collection)})
  53. @gen.coroutine
  54. def post(self, id_=None, **kwargs):
  55. data = escape.json_decode(self.request.body or {})
  56. if id_ is None:
  57. # insert a new document
  58. newData = self.db.add(self.collection, data)
  59. else:
  60. # update an existing document
  61. newData = self.db.update(self.collection, id_, data)
  62. self.write(newData)
  63. # PUT is handled by the POST method
  64. put = post
  65. class PersonsHandler(CollectionHandler):
  66. """Handle requests for Persons."""
  67. collection = 'persons'
  68. class EventsHandler(CollectionHandler):
  69. """Handle requests for Events."""
  70. collection = 'events'
  71. def run():
  72. """Run the Tornado web application."""
  73. # command line arguments; can also be written in a configuration file,
  74. # specified with the --config argument.
  75. define("port", default=5242, help="run on the given port", type=int)
  76. define("data", default=os.path.join(os.path.dirname(__file__), "data"),
  77. help="specify the directory used to store the data")
  78. define("mongodbURL", default=None,
  79. help="URL to MongoDB server", type=str)
  80. define("dbName", default='eventman',
  81. help="Name of the MongoDB database to use", type=str)
  82. define("debug", default=False, help="run in debug mode")
  83. define("config", help="read configuration file",
  84. callback=lambda path: tornado.options.parse_config_file(path, final=False))
  85. tornado.options.parse_command_line()
  86. # database backend connector
  87. db_connector = backend.EventManDB(url=options.mongodbURL, dbName=options.dbName)
  88. init_params = dict(db=db_connector)
  89. application = tornado.web.Application([
  90. (r"/persons/?(?P<id_>\w+)?", PersonsHandler, init_params),
  91. (r"/events/?(?P<id_>\w+)?", EventsHandler, init_params),
  92. (r"/(?:index.html)?", RootHandler, init_params),
  93. (r'/(.*)', tornado.web.StaticFileHandler, {"path": "angular_app"})
  94. ],
  95. template_path=os.path.join(os.path.dirname(__file__), "templates"),
  96. static_path=os.path.join(os.path.dirname(__file__), "static"),
  97. debug=options.debug)
  98. http_server = tornado.httpserver.HTTPServer(application)
  99. http_server.listen(options.port)
  100. tornado.ioloop.IOLoop.instance().start()
  101. if __name__ == '__main__':
  102. run()