Work on reparenting / reordering...
[straw.git] / straw / FeedManager.py
blob1cea621594c685c58e7b4378ccf993df23edd58e
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, category):
15 fm = _get_instance()
16 fm.import_opml(path, category)
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, feeds = None):
47 fm = _get_instance()
48 return fm.update_all_feeds(observers, feeds)
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 save_all(save_list):
60 fm = _get_instance()
61 fm.save_all(save_list)
63 def get_model():
64 fm = _get_instance()
65 return fm.nodes
67 def categories(root_id = 1):
68 nodes = get_model()
70 if not nodes.has_key(root_id):
71 return
73 yield nodes[root_id]
75 for node in nodes[root_id].children:
76 if node.type == "C":
77 for child in categories(node.id):
78 yield child
80 def move_node(node, new_parent, new_order):
81 fm = _get_instance()
82 fm.move_node(node, new_parent, new_order)
84 class FeedManager(GObject):
85 __gsignals__ = {
86 "item-added" : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,)),
87 "feed-added" : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,)),
88 "category-added" : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,)),
91 def __init__(self):
92 GObject.__init__(self)
94 self.storage = None
96 def init_storage(self, path):
97 self.storage = Storage(path)
98 self.dao = DAO(self.storage)
99 self.nodes = self.dao.get_nodes()
101 #self._process_model()
102 self._prepare_model()
104 #print self.nodes[1].children
106 def import_opml(self, path, category):
107 job = Job("opml-parse")
108 job.data = (path, category)
109 job.observers = [ { "job-done": [ self.import_opml_save ] } ]
111 JobManager.start_job(job)
113 def import_opml_save(self, handler, opml_data):
114 save_list, category = opml_data
116 self.dao.tx_begin()
117 self.save_all(save_list)
118 self.dao.tx_commit()
120 def _model_add_node(self, node):
121 self.nodes[node.parent_id].add_child(node)
123 def move_node(self, node, target_path, new_order):
124 #print node
125 #print new_parent
126 #print new_order
127 #print self.nodes[1].get_by_path(new_parent)
128 #print "move_node %d to %s [%s]" % (node.id, str(new_parent), new_parent[:-1])
129 new_parent = self.nodes[1].get_by_path(target_path[:-1])
131 if not new_parent:
132 new_parent = self.nodes[1]
134 node.move(new_parent, target_path[-1:][0])
136 def save_all(self, save_list):
137 for item in save_list:
138 if isinstance(item, Feed):
139 item.parent_id = 1
141 if item.parent:
142 item.parent_id = item.parent.id
143 else:
144 item.parent_id = 1
145 item.parent = self.nodes[1]
147 self.save_feed(item)
148 else:
149 if item.parent:
150 item.parent_id = item.parent.id
151 else:
152 item.parent_id = 1
153 item.parent = self.nodes[1]
155 self.save_category(item)
157 def lookup_feed(self, id):
158 return self.nodes[id]
160 def lookup_category(self, id):
161 return self.nodes[id]
163 def save_feed(self, feed):
164 if not feed.parent_id:
165 feed.parent_id = 1
167 category = self.lookup_category(feed.parent_id)
169 if not category:
170 return None
172 result = self.dao.save(feed)
174 self.nodes[feed.id] = feed
175 self._model_add_node(feed)
176 self.emit("feed-added", feed)
178 return result
180 def update_all_feeds(self, observers, feeds = None):
181 if not feeds:
182 feeds = [node for node in self.nodes.values() if node.type == "F"]
184 FeedUpdater.update_feeds(feeds, [{
185 "job-done": [ self.update_all_feeds_done ],
186 "task-start": [ self._on_update_feed_start ],
187 "task-done": [ self._on_update_feed_done ]
188 }, observers])
190 def update_all_feeds_done(self, handler, data):
191 pass
193 def _on_update_feed_start(self, handler, feed):
194 feed.props.status = straw.FS_UPDATING
196 def _on_update_feed_done(self, handler, data):
197 feed = data.task_info.data["feed"]
198 feed.props.status = straw.FS_IDLE
200 if not data.result:
201 return
203 self.dao.tx_begin()
205 for item in data.result.items:
206 item.feed_id = feed.id
208 if not self.feed_item_exists(item):
209 self.dao.save(item)
210 feed.props.unread_count += 1
211 self.emit("item-added", item)
213 self.dao.tx_commit()
215 ### CATEGORIES
217 def save_category(self, category):
218 self.dao.save(category)
220 self.nodes[category.id] = category
221 self._model_add_node(category)
223 self.emit("category-added", category)
225 # ITEMS
227 def get_feed_items(self, id):
228 feed = self.lookup_feed(id)
229 feed.items = self.dao.get(Item, " WHERE feed_id = %d" % feed.id)
230 for item in feed.items:
231 item.feed = feed
232 item.connect("is-read-changed", feed.on_is_read_changed)
233 item.connect("is-read-changed", self.on_item_is_read_changed)
234 return feed.items
236 def get_category_items(self, id):
237 category = self.lookup_category(id)
239 if not category:
240 return None
242 def _get_children_feeds(category_id):
243 feed_ids = [str(node.id) for node in self.lookup_category(category_id).children if node.type == "F"]
244 children_categories = [node.id for node in self.lookup_category(category_id).children if node.type == "C"]
246 for ctg_id in children_categories:
247 feed_ids.extend(_get_children_feeds(ctg_id))
249 return feed_ids
251 in_str = ", ".join(_get_children_feeds(id))
253 items = self.dao.get(Item, " WHERE feed_id IN(%s) ORDER BY id" % in_str)
255 for item in items:
256 feed = self.lookup_feed(item.feed_id)
257 item.feed = feed
258 item.connect("is-read-changed", feed.on_is_read_changed)
259 item.connect("is-read-changed", self.on_item_is_read_changed)
261 return items
263 def feed_item_exists(self, item):
264 result = self.dao.get(Item, " WHERE feed_id = ? AND title = ?", (item.feed_id, item.title,))
265 return len(result) > 0
267 def _prepare_model(self):
268 for node in self.nodes.values():
269 node.connect("parent-changed", self.on_parent_changed)
270 node.connect("norder-changed", self.on_norder_changed)
272 if node.parent_id != None:
273 node.parent = self.nodes[node.parent_id]
274 node.parent.add_child(node, node.norder)
275 else:
276 node.parent = None
278 def on_item_is_read_changed(self, obj, is_read):
279 self.dao.save(obj)
281 def on_parent_changed(self, obj, old_parent):
282 self.dao.save(obj)
284 def on_norder_changed(self, obj, old_norder):
285 self.dao.save(obj)
287 _instance = None
289 def _get_instance():
290 global _instance
291 if not _instance:
292 _instance = FeedManager()
293 return _instance