Permalink
Browse files

First prototype of a persistent data layer based on MongoDB.

  • Loading branch information...
1 parent ad6d52d commit a071c4c08923d496735cb082acbd5c5bd4db59f6 @kdeldycke committed Jul 25, 2011
Showing with 91 additions and 12 deletions.
  1. +1 −1 README
  2. +89 −11 app.py
  3. +1 −0 buildout.cfg
View
2 README
@@ -44,7 +44,7 @@ Installation
1. Install system dependencies using your favorite package manager. Here is the
example for an Ubuntu machine:
- $ apt-get install python python-httplib2 python-pyopenssl
+ $ apt-get install python python-httplib2 python-pyopenssl mongodb-server
2. Initialize the buildout environment:
View
100 app.py
@@ -2,6 +2,8 @@
import urllib
import urllib2
import json
+import os
+import pymongo
import httplib2
#httplib2.debuglevel = 1
from httplib2 import Http
@@ -11,21 +13,92 @@
class Root():
+ # App ID and secret we got from the Windows Live Dev Center
+ APP_CID = "000000004C05390D"
+ APP_SECRET = "fiMIb91LhBu9T5LPk4hPd2QaqKXLTY4a"
# Our whole application is private: nobody can see anything of it unless he's authorized
_cp_config = {'tools.authorize.on': True}
- # App IDi and secret we got from the Windows Live Dev Center
- APP_CID = "000000004C05390D"
- APP_SECRET = "fiMIb91LhBu9T5LPk4hPd2QaqKXLTY4a"
+ def get_db(self):
+ DB_NAME = 'live_browser'
+ connection = pymongo.Connection("localhost", 27017)
+ if DB_NAME not in connection.database_names():
+ cherrypy.log("There is no %r database in MongoDB." % DB_NAME, 'APP')
+ return connection[DB_NAME]
+
+ db = property(get_db)
- @cherrypy.expose
- def default(self):
- raise cherrypy.HTTPRedirect('/home')
+ def get_db_params_from_query(self, query):
+ """ Transform the API request to parameters which will help us query our DB
+ """
+ # Map the query to our DB schema
+ # Consider queries to be normalized here
+ q_elements = query.split('/')[1:]
+ # XXX Do not try to save for now query we haven't validated yet
+ if len(q_elements) > 1 or q_elements[0] == 'me':
+ cherrypy.log("Query schema had not been validated yet. Skip database persistency.", 'APP')
+ return None
+ # Get the collection
+ collection_name = 'user'
+ # Get the query specification
+ spec = {'id': q_elements[0]}
+ return (collection_name, spec)
+
+
+ def save_to_db(self, query, data):
+ """ Save a request made to the Live API to the local persistent DB
+ """
+ cherrypy.log("Savin to the databaseg %r data associated with the %r query" % (query, data), 'APP')
+ db_params = self.get_db_params_from_query(query)
+ if not db_params:
+ return None
+ (collection_name, spec) = db_params
+ c = self.db[collection_name]
+ doc = c.update(spec, data, upsert=True)
+ return doc
+
+
+ def get_data(self, query):
+ """ This is the entry point for getting data, given a REST query.
+ This method will then get data from the persisten DB layer or directly from the API.
+ """
+ # Normalize queries
+ # TODO: Make this into a method if used elsewhere
+ query = query.strip().lower()
+ if not query.startswith('/'):
+ query = '/%s' % query
+ query = os.path.abspath(query)
+ cherrypy.log('Data requested at: %s' % query, 'APP')
+ # Try to get data from the DB
+ data = self.get_data_from_db(query)
+ # Else, get our data directly from the web service
+ if not data:
+ data = self.get_data_from_ws(query)
+ # Save these data to the DB
+ self.save_to_db(query, data)
+ cherrypy.log('Data returned: %r' % data, 'APP')
+ return data
- def call_ws(self, query='me'):
+
+ def get_data_from_db(self, query):
+ """ This method return data associated with the API query from the persistent DB layer.
+ """
+ # TODO: add an expiration data to the data we saved
+ data = None
+ db_params = self.get_db_params_from_query(query)
+ if db_params:
+ (collection_name, spec) = db_params
+ c = self.db[collection_name]
+ data = c.find_one(spec)
+ return data
+
+
+ def get_data_from_ws(self, query):
+ """ This method perform a REST query to the Windows Live API.
+ """
API_URL = 'https://apis.live.net/v5.0'
params = { 'access_token': cherrypy.session.get('access_token')
}
@@ -51,6 +124,11 @@ def call_ws(self, query='me'):
return data
+ @cherrypy.expose
+ def default(self):
+ raise cherrypy.HTTPRedirect('/home')
+
+
@cherrypy.expose
def callback(self, code):
""" See documentation and reference there:
@@ -116,13 +194,13 @@ def home(self):
@cherrypy.expose
@cherrypy.tools.mako(filename="profile.mako")
- def profile(self, user_id):
+ def profile(self, user_id='me'):
template_var = {}
- me = self.call_ws('/me')
+ me = self.get_data('/me')
template_var.update({'me': me})
if user_id == 'me':
user_id = me.get('id')
- template_var.update({'profile_info': self.call_ws('/%s' % user_id)})
- template_var.update({'contacts': self.call_ws('/%s/contacts' % user_id)})
+ template_var.update({'profile_info': self.get_data('/%s' % user_id)})
+ template_var.update({'contacts': self.get_data('/%s/contacts' % user_id)})
return template_var
View
@@ -10,6 +10,7 @@ parts = live_browser
recipe = zc.recipe.egg
eggs = CherryPy == 3.2.0
Mako == 0.4.1
+ pymongo == 1.11
# Don't get the httplib2 via PyPi as it raise the following error on Debian Squeeze:
# SSLHandshakeError: [Errno 1] _ssl.c:490: error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed
# httplib2

0 comments on commit a071c4c

Please sign in to comment.