docstring in Python code
This commit is contained in:
parent
0c65f0c87b
commit
910515b2e7
2 changed files with 119 additions and 19 deletions
77
backend.py
77
backend.py
|
@ -1,6 +1,6 @@
|
||||||
"""Event Man(ager) backend
|
"""Event Man(ager) database backend
|
||||||
|
|
||||||
Classes and functions used to manage events and attendants.
|
Classes and functions used to manage events and attendants database.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import pymongo
|
import pymongo
|
||||||
|
@ -8,15 +8,29 @@ from bson.objectid import ObjectId
|
||||||
|
|
||||||
|
|
||||||
class EventManDB(object):
|
class EventManDB(object):
|
||||||
|
"""MongoDB connector."""
|
||||||
db = None
|
db = None
|
||||||
connection = None
|
connection = None
|
||||||
|
|
||||||
def __init__(self, url=None, dbName='eventman'):
|
def __init__(self, url=None, dbName='eventman'):
|
||||||
|
"""Initialize the instance, connecting to the database.
|
||||||
|
|
||||||
|
:param url: URL of the database
|
||||||
|
:type url: str (or None to connect to localhost)
|
||||||
|
"""
|
||||||
self._url = url
|
self._url = url
|
||||||
self._dbName = dbName
|
self._dbName = dbName
|
||||||
self.connect(url)
|
self.connect(url)
|
||||||
|
|
||||||
def connect(self, url=None, dbName=None):
|
def connect(self, url=None, dbName=None):
|
||||||
|
"""Connect to the database.
|
||||||
|
|
||||||
|
:param url: URL of the database
|
||||||
|
:type url: str (or None to connect to localhost)
|
||||||
|
|
||||||
|
:return: the database we're connected to
|
||||||
|
:rtype: :class:`~pymongo.database.Database`
|
||||||
|
"""
|
||||||
if self.db is not None:
|
if self.db is not None:
|
||||||
return self.db
|
return self.db
|
||||||
if url:
|
if url:
|
||||||
|
@ -28,12 +42,32 @@ class EventManDB(object):
|
||||||
return self.db
|
return self.db
|
||||||
|
|
||||||
def get(self, collection, _id):
|
def get(self, collection, _id):
|
||||||
|
"""Get a single document with the specified `_id`.
|
||||||
|
|
||||||
|
:param collection: search the document in this collection
|
||||||
|
:type collection: str
|
||||||
|
:param _id: unique ID of the document
|
||||||
|
:type _id: str or :class:`~bson.objectid.ObjectId`
|
||||||
|
|
||||||
|
:return: the document with the given `_id`
|
||||||
|
:rtype: dict
|
||||||
|
"""
|
||||||
if not isinstance(_id, ObjectId):
|
if not isinstance(_id, ObjectId):
|
||||||
_id = ObjectId(_id)
|
_id = ObjectId(_id)
|
||||||
results = self.query(collection, {'_id': _id})
|
results = self.query(collection, {'_id': _id})
|
||||||
return results and results[0] or {}
|
return results and results[0] or {}
|
||||||
|
|
||||||
def query(self, collection, query=None):
|
def query(self, collection, query=None):
|
||||||
|
"""Get multiple documents matching a query.
|
||||||
|
|
||||||
|
:param collection: search for documents in this collection
|
||||||
|
:type collection: str
|
||||||
|
:param query: search for documents with those attributes
|
||||||
|
:type query: dict or None
|
||||||
|
|
||||||
|
:return: list of matching documents
|
||||||
|
:rtype: list
|
||||||
|
"""
|
||||||
db = self.connect()
|
db = self.connect()
|
||||||
query = query or {}
|
query = query or {}
|
||||||
if'_id' in query and not isinstance(query['_id'], ObjectId):
|
if'_id' in query and not isinstance(query['_id'], ObjectId):
|
||||||
|
@ -44,11 +78,33 @@ class EventManDB(object):
|
||||||
return results
|
return results
|
||||||
|
|
||||||
def add(self, collection, data):
|
def add(self, collection, data):
|
||||||
|
"""Insert a new document.
|
||||||
|
|
||||||
|
:param collection: insert the document in this collection
|
||||||
|
:type collection: str
|
||||||
|
:param data: the document to store
|
||||||
|
:type data: dict
|
||||||
|
|
||||||
|
:return: the document, as created in the database
|
||||||
|
:rtype: dict
|
||||||
|
"""
|
||||||
db = self.connect()
|
db = self.connect()
|
||||||
_id = db[collection].insert(data)
|
_id = db[collection].insert(data)
|
||||||
return self.get(collection, _id)
|
return self.get(collection, _id)
|
||||||
|
|
||||||
def update(self, collection, _id, data):
|
def update(self, collection, _id, data):
|
||||||
|
"""Update an existing document.
|
||||||
|
|
||||||
|
:param collection: update a document in this collection
|
||||||
|
:type collection: str
|
||||||
|
:param _id: unique ID of the document to be updatd
|
||||||
|
:type _id: str or :class:`~bson.objectid.ObjectId`
|
||||||
|
:param data: the updated information to store
|
||||||
|
:type data: dict
|
||||||
|
|
||||||
|
:return: the document, after the update
|
||||||
|
:rtype: dict
|
||||||
|
"""
|
||||||
db = self.connect()
|
db = self.connect()
|
||||||
data = data or {}
|
data = data or {}
|
||||||
if '_id' in data:
|
if '_id' in data:
|
||||||
|
@ -56,3 +112,20 @@ class EventManDB(object):
|
||||||
db[collection].update({'_id': ObjectId(_id)}, {'$set': data})
|
db[collection].update({'_id': ObjectId(_id)}, {'$set': data})
|
||||||
return self.get(collection, _id)
|
return self.get(collection, _id)
|
||||||
|
|
||||||
|
def delete(self, collection, _id_or_query=None, force=False):
|
||||||
|
"""Remove one or more documents from a collection.
|
||||||
|
|
||||||
|
:param collection: search the documents in this collection
|
||||||
|
:type collection: str
|
||||||
|
:param _id_or_query: unique ID of the document or query to match multiple documents
|
||||||
|
:type _id_or_query: str or :class:`~bson.objectid.ObjectId` or dict
|
||||||
|
:param force: force the deletion of all documents, when `_id_or_query` is empty
|
||||||
|
:type force: bool
|
||||||
|
"""
|
||||||
|
if not _id_or_query and not force:
|
||||||
|
return
|
||||||
|
db = self.connect()
|
||||||
|
if not isinstance(_id_or_query, (ObjectId, dict)):
|
||||||
|
_id_or_query = ObjectId(_id_or_query)
|
||||||
|
db[collection].remove(_id_or_query)
|
||||||
|
|
||||||
|
|
|
@ -18,58 +18,84 @@ from tornado import gen, escape
|
||||||
import backend
|
import backend
|
||||||
|
|
||||||
|
|
||||||
class BaseHandler(tornado.web.RequestHandler):
|
|
||||||
def initialize(self, **kwargs):
|
|
||||||
for key, value in kwargs.iteritems():
|
|
||||||
setattr(self, key, value)
|
|
||||||
|
|
||||||
|
|
||||||
class RootHandler(BaseHandler):
|
|
||||||
angular_app_path = os.path.join(os.path.dirname(__file__), "angular_app")
|
|
||||||
@gen.coroutine
|
|
||||||
def get(self):
|
|
||||||
with open(self.angular_app_path + "/index.html", 'r') as fd:
|
|
||||||
self.write(fd.read())
|
|
||||||
|
|
||||||
|
|
||||||
class ImprovedEncoder(json.JSONEncoder):
|
class ImprovedEncoder(json.JSONEncoder):
|
||||||
|
"""Enhance the default JSON encoder to serialize datetime objects."""
|
||||||
def default(self, o):
|
def default(self, o):
|
||||||
if isinstance(o, datetime.datetime):
|
if isinstance(o, (datetime.datetime, datetime.date,
|
||||||
|
datetime.time, datetime.timedelta)):
|
||||||
return str(o)
|
return str(o)
|
||||||
return json.JSONEncoder.default(self, o)
|
return json.JSONEncoder.default(self, o)
|
||||||
|
|
||||||
json._default_encoder = ImprovedEncoder()
|
json._default_encoder = ImprovedEncoder()
|
||||||
|
|
||||||
|
|
||||||
|
class BaseHandler(tornado.web.RequestHandler):
|
||||||
|
"""Base class for request handlers."""
|
||||||
|
def initialize(self, **kwargs):
|
||||||
|
"""Add every passed (key, value) as attributes of the instance."""
|
||||||
|
for key, value in kwargs.iteritems():
|
||||||
|
setattr(self, key, value)
|
||||||
|
|
||||||
|
|
||||||
|
class RootHandler(BaseHandler):
|
||||||
|
"""Handler for the / path."""
|
||||||
|
angular_app_path = os.path.join(os.path.dirname(__file__), "angular_app")
|
||||||
|
|
||||||
|
@gen.coroutine
|
||||||
|
def get(self):
|
||||||
|
# serve the ./angular_app/index.html file
|
||||||
|
with open(self.angular_app_path + "/index.html", 'r') as fd:
|
||||||
|
self.write(fd.read())
|
||||||
|
|
||||||
|
|
||||||
class CollectionHandler(BaseHandler):
|
class CollectionHandler(BaseHandler):
|
||||||
|
"""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)
|
||||||
collection = None
|
collection = None
|
||||||
|
|
||||||
@gen.coroutine
|
@gen.coroutine
|
||||||
def get(self, id_=None):
|
def get(self, id_=None):
|
||||||
if id_ is not None:
|
if id_ is not None:
|
||||||
|
# read a single document
|
||||||
self.write(self.db.get(self.collection, id_))
|
self.write(self.db.get(self.collection, id_))
|
||||||
else:
|
else:
|
||||||
|
# return an object containing the list of all objects in the collection;
|
||||||
|
# e.g.: {'events': [{'_id': 'obj1-id, ...}, {'_id': 'obj2-id, ...}, ...]}
|
||||||
|
# Please, never return JSON lists that are not encapsulated in an object,
|
||||||
|
# to avoid XSS vulnerabilities.
|
||||||
self.write({self.collection: self.db.query(self.collection)})
|
self.write({self.collection: self.db.query(self.collection)})
|
||||||
|
|
||||||
@gen.coroutine
|
@gen.coroutine
|
||||||
def post(self, id_=None, **kwargs):
|
def post(self, id_=None, **kwargs):
|
||||||
data = escape.json_decode(self.request.body or {})
|
data = escape.json_decode(self.request.body or {})
|
||||||
if id_ is None:
|
if id_ is None:
|
||||||
|
# insert a new document
|
||||||
newData = self.db.add(self.collection, data)
|
newData = self.db.add(self.collection, data)
|
||||||
else:
|
else:
|
||||||
|
# update an existing document
|
||||||
newData = self.db.update(self.collection, id_, data)
|
newData = self.db.update(self.collection, id_, data)
|
||||||
self.write(newData)
|
self.write(newData)
|
||||||
|
|
||||||
|
# PUT is handled by the POST method
|
||||||
put = post
|
put = post
|
||||||
|
|
||||||
|
|
||||||
class PersonsHandler(CollectionHandler):
|
class PersonsHandler(CollectionHandler):
|
||||||
|
"""Handle requests for Persons."""
|
||||||
collection = 'persons'
|
collection = 'persons'
|
||||||
|
|
||||||
|
|
||||||
class EventsHandler(CollectionHandler):
|
class EventsHandler(CollectionHandler):
|
||||||
|
"""Handle requests for Events."""
|
||||||
collection = 'events'
|
collection = 'events'
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def run():
|
||||||
|
"""Run the Tornado web application."""
|
||||||
|
# command line arguments; can also be written in a configuration file,
|
||||||
|
# specified with the --config argument.
|
||||||
define("port", default=5242, help="run on the given port", type=int)
|
define("port", default=5242, help="run on the given port", type=int)
|
||||||
define("data", default=os.path.join(os.path.dirname(__file__), "data"),
|
define("data", default=os.path.join(os.path.dirname(__file__), "data"),
|
||||||
help="specify the directory used to store the data")
|
help="specify the directory used to store the data")
|
||||||
|
@ -82,6 +108,7 @@ def main():
|
||||||
callback=lambda path: tornado.options.parse_config_file(path, final=False))
|
callback=lambda path: tornado.options.parse_config_file(path, final=False))
|
||||||
tornado.options.parse_command_line()
|
tornado.options.parse_command_line()
|
||||||
|
|
||||||
|
# database backend connector
|
||||||
db_connector = backend.EventManDB(url=options.mongodbURL, dbName=options.dbName)
|
db_connector = backend.EventManDB(url=options.mongodbURL, dbName=options.dbName)
|
||||||
init_params = dict(db=db_connector)
|
init_params = dict(db=db_connector)
|
||||||
|
|
||||||
|
@ -100,5 +127,5 @@ def main():
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
run()
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue