From 8b8df99dce1aa78b107d2e05839475487f11a7f3 Mon Sep 17 00:00:00 2001 From: Davide Alberani Date: Sun, 22 Mar 2015 00:07:05 +0100 Subject: [PATCH 01/10] store new ID into scope --- angular_app/js/controllers.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/angular_app/js/controllers.js b/angular_app/js/controllers.js index 204e481..23cf34a 100644 --- a/angular_app/js/controllers.js +++ b/angular_app/js/controllers.js @@ -28,9 +28,9 @@ eventManControllers.controller('EventDetailsCtrl', ['$scope', 'Event', '$routePa } $scope.save = function() { if ($scope.event.id === undefined) { - Event.save($scope.event); + $scope.event = Event.save($scope.event); } else { - Event.update($scope.event); + $scope.event = Event.update($scope.event); } }; }] @@ -52,9 +52,9 @@ eventManControllers.controller('PersonDetailsCtrl', ['$scope', 'Person', '$route } $scope.save = function() { if ($scope.person.id === undefined) { - Person.save($scope.person); + $scope.person = Person.save($scope.person); } else { - Person.update($scope.person); + $scope.person = Person.update($scope.person); } }; }] From 0c65f0c87b16c64a2f059763189377d7c2a79d92 Mon Sep 17 00:00:00 2001 From: Davide Alberani Date: Sun, 22 Mar 2015 08:57:57 +0100 Subject: [PATCH 02/10] use strict mode --- angular_app/js/app.js | 2 ++ angular_app/js/services.js | 2 ++ 2 files changed, 4 insertions(+) diff --git a/angular_app/js/app.js b/angular_app/js/app.js index 3efc5cc..41063aa 100644 --- a/angular_app/js/app.js +++ b/angular_app/js/app.js @@ -1,3 +1,5 @@ +'use strict'; + var eventManApp = angular.module('eventManApp', [ 'ngRoute', 'eventManServices', diff --git a/angular_app/js/services.js b/angular_app/js/services.js index 6d0b352..334fc69 100644 --- a/angular_app/js/services.js +++ b/angular_app/js/services.js @@ -1,3 +1,5 @@ +'use strict'; + var eventManServices = angular.module('eventManServices', ['ngResource']); eventManServices.factory('Event', ['$resource', From 910515b2e70b77e7ea87ccfa7a35a56188c157fd Mon Sep 17 00:00:00 2001 From: Davide Alberani Date: Sun, 22 Mar 2015 08:58:25 +0100 Subject: [PATCH 03/10] docstring in Python code --- backend.py | 77 ++++++++++++++++++++++++++++++++++++++++++++-- eventman_server.py | 61 ++++++++++++++++++++++++++---------- 2 files changed, 119 insertions(+), 19 deletions(-) diff --git a/backend.py b/backend.py index 54457a8..e6dcf2e 100644 --- a/backend.py +++ b/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 @@ -8,15 +8,29 @@ from bson.objectid import ObjectId class EventManDB(object): + """MongoDB connector.""" db = None connection = None 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._dbName = dbName self.connect(url) 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: return self.db if url: @@ -28,12 +42,32 @@ class EventManDB(object): return self.db 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): _id = ObjectId(_id) results = self.query(collection, {'_id': _id}) return results and results[0] or {} 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() query = query or {} if'_id' in query and not isinstance(query['_id'], ObjectId): @@ -44,11 +78,33 @@ class EventManDB(object): return results 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() _id = db[collection].insert(data) return self.get(collection, _id) 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() data = data or {} if '_id' in data: @@ -56,3 +112,20 @@ class EventManDB(object): db[collection].update({'_id': ObjectId(_id)}, {'$set': data}) 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) + diff --git a/eventman_server.py b/eventman_server.py index 6e656b0..8fd05ea 100755 --- a/eventman_server.py +++ b/eventman_server.py @@ -18,58 +18,84 @@ from tornado import gen, escape 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): + """Enhance the default JSON encoder to serialize datetime objects.""" def default(self, o): - if isinstance(o, datetime.datetime): + if isinstance(o, (datetime.datetime, datetime.date, + datetime.time, datetime.timedelta)): return str(o) return json.JSONEncoder.default(self, o) 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): + """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 @gen.coroutine def get(self, id_=None): if id_ is not None: + # read a single document self.write(self.db.get(self.collection, id_)) 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)}) @gen.coroutine def post(self, id_=None, **kwargs): data = escape.json_decode(self.request.body or {}) if id_ is None: + # insert a new document newData = self.db.add(self.collection, data) else: + # update an existing document newData = self.db.update(self.collection, id_, data) self.write(newData) + # PUT is handled by the POST method put = post + class PersonsHandler(CollectionHandler): + """Handle requests for Persons.""" collection = 'persons' + class EventsHandler(CollectionHandler): + """Handle requests for 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("data", default=os.path.join(os.path.dirname(__file__), "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)) tornado.options.parse_command_line() + # database backend connector db_connector = backend.EventManDB(url=options.mongodbURL, dbName=options.dbName) init_params = dict(db=db_connector) @@ -100,5 +127,5 @@ def main(): if __name__ == '__main__': - main() + run() From 488348df39961a3681d243172a0cc99830a10002 Mon Sep 17 00:00:00 2001 From: Davide Alberani Date: Sun, 22 Mar 2015 09:08:38 +0100 Subject: [PATCH 04/10] document the html --- angular_app/event-detail.html | 1 + angular_app/events-list.html | 41 ++++++++++++++++------------------ angular_app/index.html | 4 +++- angular_app/person-detail.html | 1 + angular_app/persons-list.html | 41 ++++++++++++++++------------------ 5 files changed, 43 insertions(+), 45 deletions(-) diff --git a/angular_app/event-detail.html b/angular_app/event-detail.html index 0f4c913..20b3f34 100644 --- a/angular_app/event-detail.html +++ b/angular_app/event-detail.html @@ -1,3 +1,4 @@ +
diff --git a/angular_app/events-list.html b/angular_app/events-list.html index d88a0ce..968da4c 100644 --- a/angular_app/events-list.html +++ b/angular_app/events-list.html @@ -1,28 +1,25 @@ -
+ +
-
- - Search: - Sort by: - - -
+
+ Search: + Sort by: + +
-
- - - -
+
+ +
-
+
diff --git a/angular_app/index.html b/angular_app/index.html index 3c9f06e..3abe275 100644 --- a/angular_app/index.html +++ b/angular_app/index.html @@ -1,6 +1,7 @@ + Event Man(ager) @@ -10,7 +11,6 @@ - Event Man(ager) @@ -24,6 +24,8 @@
+