Oops, forgot to commit ItemManager :)
[straw.git] / straw / FeedManager.py
blob3f9feaa246c4c7aec7836652a91eed15c31695e0
1 from JobManager import Job
2 from error import debug
3 from gobject import GObject, SIGNAL_RUN_FIRST
4 from model import Feed, Item, Category
5 from storage import DAO, Storage
6 import FeedUpdater
7 import ItemManager
8 import JobManager
9 import gobject
10 import opml
11 import straw
13 _storage_path = None
15 def init():
16 fm = _get_instance()
17 fm.init_storage(_storage_path)
19 def setup(storage_path = None):
20 global _storage_path
21 _storage_path = storage_path
23 def import_opml(path, category):
24 fm = _get_instance()
25 fm.import_opml(path, category)
27 def export_opml(root_id, filename):
28 fm = _get_instance()
29 fm.export_opml(root_id, filename)
31 def lookup_feed(id):
32 fm = _get_instance()
33 return fm.lookup_feed(id)
35 def lookup_category(ctg_id):
36 fm = _get_instance()
37 return fm.lookup_category(ctg_id)
39 def get_feeds():
40 fm = _get_instance()
41 return fm.get_feeds()
43 def update_all_feeds(observers):
44 fm = _get_instance()
45 return fm.update_all_feeds(observers)
47 def is_update_all_running():
48 fm = _get_instance()
49 return fm.update_all_running
51 def update_nodes(nodes, observers = {}):
52 fm = _get_instance()
53 return fm.update_nodes(nodes, observers)
55 def save_category(category):
56 fm = _get_instance()
57 return fm.save_category(category)
59 def save_feed(feed):
60 fm = _get_instance()
61 result = fm.save_feed(feed)
62 return result
64 def save_all(save_list):
65 fm = _get_instance()
66 fm.save_all(save_list)
68 def get_model():
69 fm = _get_instance()
70 return fm.nodes
72 def get_children_feeds(node_id):
73 node = lookup_category(node_id)
75 if not node:
76 return None
78 return [_node for _node in node.all_children() if _node.type == "F"]
80 def categories(root_id = 1):
81 nodes = get_model()
83 if not nodes.has_key(root_id):
84 return
86 yield nodes[root_id]
88 for node in nodes[root_id].children:
89 if node.type == "C":
90 for child in categories(node.id):
91 yield child
93 def move_node(node, target_path):
94 fm = _get_instance()
95 fm.move_node(node, target_path)
97 def delete_nodes(node_ids):
98 fm = _get_instance()
99 fm.delete_nodes(node_ids)
101 class FeedManager(GObject):
102 __gsignals__ = {
103 "item-added" : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,)),
104 "feed-added" : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,)),
105 "category-added" : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,)),
106 "update-all-done" : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, ())
109 def __init__(self):
110 GObject.__init__(self)
112 self.storage = None
113 self.update_all_running = False
115 def init_storage(self, path):
116 self.storage = Storage(path)
117 self.dao = DAO(self.storage)
118 self.nodes = self.dao.get_nodes()
120 self._prepare_model()
122 def _prepare_model(self):
123 for node in self.nodes.values():
124 self._setup_node_signals(node)
126 if node.parent_id != None:
127 node.parent = self.nodes[node.parent_id]
128 node.parent.append_child(node)
129 else:
130 node.parent = None
132 def _setup_node_signals(self, node):
133 node.connect("parent-changed", self.on_parent_changed)
134 node.connect("norder-changed", self.on_norder_changed)
136 def import_opml(self, path, category):
137 job = Job("opml-parse")
138 job.data = (path, category)
139 job.observers = [ { "job-done": [ self.import_opml_save ] } ]
141 JobManager.start_job(job)
143 def import_opml_save(self, handler, opml_data):
144 save_list, category = opml_data
146 self.dao.tx_begin()
147 self.save_all(save_list)
148 self.dao.tx_commit()
150 def export_opml(self, root_id, filename):
151 if not self.nodes.has_key(root_id):
152 return None
154 opml.export(self.nodes[root_id], filename)
156 def _model_add_node(self, node):
157 self.nodes[node.parent_id].add_child(node)
159 def move_node(self, node, target_path):
160 debug("fm:move_node %d to %s" % (node.id, str(target_path)))
161 new_parent = self.nodes[1].get_by_path(target_path[:-1])
163 if not new_parent:
164 new_parent = self.nodes[1]
166 node.move(new_parent, target_path[-1:][0])
168 def delete_nodes(self, node_ids):
169 self.dao.tx_begin()
171 for id in node_ids:
172 self.delete_node(id)
174 self.dao.tx_commit()
176 def delete_node(self, id):
177 if not self.nodes.has_key(id):
178 return
180 node = self.nodes[id]
182 children = list(node.children)
183 children.reverse()
185 for child_node in children:
186 self.delete_node(child_node.id)
188 node.parent.remove_child(node)
189 del self.nodes[id]
191 if node.type == "F":
192 debug("deleting F %s" % id)
193 self.dao.delete_feed(id)
194 elif node.type == "C":
195 debug("deleting C %s" % id)
196 self.dao.delete_category(id)
198 def save_all(self, save_list):
199 for item in save_list:
200 if isinstance(item, Feed):
201 item.parent_id = 1
203 if item.parent:
204 item.parent_id = item.parent.id
205 else:
206 item.parent_id = 1
207 item.parent = self.nodes[1]
209 self.save_feed(item)
210 else:
211 if item.parent:
212 item.parent_id = item.parent.id
213 else:
214 item.parent_id = 1
215 item.parent = self.nodes[1]
217 self.save_category(item)
219 def lookup_feed(self, id):
220 return self.nodes[id]
222 def lookup_category(self, id):
223 return self.nodes[id]
225 def save_feed(self, feed):
226 if not feed.parent_id:
227 feed.parent_id = 1
229 category = self.lookup_category(feed.parent_id)
231 if not category:
232 return None
234 result = self.dao.save(feed)
236 self.nodes[feed.id] = feed
237 self._model_add_node(feed)
238 self._setup_node_signals(feed)
239 self.emit("feed-added", feed)
241 return result
243 def update_all_feeds(self, observers):
244 feeds = [node for node in self.nodes.values() if node.type == "F"]
246 self.update_all_running = True
248 FeedUpdater.update_feeds(feeds, [{
249 "job-done": [ self._on_update_all_done ],
250 "task-start": [ self._on_update_feed_start ],
251 "task-done": [ self._on_update_feed_done ]
252 }, observers])
254 def update_nodes(self, nodes, observers):
255 feeds = []
257 for node in nodes:
258 if node.type == "F":
259 feeds.append(node)
260 else:
261 feeds.extend([child_node for child_node in node.all_children() if child_node.type == "F"])
263 FeedUpdater.update_feeds(feeds, [{
264 "task-start": [ self._on_update_feed_start ],
265 "task-done": [ self._on_update_feed_done ]
266 }, observers])
268 def _on_update_all_done(self, handler, data):
269 self.update_all_running = False
270 self.emit("update-all-done")
272 def _on_update_feed_start(self, handler, feed):
273 feed.props.status = straw.FS_UPDATING
275 def _on_update_feed_done(self, handler, data):
276 feed = data.task_info.data["feed"]
277 feed.props.status = straw.FS_IDLE
279 if not data.result:
280 return
282 self.dao.tx_begin()
284 for item in data.result.items:
285 item.feed_id = feed.id
287 if not ItemManager.feed_item_exists(item):
288 self.dao.save(item)
289 feed.props.unread_count += 1
290 self.emit("item-added", item)
292 self.dao.tx_commit()
294 def save_category(self, category):
295 debug("saving category %s with parent %s" % (category.name, str(category.parent_id)))
297 if category.parent and not category.parent_id:
298 category.parent_id = category.parent.id
300 self.dao.tx_begin()
302 self.dao.save(category)
303 self._model_add_node(category)
304 self.dao.save(category)
306 self.dao.tx_commit()
308 self.nodes[category.id] = category
310 self._setup_node_signals(category)
311 self.emit("category-added", category)
313 def on_parent_changed(self, obj, old_parent):
314 debug("parent changed, saving %d" % (obj.id))
315 self.dao.save(obj)
317 def on_norder_changed(self, obj, old_norder):
318 debug("norder changed, saving %d" % (obj.id))
319 self.dao.save(obj)
321 _instance = None
323 def _get_instance():
324 global _instance
325 if not _instance:
326 _instance = FeedManager()
327 return _instance