0.2.3-2 OPML import/export plus font config
[feedingit] / src / rss.py
1 #!/usr/bin/env python2.5
2
3
4 # Copyright (c) 2007-2008 INdT.
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Lesser General Public License as published by
7 # the Free Software Foundation, either version 3 of the License, or
8 # (at your option) any later version.
9 #
10 #  This program is distributed in the hope that it will be useful,
11 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
12 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 #  GNU Lesser General Public License for more details.
14 #
15 #  You should have received a copy of the GNU Lesser General Public License
16 #  along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 #
18
19 # ============================================================================
20 # Name        : FeedingIt.py
21 # Author      : Yves Marcoz
22 # Version     : 0.2.0
23 # Description : Simple RSS Reader
24 # ============================================================================
25
26 from os.path import isfile
27 from os.path import isdir
28 from os import remove
29 import pickle
30 import md5
31 import feedparser
32 import time
33
34 CONFIGDIR="/home/user/.feedingit/"
35
36 def getId(string):
37     return md5.new(string).hexdigest()
38
39 class Feed:
40     # Contains all the info about a single feed (articles, ...), and expose the data
41     def __init__(self, name, url):
42         self.entries = []
43         self.fontSize = "+0"
44         self.readItems = {}
45         self.countUnread = 0
46         self.name = name
47         self.url = url
48         self.updateTime = "Never"
49
50     def getFontSize(self):
51         try:
52             return self.fontSize
53         except:
54             self.fontSize = "+0"
55             return self.fontSize
56
57     def saveFeed(self):
58         file = open(CONFIGDIR+getId(self.name), "w")
59         pickle.dump(self, file )
60         file.close()
61
62     def updateFeed(self):
63         tmp=feedparser.parse(self.url)
64         # Check if the parse was succesful (number of entries > 0, else do nothing)
65         if len(tmp["entries"])>0:
66            self.tmpReadItems = self.readItems
67            self.readItems = {}
68            self.updateTime = time.asctime()
69            self.entries = tmp["entries"]
70            self.countUnread = 0
71            # Initialize the new articles to unread
72            for index in range(self.getNumberOfEntries()):
73                if not self.tmpReadItems.has_key(self.getUniqueId(index)):
74                    self.readItems[self.getUniqueId(index)] = False
75                else:
76                    self.readItems[self.getUniqueId(index)] = self.tmpReadItems[self.getUniqueId(index)]
77                if self.readItems[self.getUniqueId(index)]==False:
78                   self.countUnread = self.countUnread + 1
79            del tmp
80            self.saveFeed()
81     
82     def setEntryRead(self, index):
83         if self.readItems[self.getUniqueId(index)]==False:
84             self.countUnread = self.countUnread - 1
85             self.readItems[self.getUniqueId(index)] = True
86     
87     def isEntryRead(self, index):
88         return self.readItems[self.getUniqueId(index)]
89     
90     def getTitle(self, index):
91         return self.entries[index]["title"]
92     
93     def getUniqueId(self,index):
94         entry = self.entries[index]
95         if entry.has_key("updated_parsed"):
96             return getId(time.strftime("%a, %d %b %Y %H:%M:%S",entry["updated_parsed"]) + entry["title"])
97         elif entry.has_key("link"):
98             return getId(entry["link"] + entry["title"])
99         else:
100             return getId(entry["title"])
101     
102     def getUpdateTime(self):
103         return self.updateTime
104     
105     def getEntries(self):
106         try:
107             return self.entries
108         except:
109             return []
110     
111     def getNumberOfUnreadItems(self):
112         return self.countUnread
113     
114     def getNumberOfEntries(self):
115         return len(self.entries)
116     
117     def getItem(self, index):
118         try:
119             return self.entries[index]
120         except:
121             return []
122     
123     def getContent(self, index):
124         entry = self.entries[index]
125         if entry.has_key('content'):
126             content = entry.content[0].value
127         else:
128             content = entry.get('summary', '')
129         return content
130     
131     def getArticle(self, index):
132         self.setEntryRead(index)
133         entry = self.entries[index]
134         title = entry.get('title', 'No title')
135         #content = entry.get('content', entry.get('summary_detail', {}))
136         content = self.getContent(index)
137
138         link = entry.get('link', 'NoLink')
139         if entry.has_key("updated_parsed"):
140             date = time.strftime("%a, %d %b %Y %H:%M:%S",entry["updated_parsed"])
141         elif entry.has_key("published_parsed"):
142             date = time.strftime("%a, %d %b %Y %H:%M:%S", entry["published_parsed"])
143         else:
144             date = ""
145         #text = '''<div style="color: black; background-color: white;">'''
146         text = '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>'
147         text += '<div><a href=\"' + link + '\">' + title + "</a>"
148         text += "<BR /><small><i>Date: " + date + "</i></small></div>"
149         text += "<BR /><BR />"
150         text += content
151         return text    
152
153
154 class Listing:
155     # Lists all the feeds in a dictionary, and expose the data
156     def __init__(self):
157         self.feeds = {}
158         if isfile(CONFIGDIR+"feeds.pickle"):
159             file = open(CONFIGDIR+"feeds.pickle")
160             self.listOfFeeds = pickle.load(file)
161             file.close()
162         else:
163             self.listOfFeeds = {getId("Slashdot"):{"title":"Slashdot", "url":"http://rss.slashdot.org/Slashdot/slashdot"}, }
164         if not self.listOfFeeds.has_key("font"):
165             self.setFont("16")
166         if self.listOfFeeds.has_key("feedingit-order"):
167             self.sortedKeys = self.listOfFeeds["feedingit-order"]
168         else:
169             self.sortedKeys = self.listOfFeeds.keys()
170             if "font" in self.sortedKeys:
171                 self.sortedKeys.remove("font")
172             self.sortedKeys.sort(key=lambda obj: self.getFeedTitle(obj))
173         for key in self.sortedKeys:
174             try:
175                 self.loadFeed(key)
176             except:
177                 self.sortedKeys.remove(key)
178         #self.saveConfig()
179         
180     def getFontSize(self):
181         return self.listOfFeeds["font"]
182     
183     def getReadFont(self):
184         return "sans %s" % self.listOfFeeds["font"]
185     
186     def getUnreadFont(self):
187         return "sans bold %s" % self.listOfFeeds["font"]
188     
189     def setFont(self, fontname):
190         self.listOfFeeds["font"] = fontname
191         
192     def loadFeed(self, key):
193             if isfile(CONFIGDIR+key):
194                 file = open(CONFIGDIR+key)
195                 self.feeds[key] = pickle.load(file)
196                 file.close()
197             else:
198                 title = self.listOfFeeds[key]["title"]
199                 url = self.listOfFeeds[key]["url"]
200                 self.feeds[key] = Feed(title, url)
201         
202     def updateFeeds(self):
203         for key in self.getListOfFeeds():
204             self.feeds[key].updateFeed()
205             
206     def updateFeed(self, key):
207         self.feeds[key].updateFeed()
208             
209     def getFeed(self, key):
210         return self.feeds[key]
211     
212     def getFeedUpdateTime(self, key):
213         return self.feeds[key].getUpdateTime()
214     
215     def getFeedNumberOfUnreadItems(self, key):
216         return self.feeds[key].getNumberOfUnreadItems()
217    
218     def getFeedTitle(self, key):
219         return self.listOfFeeds[key]["title"]
220     
221     def getFeedUrl(self, key):
222         return self.listOfFeeds[key]["url"]
223     
224     def getListOfFeeds(self):
225         return self.sortedKeys
226     
227     def addFeed(self, title, url):
228         if not self.listOfFeeds.has_key(getId(title)):
229             self.listOfFeeds[getId(title)] = {"title":title, "url":url}
230             self.sortedKeys.append(getId(title))
231             self.saveConfig()
232             self.feeds[getId(title)] = Feed(title, url)
233         
234     def removeFeed(self, key):
235         del self.listOfFeeds[key]
236         self.sortedKeys.remove(key)
237         del self.feeds[key]
238         if isfile(CONFIGDIR+key):
239            remove(CONFIGDIR+key)
240     
241     def saveConfig(self):
242         self.listOfFeeds["feedingit-order"] = self.sortedKeys
243         file = open(CONFIGDIR+"feeds.pickle", "w")
244         pickle.dump(self.listOfFeeds, file)
245         file.close()
246         
247     def moveUp(self, key):
248         index = self.sortedKeys.index(key)
249         self.sortedKeys[index] = self.sortedKeys[index-1]
250         self.sortedKeys[index-1] = key
251         
252     def moveDown(self, key):
253         index = self.sortedKeys.index(key)
254         index2 = (index+1)%len(self.sortedKeys)
255         self.sortedKeys[index] = self.sortedKeys[index2]
256         self.sortedKeys[index2] = key