Work on GUI integration with new model.
[straw/fork.git] / straw / FeedManager.py
blob0463687450e2bfcf265f1d2df68faf0acc0eec10
1 from JobManager import Job
2 from gobject import GObject, SIGNAL_RUN_FIRST
3 from model import Feed, Item, Category
4 from storage import DAO, Storage
5 import FeedUpdater
6 import JobManager
7 import gobject
8 import straw
10 _storage_path = None
12 model_data = None
14 def import_opml(path):
15 fm = _get_instance()
16 fm.import_opml(path)
18 def init():
19 fm = _get_instance()
20 fm.init_storage(_storage_path)
22 def setup(storage_path = None):
23 global _storage_path
24 _storage_path = storage_path
26 def lookup_feed(id, ctg_id):
27 fm = _get_instance()
28 return fm.lookup_feed(id, ctg_id)
30 def lookup_category(ctg_id):
31 fm = _get_instance()
32 return fm.lookup_category(ctg_id)
34 def get_feed_items(id):
35 fm = _get_instance()
36 return fm.get_feed_items(id)
38 def get_category_items(id):
39 fm = _get_instance()
40 return fm.get_category_items(id)
42 def get_feeds():
43 fm = _get_instance()
44 return fm.get_feeds()
46 def update_all_feeds(observers):
47 fm = _get_instance()
48 return fm.update_all_feeds(observers)
50 def save_category(title, pid = None):
51 fm = _get_instance()
52 return fm.save_category(title, parent_id = pid)
54 def save_feed(feed):
55 fm = _get_instance()
56 result = fm.save_feed(feed)
57 return result
59 def get_model():
60 fm = _get_instance()
61 return fm.categories, fm.model
63 class FeedManager(GObject):
64 __gsignals__ = {
65 "item-added" : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,)),
66 "feed-added" : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,)),
67 "category-added" : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,)),
70 def __init__(self):
71 GObject.__init__(self)
73 self.storage = None
75 def init_storage(self, path):
76 self.storage = Storage(path)
77 self.dao = DAO(self.storage)
79 #self.categories = self.get_categories()
80 self.nodes, self.model = self.dao.get_nodes()
83 #self.feeds = dict([(node.id, node) for node in self.nodes.values() if node.type == "F"])
84 #print self.model
86 self._process_model()
88 def import_opml(self, path):
89 job = Job("opml-parse")
90 job.data = path
91 job.observers = [ { "job-done": [ self.import_opml_save ] } ]
92 JobManager.start_job(job)
94 def import_opml_save(self, handler, opml_data):
95 categories = opml_data
96 #print opml_data
97 self._save_tree(opml_data)
98 #for item in opml_data:
99 # if isinstance(item, Feed):
100 # self.save_feed(item)
101 # else:
102 # self.save_category(item)
104 def _save_tree(self, level, parent_id = 1):
105 for item in level:
106 if isinstance(item, Feed):
107 parent_id = 1
108 if item.parent:
109 parent_id = item.parent.id
110 item.parent_id = parent_id
111 self.save_feed(item)
112 else:
113 parent_id = 1
114 if item.parent:
115 parent_id = item.parent.id
116 item.parent_id = parent_id
117 self.save_category(item)
119 def lookup_feed(self, id):
120 return self.nodes[id]
122 def lookup_category(self, id):
123 return self.categories[id]
125 def save_feed(self, feed):
126 if not feed.parent_id:
127 feed.parent_id = 1
129 category = self.lookup_category(feed.parent_id)
131 if not category:
132 return None
134 result = self.dao.save(feed)
136 if not self.feeds.has_key(feed.parent_id):
137 self.feeds[feed.parent_id] = []
139 feed.connect("unread-count-changed", category.on_unread_count_changed)
141 self.feeds[feed.parent_id].append(feed)
142 self.emit("feed-added", feed)
144 return result
146 def update_all_feeds(self, observers):
147 feeds = [node for key in self.model.keys() for node in self.model[key] if node.type == "F"]
149 FeedUpdater.update_feeds(feeds, [{
150 "job-done": [ self.update_all_feeds_done ],
151 "task-start": [ self._on_update_feed_start ],
152 "task-done": [ self._on_update_feed_done ]
153 }, observers])
155 def update_all_feeds_done(self, handler, data):
156 pass
158 def _on_update_feed_start(self, handler, feed):
159 feed.props.status = straw.FS_UPDATING
161 def _on_update_feed_done(self, handler, data):
162 feed = data.task_info.data["feed"]
163 feed.props.status = straw.FS_IDLE
165 if not data.result:
166 return
168 for item in data.result.items:
169 item.feed_id = feed.id
171 if not self.feed_item_exists(item):
172 self.dao.save(item)
173 feed.props.unread_count += 1
174 self.emit("item-added", item)
176 ### CATEGORIES
178 def save_category(self, category):
179 self.dao.save(category)
180 category.connect("unread-count-changed",
181 self.lookup_category(category.parent_id).on_unread_count_changed)
182 self.categories[category.id] = category
183 self.emit("category-added", category)
185 # ITEMS
187 def get_feed_items(self, id):
188 feed = self.lookup_feed(id)
189 feed.items = self.dao.get(Item, " WHERE feed_id = %d" % feed.id)
190 for item in feed.items:
191 item.feed = feed
192 item.connect("is-read-changed", feed.on_is_read_changed)
193 item.connect("is-read-changed", self.on_item_is_read_changed)
194 return feed.items
196 def get_category_items(self, id):
197 category = self.lookup_category(id)
199 if category == None:
200 return None
202 def _get_children_feeds(category_id):
203 feed_ids = []
205 if self.feeds.has_key(category_id):
206 feed_ids = [str(feed.id) for feed in self.feeds[category_id]]
208 children_categories = [ctg_id for ctg_id in self.categories.keys() if self.categories[ctg_id].parent_id == category_id]
210 for ctg_id in children_categories:
211 feed_ids.extend(_get_children_feeds(ctg_id))
213 return feed_ids
215 in_str = ", ".join(_get_children_feeds(id))
217 items = self.dao.get(Item, " WHERE feed_id IN(%s) ORDER BY id" % in_str)
219 for item in items:
220 feed = self.lookup_feed(item.feed_id)
221 item.feed = feed
222 item.connect("is-read-changed", feed.on_is_read_changed)
223 item.connect("is-read-changed", self.on_item_is_read_changed)
225 return items
227 def feed_item_exists(self, item):
228 result = self.dao.get(Item, " WHERE feed_id = ? AND title = ?", (item.feed_id, item.title,))
229 return len(result) > 0
231 def on_item_is_read_changed(self, obj, is_read):
232 self.dao.save(obj)
234 def _process_model(self):
235 self.categories = dict([(node.id, node) for node in self.nodes.values() if node.type == "C"])
236 self.feeds = {}
238 for category_id in self.categories.keys():
239 category = self.categories[category_id]
241 if category.parent_id != None:
242 category.connect("unread-count-changed",
243 self.categories[category.parent_id].on_unread_count_changed)
245 for node in self.nodes.values():
246 if node.parent_id != None:
247 node.parent = self.nodes[node.parent_id]
249 if node.type == "F":
250 if not self.feeds.has_key(node.parent_id):
251 self.feeds[node.parent_id] = []
253 self.feeds[node.parent_id].append(node)
254 self.categories[node.parent_id].props.unread_count += node.unread_count
256 node.connect("unread-count-changed", node.parent.on_unread_count_changed)
258 _instance = None
260 def _get_instance():
261 global _instance
262 if not _instance:
263 _instance = FeedManager()
264 return _instance