1 #!/usr/bin/env python2.5
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.
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.
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/>.
19 # ============================================================================
21 # Author : Yves Marcoz
23 # Description : Simple RSS Reader
24 # ============================================================================
26 from os.path import isfile
27 from os.path import isdir
35 #CONFIGDIR="/home/user/.feedingit/"
38 return md5.new(string).hexdigest()
41 # Contains all the info about a single feed (articles, ...), and expose the data
42 def __init__(self, name, url):
48 self.updateTime = "Never"
50 def saveFeed(self, configdir):
51 file = open(configdir+getId(self.name), "w")
52 pickle.dump(self, file )
55 def updateFeed(self, configdir, expiryTime=24):
56 # Expiry time is in hours
57 tmp=feedparser.parse(self.url)
58 # Check if the parse was succesful (number of entries > 0, else do nothing)
59 if len(tmp["entries"])>0:
60 #reversedEntries = self.getEntries()
61 #reversedEntries.reverse()
63 for entry in tmp["entries"]:
64 tmpIds.append(self.getUniqueId(-1, entry))
65 for entry in self.getEntries():
66 currentTime = time.time()
67 expiry = float(expiryTime) * 3600.
68 if entry.has_key("updated_parsed"):
69 articleTime = time.mktime(entry["updated_parsed"])
70 if currentTime - articleTime < expiry:
71 id = self.getUniqueId(-1, entry)
73 tmp["entries"].append(entry)
75 self.entries = tmp["entries"]
77 # Initialize the new articles to unread
78 tmpReadItems = self.readItems
80 for index in range(self.getNumberOfEntries()):
81 if not tmpReadItems.has_key(self.getUniqueId(index)):
82 self.readItems[self.getUniqueId(index)] = False
84 self.readItems[self.getUniqueId(index)] = tmpReadItems[self.getUniqueId(index)]
85 if self.readItems[self.getUniqueId(index)]==False:
86 self.countUnread = self.countUnread + 1
88 self.updateTime = time.asctime()
89 self.saveFeed(configdir)
91 def setEntryRead(self, index):
92 if self.readItems[self.getUniqueId(index)]==False:
93 self.countUnread = self.countUnread - 1
94 self.readItems[self.getUniqueId(index)] = True
96 def isEntryRead(self, index):
97 return self.readItems[self.getUniqueId(index)]
99 def getTitle(self, index):
100 return self.entries[index]["title"]
102 def getLink(self, index):
103 return self.entries[index]["link"]
105 def getDate(self, index):
107 return self.entries[index]["updated_parsed"]
109 return time.localtime()
111 def getUniqueId(self, index, entry=None):
113 entry = self.entries[index]
114 if entry.has_key("updated_parsed"):
115 return getId(time.strftime("%a, %d %b %Y %H:%M:%S",entry["updated_parsed"]) + entry["title"])
116 elif entry.has_key("link"):
117 return getId(entry["link"] + entry["title"])
119 return getId(entry["title"])
121 def getUpdateTime(self):
122 return self.updateTime
124 def getEntries(self):
130 def getNumberOfUnreadItems(self):
131 return self.countUnread
133 def getNumberOfEntries(self):
134 return len(self.entries)
136 def getItem(self, index):
138 return self.entries[index]
142 def getContent(self, index):
143 entry = self.entries[index]
144 if entry.has_key('content'):
145 content = entry.content[0].value
147 content = entry.get('summary', '')
150 def getArticle(self, index):
151 self.setEntryRead(index)
152 entry = self.entries[index]
153 title = entry.get('title', 'No title')
154 #content = entry.get('content', entry.get('summary_detail', {}))
155 content = self.getContent(index)
157 link = entry.get('link', 'NoLink')
158 if entry.has_key("updated_parsed"):
159 date = time.strftime("%a, %d %b %Y %H:%M:%S",entry["updated_parsed"])
160 elif entry.has_key("published_parsed"):
161 date = time.strftime("%a, %d %b %Y %H:%M:%S", entry["published_parsed"])
164 #text = '''<div style="color: black; background-color: white;">'''
165 text = '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>'
166 text += '<div><a href=\"' + link + '\">' + title + "</a>"
167 text += "<BR /><small><i>Date: " + date + "</i></small></div>"
168 text += "<BR /><BR />"
172 class ArchivedArticles(Feed):
173 def addArchivedArticle(self, title, link, updated_parsed, configdir):
175 entry["title"] = title
177 entry["downloaded"] = False
178 entry["summary"] = '<a href=\"' + link + '\">' + title + "</a>"
179 entry["updated_parsed"] = updated_parsed
180 self.entries.append(entry)
181 self.readItems[self.getUniqueId(len(self.entries)-1)] = False
182 self.countUnread = self.countUnread + 1
183 self.saveFeed(configdir)
186 def updateFeed(self, configdir, expiryTime=24):
187 for entry in self.getEntries():
188 if not entry["downloaded"]:
190 f = urllib2.urlopen(entry["link"])
191 entry["summary"] = f.read()
193 entry["downloaded"] = True
194 entry["time"] = time.time()
197 currentTime = time.time()
198 expiry = float(expiryTime) * 3600
199 if currentTime - entry["time"] > expiry:
200 self.entries.remove(entry)
201 self.saveFeed(configdir)
203 def getArticle(self, index):
204 self.setEntryRead(index)
205 content = self.getContent(index)
210 # Lists all the feeds in a dictionary, and expose the data
211 def __init__(self, configdir):
212 self.configdir = configdir
214 if isfile(self.configdir+"feeds.pickle"):
215 file = open(self.configdir+"feeds.pickle")
216 self.listOfFeeds = pickle.load(file)
219 self.listOfFeeds = {getId("Slashdot"):{"title":"Slashdot", "url":"http://rss.slashdot.org/Slashdot/slashdot"}, }
220 if self.listOfFeeds.has_key("font"):
221 del self.listOfFeeds["font"]
222 if self.listOfFeeds.has_key("feedingit-order"):
223 self.sortedKeys = self.listOfFeeds["feedingit-order"]
225 self.sortedKeys = self.listOfFeeds.keys()
226 if "font" in self.sortedKeys:
227 self.sortedKeys.remove("font")
228 self.sortedKeys.sort(key=lambda obj: self.getFeedTitle(obj))
229 for key in self.sortedKeys:
233 self.sortedKeys.remove(key)
236 def addArchivedArticle(self, key, index):
237 title = self.getFeed(key).getTitle(index)
238 link = self.getFeed(key).getLink(index)
239 date = self.getFeed(key).getDate(index)
240 if not self.listOfFeeds.has_key(getId("Archived Articles")):
241 self.listOfFeeds[getId("Archived Articles")] = {"title":"Archived Articles", "url":""}
242 self.sortedKeys.append(getId("Archived Articles"))
243 self.feeds[getId("Archived Articles")] = ArchivedArticles("Archived Articles", "")
246 self.getFeed(getId("Archived Articles")).addArchivedArticle(title, link, date, self.configdir)
248 def loadFeed(self, key):
249 if isfile(self.configdir+key):
250 file = open(self.configdir+key)
251 self.feeds[key] = pickle.load(file)
254 title = self.listOfFeeds[key]["title"]
255 url = self.listOfFeeds[key]["url"]
256 self.feeds[key] = Feed(title, url)
258 def updateFeeds(self, expiryTime=24):
259 for key in self.getListOfFeeds():
260 self.feeds[key].updateFeed(self.configdir, expiryTime)
262 def updateFeed(self, key, expiryTime=24):
263 self.feeds[key].updateFeed(self.configdir, expiryTime)
265 def getFeed(self, key):
266 return self.feeds[key]
268 def getFeedUpdateTime(self, key):
269 return self.feeds[key].getUpdateTime()
271 def getFeedNumberOfUnreadItems(self, key):
272 return self.feeds[key].getNumberOfUnreadItems()
274 def getFeedTitle(self, key):
275 return self.listOfFeeds[key]["title"]
277 def getFeedUrl(self, key):
278 return self.listOfFeeds[key]["url"]
280 def getListOfFeeds(self):
281 return self.sortedKeys
283 def addFeed(self, title, url):
284 if not self.listOfFeeds.has_key(getId(title)):
285 self.listOfFeeds[getId(title)] = {"title":title, "url":url}
286 self.sortedKeys.append(getId(title))
288 self.feeds[getId(title)] = Feed(title, url)
290 def removeFeed(self, key):
291 del self.listOfFeeds[key]
292 self.sortedKeys.remove(key)
294 if isfile(self.configdir+key):
295 remove(self.configdir+key)
297 def saveConfig(self):
298 self.listOfFeeds["feedingit-order"] = self.sortedKeys
299 file = open(self.configdir+"feeds.pickle", "w")
300 pickle.dump(self.listOfFeeds, file)
303 def moveUp(self, key):
304 index = self.sortedKeys.index(key)
305 self.sortedKeys[index] = self.sortedKeys[index-1]
306 self.sortedKeys[index-1] = key
308 def moveDown(self, key):
309 index = self.sortedKeys.index(key)
310 index2 = (index+1)%len(self.sortedKeys)
311 self.sortedKeys[index] = self.sortedKeys[index2]
312 self.sortedKeys[index2] = key