Flex Reader on Google App Engine

This entry is part of 3 in the series Flex on GAE

2. Python back-end

In my application I store feeds information in the Google App Engine datastore. The App Engine datastore is not a relational database, yet it uses a SQL-like query language called GQL, where data objects called entities replace tables.

2.1 The main Python script handler main.py

Again I used a Google AJAX API and unsurprisingly it requires JSON parsing with simplejson. The ingredients present in “Flex Search on Google App Engine” I discussed last time are also present:

  • a webapp RequestHandler called FeedRequestHandler
  • a navigation page template configuration class by the name of MainPage
  • a main function that runs the WSGIApplication

Figure 3: main.py class diagram

main py class diagram thumb

The UML diagram in Figure 3 is a bit different than the typical Java class diagram. First, the return type was made up, because the return type does not need to be specified in Python. Second, in the Python method signature you have self, which can be seen as a parameter. I left it out of the diagram, because I felt it did not belong there. Finally, there is the main function, which is not part of any of the classes so I left it out as well. But, there are also similarities. In Google App Engine you extend the RequestHandler classes, whereas in Java servlets you extend HTTPServlet. In addition certain methods are linked to GET and POST requests – doPost and doGet for HTTPServlet, post and get for RequestHandler.

Listing 9: The main Python script handler main.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
from google.appengine.ext import webapp
from google.appengine.ext.webapp.util import run_wsgi_app
import os
from google.appengine.ext.webapp import template
import urllib
from django.utils import simplejson
from google.appengine.ext import db
from net.ivanidris.flexreader import Feed
from google.appengine.api import urlfetch
import logging
import cgi
 
class FeedRequestHandler(webapp.RequestHandler):
  def post(self):
      self.printOutput()
 
  def encode(self, s):
       return urllib.quote_plus(s)
 
  def getQuery(self):
    feeds = db.GqlQuery("SELECT * from Feed")
    form = cgi.FieldStorage()
    feedLinks = simplejson.loads(urllib.unquote_plus(form.getfirst("feedLinks")))
 
    for feed in feeds:
        query = feed.link
 
        if query not in feedLinks:
            return query
 
    if len(feedLinks) > 0:
       return feedLinks[0] 
    else:
        return ''
 
  def printOutput(self):
    resultList = []
    query = urllib.quote_plus(urllib.unquote(self.getQuery()))
 
    if query != '':
        url = "http://ajax.googleapis.com/ajax/services/feed/load?v=1.0&num=100&q=" + query
        result = urlfetch.fetch(url)
 
    if query != '' and result.status_code == 200:
        json = simplejson.loads(result.content.strip())
 
        if json['responseStatus'] == 200:
            entriesList = []
 
            for searchResult in json['responseData']['feed']['entries']:
                filteredDict = dict(title = searchResult['title'],
                    url = searchResult['link'], content = searchResult['content'])
                entriesList.append(filteredDict)
 
            resultList.append(dict(feedLink = query))
            resultList.append(dict(entries = entriesList))
            logging.debug('resultList ' + str(resultList))
 
    jsonString = simplejson.dumps( resultList )
    self.response.out.write( jsonString.strip())
 
class MainPage(webapp.RequestHandler):
  def get(self):
    template_values = {
      'reader_url': 'static/reader/FlexReader.html',
      'reader_url_linktext': 'Reader',
      }
 
    path = os.path.join(os.path.dirname(__file__), 'nav.html')
    self.response.out.write(template.render(path, template_values))
 
application = webapp.WSGIApplication(
                                     [('/', MainPage),
                                      ('/feed', FeedRequestHandler)],
                                     debug=True)
 
def main():
  run_wsgi_app(application)
 
if __name__ == "__main__":
  main()

Recall from Listing 3 that a POST request was sent with URL /feed and parameter feedLinks. As you can see this URL is mapped in main.py to FeedRequestHandler. FeedRequestHandler has a post method, which processes HTTP POST requests. The post method resembles, as I already mentioned, the doPost method in Java servlets. Similarly, MainPage has a get method for GET requests. The JSON response format of feeds loading is different from that of web search. This makes reusing the JSON parsing part of my Python code difficult. Fortunately, the parsing code only consists of a few lines. The simplejson module from Django is again used for parsing. The simplejson loads and dumps method execute the JSON decoding and encoding. FeedRequestHandler sends back a filtered summary of the search result in the JSON format, containing the titles and urls of the found feeds.

1
2
3
4
5
6
7
8
9
10
11
{"responseData": 
   {"feed":
      {"title":"Ivan Idris Blog",
      "link":"http://ivanidris.net/wordpress",
      "author":"",
      "description":"Just another WordPress weblog",
      "type":"rss20",
      "entries":
         [{"title":"Flex Search on Google App Engine",
         "link":"http://feedproxy.google.com/~r/IvanIdrisBlog/~3/4qQGogdYzeQ/flex-search-on-google-app-engine",
      ...

JSON format of the feed load response

Except for the different JSON format, there is one major difference, the datastore. The only method that interacts with the Google App Engine datastore is getQuery. It takes the value of the feedLinks parameter sent in the request. getQuery checks whether the feed we are querying for is already in the datastore.

Series Navigation
0saves
If you enjoyed this post, please consider leaving a comment or subscribing to the RSS feed to have future articles delivered to your feed reader.
Share
This entry was posted in programming and tagged , . Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="" highlight="">