Yet another fixes to tree view manipulation...
[straw.git] / straw / FeedManager.py
blobb7751e21d39fc3cfce91df99981407731e62a844
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 JobManager
8 import gobject
9 import straw
11 _storage_path = None
13 model_data = None
15 def import_opml(path, category):
16 fm = _get_instance()
17 fm.import_opml(path, category)
19 def init():
20 fm = _get_instance()
21 fm.init_storage(_storage_path)
23 def setup(storage_path = None):
24 global _storage_path
25 _storage_path = storage_path
27 def lookup_feed(id, ctg_id):
28 fm = _get_instance()
29 return fm.lookup_feed(id, ctg_id)
31 def lookup_category(ctg_id):
32 fm = _get_instance()
33 return fm.lookup_category(ctg_id)
35 def get_feed_items(id):
36 fm = _get_instance()
37 return fm.get_feed_items(id)
39 def get_category_items(id):
40 fm = _get_instance()
41 return fm.get_category_items(id)
43 def get_feeds():
44 fm = _get_instance()
45 return fm.get_feeds()
47 def update_all_feeds(observers, feeds = None):
48 fm = _get_instance()
49 return fm.update_all_feeds(observers, feeds)
51 def save_category(title, pid = None):
52 fm = _get_instance()
53 return fm.save_category(title, parent_id = pid)
55 def save_feed(feed):
56 fm = _get_instance()
57 result = fm.save_feed(feed)
58 return result
60 def save_all(save_list):
61 fm = _get_instance()
62 fm.save_all(save_list)
64 def get_model():
65 fm = _get_instance()
66 return fm.nodes
68 def categories(root_id = 1):
69 nodes = get_model()
71 if not nodes.has_key(root_id):
72 return
74 yield nodes[root_id]
76 for node in nodes[root_id].children:
77 if node.type == "C":
78 for child in categories(node.id):
79 yield child
81 def move_node(node, target_path):
82 fm = _get_instance()
83 fm.move_node(node, target_path)
85 class FeedManager(GObject):
86 __gsignals__ = {
87 "item-added" : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,)),
88 "feed-added" : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,)),
89 "category-added" : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,)),
92 def __init__(self):
93 GObject.__init__(self)
95 self.storage = None
97 def init_storage(self, path):
98 self.storage = Storage(path)
99 self.dao = DAO(self.storage)
100 self.nodes = self.dao.get_nodes()
102 #self._process_model()
103 self._prepare_model()
105 #print self.nodes[1].children
107 def import_opml(self, path, category):
108 job = Job("opml-parse")
109 job.data = (path, category)
110 job.observers = [ { "job-done": [ self.import_opml_save ] } ]
112 JobManager.start_job(job)
114 def import_opml_save(self, handler, opml_data):
115 save_list, category = opml_data
117 self.dao.tx_begin()
118 self.save_all(save_list)
119 self.dao.tx_commit()
121 def _model_add_node(self, node):
122 self.nodes[node.parent_id].add_child(node)
124 def move_node(self, node, target_path):
125 #print node
126 #print new_parent
127 #print new_order
128 #print self.nodes[1].get_by_path(new_parent)
129 #print "move_node %d to %s [%s]" % (node.id, str(new_parent), new_parent[:-1])
130 new_parent = self.nodes[1].get_by_path(target_path[:-1])
132 if not new_parent:
133 new_parent = self.nodes[1]
135 node.move(new_parent, target_path[-1:][0])
137 def save_all(self, save_list):
138 for item in save_list:
139 item.connect("parent-changed", self.on_parent_changed)
140 item.connect("norder-changed", self.on_norder_changed)
142 if isinstance(item, Feed):
143 item.parent_id = 1
145 if item.parent:
146 item.parent_id = item.parent.id
147 else:
148 item.parent_id = 1
149 item.parent = self.nodes[1]
151 self.save_feed(item)
152 else:
153 if item.parent:
154 item.parent_id = item.parent.id
155 else:
156 item.parent_id = 1
157 item.parent = self.nodes[1]
159 self.save_category(item)
161 def lookup_feed(self, id):
162 return self.nodes[id]
164 def lookup_category(self, id):
165 return self.nodes[id]
167 def save_feed(self, feed):
168 if not feed.parent_id:
169 feed.parent_id = 1
171 category = self.lookup_category(feed.parent_id)
173 if not category:
174 return None
176 result = self.dao.save(feed)
178 self.nodes[feed.id] = feed
179 self._model_add_node(feed)
180 self.emit("feed-added", feed)
182 return result
184 def update_all_feeds(self, observers, feeds = None):
185 if not feeds:
186 feeds = [node for node in self.nodes.values() if node.type == "F"]
188 FeedUpdater.update_feeds(feeds, [{
189 "job-done": [ self.update_all_feeds_done ],
190 "task-start": [ self._on_update_feed_start ],
191 "task-done": [ self._on_update_feed_done ]
192 }, observers])
194 def update_all_feeds_done(self, handler, data):
195 pass
197 def _on_update_feed_start(self, handler, feed):
198 feed.props.status = straw.FS_UPDATING
200 def _on_update_feed_done(self, handler, data):
201 feed = data.task_info.data["feed"]
202 feed.props.status = straw.FS_IDLE
204 if not data.result:
205 return
207 self.dao.tx_begin()
209 for item in data.result.items:
210 item.feed_id = feed.id
212 if not self.feed_item_exists(item):
213 self.dao.save(item)
214 feed.props.unread_count += 1
215 self.emit("item-added", item)
217 self.dao.tx_commit()
219 ### CATEGORIES
221 def save_category(self, category):
222 self.dao.save(category)
224 self.nodes[category.id] = category
225 self._model_add_node(category)
227 self.emit("category-added", category)
229 # ITEMS
231 def get_feed_items(self, id):
232 feed = self.lookup_feed(id)
233 feed.items = self.dao.get(Item, " WHERE feed_id = %d" % feed.id)
234 for item in feed.items:
235 item.feed = feed
236 item.connect("is-read-changed", feed.on_is_read_changed)
237 item.connect("is-read-changed", self.on_item_is_read_changed)
238 return feed.items
240 def get_category_items(self, id):
241 category = self.lookup_category(id)
243 if not category:
244 return None
246 def _get_children_feeds(category_id):
247 feed_ids = [str(node.id) for node in self.lookup_category(category_id).children if node.type == "F"]
248 children_categories = [node.id for node in self.lookup_category(category_id).children if node.type == "C"]
250 for ctg_id in children_categories:
251 feed_ids.extend(_get_children_feeds(ctg_id))
253 return feed_ids
255 in_str = ", ".join(_get_children_feeds(id))
257 items = self.dao.get(Item, " WHERE feed_id IN(%s) ORDER BY id" % in_str)
259 for item in items:
260 feed = self.lookup_feed(item.feed_id)
261 item.feed = feed
262 item.connect("is-read-changed", feed.on_is_read_changed)
263 item.connect("is-read-changed", self.on_item_is_read_changed)
265 return items
267 def feed_item_exists(self, item):
268 result = self.dao.get(Item, " WHERE feed_id = ? AND title = ?", (item.feed_id, item.title,))
269 return len(result) > 0
271 def _prepare_model(self):
272 for node in self.nodes.values():
273 node.connect("parent-changed", self.on_parent_changed)
274 node.connect("norder-changed", self.on_norder_changed)
276 if node.parent_id != None:
277 node.parent = self.nodes[node.parent_id]
278 node.parent.append_child(node)
279 else:
280 node.parent = None
282 def on_item_is_read_changed(self, obj, is_read):
283 self.dao.save(obj)
285 def on_parent_changed(self, obj, old_parent):
286 debug("parent changed, saving %d" % (obj.id))
287 self.dao.save(obj)
289 def on_norder_changed(self, obj, old_norder):
290 debug("norder changed, saving %d" % (obj.id))
291 self.dao.save(obj)
293 _instance = None
295 def _get_instance():
296 global _instance
297 if not _instance:
298 _instance = FeedManager()
299 return _instance