Flex Reader on Google App Engine

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

2.4 Discovering or finding feeds

The “Add Feed” feature requires an exact URL of a feed. Discovering feeds or “Find Feed” is more flexible – all the user needs to give it is something to search for. This freedom, on the other hand, makes it harder to program.

Figure 5: Discovering feeds dialog and class diagram.

Finding Feeds Find feed class diagram

Listing 13: Discovering feeds dialog – FindFeed.mxml

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
<?xml version="1.0" encoding="utf-8"?>
<iic:ClosableTitleWindow xmlns:mx="http://www.adobe.com/2006/mxml" 
   xmlns:iic="net.ivanidris.flex.components.*" creationComplete="init();">
    <mx:Script source="Find.as"/>
    <mx:VBox>
      <mx:Grid>
         <mx:GridRow>
              <mx:GridItem>
                  <mx:Label text="Find feeds based on keywords"/>
              </mx:GridItem>
         </mx:GridRow>
           <mx:GridRow>
              <mx:GridItem>
                  <mx:TextInput id="keywords"/> 
              </mx:GridItem>
              <mx:GridItem>
                  <mx:Button id="find" label="Find" click="onFind();"/>  
              </mx:GridItem>
           </mx:GridRow>   
           <mx:GridRow>
              <mx:GridItem>
                  <mx:Label text="For instance cnn or news"/>
              </mx:GridItem>
           </mx:GridRow>
       </mx:Grid>
 
        <mx:DataGrid dataProvider="{feeds}" width="782">
            <mx:columns>
                <mx:DataGridColumn headerText="Add" dataField="add" width="50" textAlign="center">
                    <mx:itemRenderer>
                        <mx:Component>
                            <mx:CheckBox click="data.add=!data.add;"  selected="{data.add}"/>            
                        </mx:Component>                        
                    </mx:itemRenderer>
                </mx:DataGridColumn>
                <mx:DataGridColumn headerText="URL" dataField="url"/>
                <mx:DataGridColumn headerText="Title" dataField="title"/>
            </mx:columns>            
        </mx:DataGrid>
    </mx:VBox>
    <iic:PushButton x="10" y="256" label="Add Selected Feeds" id="addSelected" enabled="false" click="onAddSelected();"/>
</iic:ClosableTitleWindow>

You probably have noticed that this file does not contain any Actionscript code. That is because it is in Find.as.

Listing 14: Discovering feeds – Find.as

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
82
83
84
85
86
87
88
89
90
91
92
    import com.adobe.serialization.json.JSON;
 
    import mx.collections.ArrayCollection;
    import mx.controls.Alert;
    import mx.managers.PopUpManager;
    import mx.rpc.events.FaultEvent;
    import mx.rpc.events.ResultEvent;
    import mx.rpc.http.HTTPService;
 
    import net.ivanidris.flexreader.FeedInfo;
 
    [Bindable]
    private var feeds: ArrayCollection;
 
    private var loader:URLLoader;
 
    private function init():void {
        keywords.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
    }
 
    private function onFind():void {
         var url:String = '/findFeed.py?q=' + keywords.text;
         var request:URLRequest = new URLRequest(url);
         loader = new URLLoader(request);
         loader.addEventListener("complete", onServerResponse);
    }
 
    private function onServerResponse(e:Event):void {
        var feed: FeedInfo;
        feeds = new ArrayCollection();
 
        var json:Object = JSON.decode(loader.data);
 
        for each(var i in json) {
            feed = new FeedInfo();
            feed.url = i.url;
            feed.title = i.title;
            feeds.addItem(feed);
        }
 
        if(feeds.length > 0) {
         addSelected.enabled = true;
        }
    }
 
    override public function onKeyDown(event:KeyboardEvent) :void {
        // process enters
        if(event.charCode == 13 && keywords.text.length > 0) {
            onFind();
        }
    }
 
 
    public function useHttpService(parameters:Object):void { 
        var service:HTTPService;
        service = new HTTPService(); 
        service.url = "/addSelectedFeeds.py"; 
        service.method = "POST"; 
        service.addEventListener("result", httpResult); 
        service.addEventListener("fault", httpFault); 
        service.send(parameters); 
    } 
 
    public function httpResult(event:ResultEvent):void { 
      removeMe();
    } 
 
    public function httpFault(event:FaultEvent):void { 
        var faultstring:String = event.fault.faultString; 
        Alert.show(faultstring); 
    } 
 
    public function onAddSelected():void {
      var len:int = feeds.length;
      var result:Array = new Array();
 
        for (var i:int=0;i<len;i++)
        {
            var feed:FeedInfo = FeedInfo(feeds.getItemAt(i));
 
            if (feed.add)
            {
               result.push(feed);
            }
        }
 
        if(result.length > 0) {
           var parameters:Object = new Object();
           parameters.data = JSON.encode(result);
           useHttpService(parameters);
        }
    }

Find.as either sends a request to findFeed.py or addSelectedFeeds.py. In the discover feeds scenario, you are more likely to find multiple items for query such as “cnn” or ” hamburger” or whatever. The FeedInfo class stores information about the found feeds. FeedInfo is small class with three fields – a url, a title and a boolean telling us whether to add this feed or not.

Listing 15: Discovering feeds – findFeed.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
import cgi
import urllib
from google.appengine.api import urlfetch
from django.utils import simplejson
import re
 
form = cgi.FieldStorage()
query = urllib.quote(form.getfirst("q", ""))
 
url = "http://ajax.googleapis.com/ajax/services/feed/find?v=1.0&q=" + query
result = urlfetch.fetch(url)
resultList = []
 
print 'Content-type: text/plain'
print ''
 
if result.status_code == 200:
    json = simplejson.loads(result.content.strip())
 
    if json['responseStatus'] == 200:
        for entry in json['responseData']['entries']:
            text = re.sub('<.*?>', '', entry['title'])
            filteredDict = dict(title = text,
                url = entry['url'])
            resultList.append(filteredDict)
 
jsonString = simplejson.dumps( resultList )
print jsonString.strip()

findFeed.py in Listing 15 finds the feeds and returns feed info in JSON format. The script removes tags in the feed titles. The reason is that Flex doesn’t render the tags properly anyway.

Listing 16: Adding selected feeds – addSelectedFeeds.py

1
2
3
4
5
6
7
8
9
10
11
12
import cgi
from net.ivanidris.flexreader import Feed
from django.utils import simplejson
 
form = cgi.FieldStorage()
data = form.getfirst("data", "")
 
json = simplejson.loads(data)
 
for i in json:
    feed = Feed.Feed(key_name = i['url'], link = i['url'], title = i['title'])
    feed.put()

In the Find Feed dialog the user can select, the feeds to add. addSelectedFeeds.py of Listing 16 as suggested by its name, adds selected feeds to 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="">