docstring in Python code

This commit is contained in:
Davide Alberani 2015-03-22 08:58:25 +01:00
parent 0c65f0c87b
commit 910515b2e7
2 changed files with 119 additions and 19 deletions

View file

@ -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)

View file

@ -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()