Work on reordering and reparenting in the tree view, GUI fixes.
[straw/fork.git] / straw / FeedManager.py
blob26eddd21c991472c2d4e3fd536a6bfca3620bc85
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):
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 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.categories, fm.model
67 def categories(root_id = 1):
68 _categories, model = get_model()
70 yield _categories[root_id]
72 if model.has_key(root_id):
73 for node in model[root_id]:
74 if node.type == "C":
75 for child in categories(node.id):
76 yield child
77 return
79 def move_node(node, new_parent, new_order):
80 fm = _get_instance()
81 fm.move_node(node, new_parent, new_order)
83 class FeedManager(GObject):
84 __gsignals__ = {
85 "item-added" : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,)),
86 "feed-added" : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,)),
87 "category-added" : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,)),
90 def __init__(self):
91 GObject.__init__(self)
93 self.storage = None
95 def init_storage(self, path):
96 self.storage = Storage(path)
97 self.dao = DAO(self.storage)
98 self.nodes, self.model = self.dao.get_nodes()
100 self._process_model()
102 def import_opml(self, path, category):
103 job = Job("opml-parse")
104 job.data = (path, category)
105 job.observers = [ { "job-done": [ self.import_opml_save ] } ]
107 JobManager.start_job(job)
109 def import_opml_save(self, handler, opml_data):
110 save_list, category = opml_data
112 self.dao.tx_begin()
113 self.save_all(save_list)
114 self.dao.tx_commit()
116 def _model_add_node(self, node):
117 if not self.model.has_key(node.parent_id):
118 self.model[node.parent_id] = []
120 self.model[node.parent_id].append(node)
122 def _calculate_order(self, node):
123 if not self.model.has_key(node.parent.id):
124 return 0
125 else:
126 return len(self.model[node.parent.id])
128 def move_node(self, node, new_parent, new_order):
129 print node
130 print new_parent
131 print new_order
133 def save_all(self, save_list):
134 for item in save_list:
135 if isinstance(item, Feed):
136 item.parent_id = 1
138 if item.parent:
139 item.parent_id = item.parent.id
140 else:
141 item.parent_id = 1
142 item.parent = self.nodes[1]
144 item.norder = self._calculate_order(item)
146 self.save_feed(item)
147 else:
148 if item.parent:
149 item.parent_id = item.parent.id
150 else:
151 item.parent_id = 1
152 item.parent = self.nodes[1]
154 item.norder = self._calculate_order(item)
156 self.save_category(item)
158 def lookup_feed(self, id):
159 return self.nodes[id]
161 def lookup_category(self, id):
162 return self.categories[id]
164 def save_feed(self, feed):
165 if not feed.parent_id:
166 feed.parent_id = 1
168 category = self.lookup_category(feed.parent_id)
170 if not category:
171 return None
173 result = self.dao.save(feed)
175 if not self.feeds.has_key(feed.parent_id):
176 self.feeds[feed.parent_id] = []
178 feed.connect("unread-count-changed", category.on_unread_count_changed)
180 self.feeds[feed.parent_id].append(feed)
181 self.nodes[feed.id] = feed
182 self._model_add_node(feed)
184 self.emit("feed-added", feed)
186 return result
188 def update_all_feeds(self, observers):
189 feeds = [node for key in self.model.keys() for node in self.model[key] if node.type == "F"]
191 FeedUpdater.update_feeds(feeds, [{
192 "job-done": [ self.update_all_feeds_done ],
193 "task-start": [ self._on_update_feed_start ],
194 "task-done": [ self._on_update_feed_done ]
195 }, observers])
197 def update_all_feeds_done(self, handler, data):
198 pass
200 def _on_update_feed_start(self, handler, feed):
201 feed.props.status = straw.FS_UPDATING
203 def _on_update_feed_done(self, handler, data):
204 feed = data.task_info.data["feed"]
205 feed.props.status = straw.FS_IDLE
207 if not data.result:
208 return
210 for item in data.result.items:
211 item.feed_id = feed.id
213 if not self.feed_item_exists(item):
214 self.dao.save(item)
215 feed.props.unread_count += 1
216 self.emit("item-added", item)
218 ### CATEGORIES
220 def save_category(self, category):
221 self.dao.save(category)
223 category.connect("unread-count-changed",
224 self.lookup_category(category.parent_id).on_unread_count_changed)
226 self.categories[category.id] = category
227 self.nodes[category.id] = category
228 self._model_add_node(category)
230 self.emit("category-added", category)
232 # ITEMS
234 def get_feed_items(self, id):
235 feed = self.lookup_feed(id)
236 feed.items = self.dao.get(Item, " WHERE feed_id = %d" % feed.id)
237 for item in feed.items:
238 item.feed = feed
239 item.connect("is-read-changed", feed.on_is_read_changed)
240 item.connect("is-read-changed", self.on_item_is_read_changed)
241 return feed.items
243 def get_category_items(self, id):
244 category = self.lookup_category(id)
246 if category == None:
247 return None
249 def _get_children_feeds(category_id):
250 feed_ids = []
252 if self.feeds.has_key(category_id):
253 feed_ids = [str(feed.id) for feed in self.feeds[category_id]]
255 children_categories = [ctg_id for ctg_id in self.categories.keys() if self.categories[ctg_id].parent_id == category_id]
257 for ctg_id in children_categories:
258 feed_ids.extend(_get_children_feeds(ctg_id))
260 return feed_ids
262 in_str = ", ".join(_get_children_feeds(id))
264 items = self.dao.get(Item, " WHERE feed_id IN(%s) ORDER BY id" % in_str)
266 for item in items:
267 feed = self.lookup_feed(item.feed_id)
268 item.feed = feed
269 item.connect("is-read-changed", feed.on_is_read_changed)
270 item.connect("is-read-changed", self.on_item_is_read_changed)
272 return items
274 def feed_item_exists(self, item):
275 result = self.dao.get(Item, " WHERE feed_id = ? AND title = ?", (item.feed_id, item.title,))
276 return len(result) > 0
278 def on_item_is_read_changed(self, obj, is_read):
279 self.dao.save(obj)
281 def _process_model(self):
282 self.categories = dict([(node.id, node) for node in self.nodes.values() if node.type == "C"])
283 self.feeds = {}
285 for category_id in self.categories.keys():
286 category = self.categories[category_id]
288 if category.parent_id != None:
289 category.connect("unread-count-changed",
290 self.categories[category.parent_id].on_unread_count_changed)
292 for node in self.nodes.values():
293 if node.parent_id != None:
294 node.parent = self.nodes[node.parent_id]
295 else:
296 node.parent = None
298 if node.type == "F":
299 if not self.feeds.has_key(node.parent_id):
300 self.feeds[node.parent_id] = []
302 self.feeds[node.parent_id].append(node)
303 self.categories[node.parent_id].props.unread_count += node.unread_count
305 node.connect("unread-count-changed", node.parent.on_unread_count_changed)
307 _instance = None
309 def _get_instance():
310 global _instance
311 if not _instance:
312 _instance = FeedManager()
313 return _instance