1 from Constants
import *
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
15 fm
.init_storage(_storage_path
)
17 def setup(storage_path
= None):
19 _storage_path
= storage_path
21 def import_opml(path
, category
):
23 fm
.import_opml(path
, category
)
25 def export_opml(root_id
, filename
):
27 fm
.export_opml(root_id
, filename
)
31 return fm
.lookup_feed(id)
33 def lookup_category(ctg_id
):
35 return fm
.lookup_category(ctg_id
)
41 def update_all_feeds(observers
):
43 return fm
.update_all_feeds(observers
)
45 def is_update_all_running():
47 return fm
.update_all_id
!= None
49 def stop_update_all():
51 return fm
.stop_update_all()
53 def update_nodes(nodes
, observers
= {}):
55 return fm
.update_nodes(nodes
, observers
)
57 def save_category(category
):
59 return fm
.save_category(category
)
63 result
= fm
.save_feed(feed
)
66 def save_all(save_list
):
68 fm
.save_all(save_list
)
74 def get_children_feeds(node_id
):
75 node
= lookup_category(node_id
)
80 return [_node
for _node
in node
.all_children() if _node
.type == "F"]
82 def categories(root_id
= 1):
85 if not nodes
.has_key(root_id
):
90 for node
in nodes
[root_id
].children
:
92 for child
in categories(node
.id):
95 def move_node(node
, target_path
):
97 fm
.move_node(node
, target_path
)
99 def delete_nodes(node_ids
):
101 fm
.delete_nodes(node_ids
)
103 class FeedManager(GObject
):
105 "item-added": (gobject
.SIGNAL_RUN_LAST
, gobject
.TYPE_NONE
, (gobject
.TYPE_PYOBJECT
,)),
106 "feed-added": (gobject
.SIGNAL_RUN_LAST
, gobject
.TYPE_NONE
, (gobject
.TYPE_PYOBJECT
,)),
107 "feed-status-changed": (gobject
.SIGNAL_RUN_LAST
, gobject
.TYPE_NONE
, (gobject
.TYPE_PYOBJECT
,)),
108 "category-added": (gobject
.SIGNAL_RUN_LAST
, gobject
.TYPE_NONE
, (gobject
.TYPE_PYOBJECT
,)),
109 "update-all-done": (gobject
.SIGNAL_RUN_LAST
, gobject
.TYPE_NONE
, ())
113 GObject
.__init
__(self
)
116 self
.update_all_id
= None
118 def emit(self
, *args
):
119 gobject
.idle_add(gobject
.GObject
.emit
, self
, *args
)
121 def init_storage(self
, path
):
122 self
.storage
= Storage(path
)
123 self
.dao
= DAO(self
.storage
)
124 self
.nodes
= self
.dao
.get_nodes()
126 self
._prepare
_model
()
128 def _prepare_model(self
):
129 for node
in self
.nodes
.values():
130 self
._setup
_node
_signals
(node
)
132 if node
.parent_id
!= None:
133 node
.parent
= self
.nodes
[node
.parent_id
]
134 node
.parent
.append_child(node
)
138 if isinstance(node
, Feed
):
139 self
._set
_feed
_status
(node
, FS_IDLE
)
141 def _setup_node_signals(self
, node
):
142 node
.connect("parent-changed", self
.on_parent_changed
)
143 node
.connect("norder-changed", self
.on_norder_changed
)
144 node
.connect("mark-all-items-as-read", self
.on_mark_all_items_as_read
)
146 def _set_feed_status(self
, feed
, status
):
147 if not hasattr(feed
, "status"):
150 if feed
.status
!= status
:
152 self
.emit("feed-status-changed", feed
)
154 def import_opml(self
, path
, category
):
155 # XXX: We should support remote locations for OPML import!
156 url
= "file://" + path
157 opml
.import_opml(url
, category
, [ { "opml-imported": [ self
._on
_opml
_imported
] } ])
159 def _on_opml_imported(self
, handler
, save_list
):
162 self
.save_all(save_list
)
165 def export_opml(self
, root_id
, filename
):
166 if not self
.nodes
.has_key(root_id
):
169 opml
.export(self
.nodes
[root_id
], filename
)
171 def _model_add_node(self
, node
):
172 self
.nodes
[node
.parent_id
].add_child(node
)
174 def move_node(self
, node
, target_path
):
175 debug("fm:move_node %d to %s" % (node
.id, str(target_path
)))
176 new_parent
= self
.nodes
[1].get_by_path(target_path
[:-1])
179 new_parent
= self
.nodes
[1]
182 node
.move(new_parent
, target_path
[-1:][0])
185 def delete_nodes(self
, node_ids
):
193 def delete_node(self
, id):
194 if not self
.nodes
.has_key(id):
197 node
= self
.nodes
[id]
199 children
= list(node
.children
)
202 for child_node
in children
:
203 self
.delete_node(child_node
.id)
205 node
.parent
.remove_child(node
)
209 debug("deleting F %s" % id)
210 self
.dao
.delete_feed(id)
211 elif node
.type == "C":
212 debug("deleting C %s" % id)
213 self
.dao
.delete_category(id)
215 def save_all(self
, save_list
):
216 for item
in save_list
:
217 if isinstance(item
, Feed
):
218 self
._set
_feed
_status
(item
, FS_IDLE
)
222 item
.parent_id
= item
.parent
.id
225 item
.parent
= self
.nodes
[1]
230 item
.parent_id
= item
.parent
.id
233 item
.parent
= self
.nodes
[1]
235 self
.save_category(item
)
237 def lookup_feed(self
, id):
238 return self
.nodes
[id]
240 def lookup_category(self
, id):
241 return self
.nodes
[id]
243 def save_feed(self
, feed
):
244 if not feed
.parent_id
:
247 category
= self
.lookup_category(feed
.parent_id
)
253 result
= self
.dao
.save(feed
)
255 result
= self
.dao
.save(feed
)
256 self
._model
_add
_node
(feed
)
259 self
.nodes
[feed
.id] = feed
261 self
._setup
_node
_signals
(feed
)
262 self
.emit("feed-added", feed
)
266 def update_all_feeds(self
, observers
):
267 feeds
= [node
for node
in self
.nodes
.values() if node
.type == "F"]
269 self
.update_all_id
= FeedUpdater
.update(feeds
, [{
270 "job-done": [ self
._on
_update
_all
_done
],
271 "update-started": [ self
._on
_update
_feed
_start
],
272 "update-done": [ self
._on
_update
_feed
_done
]
275 def update_nodes(self
, nodes
, observers
):
282 feeds
.extend([child_node
for child_node
in node
.all_children() if child_node
.type == "F"])
284 FeedUpdater
.update(feeds
, [{
285 "update-started": [ self
._on
_update
_feed
_start
],
286 "update-done": [ self
._on
_update
_feed
_done
]
289 def stop_update_all(self
):
290 FeedUpdater
.stop(self
.update_all_id
)
292 def _on_update_all_done(self
, handler
, data
):
293 self
.update_all_id
= None
294 self
.emit("update-all-done")
296 def _on_update_feed_start(self
, handler
, feed
):
297 self
._set
_feed
_status
(feed
, FS_UPDATING
)
299 def _on_update_feed_done(self
, handler
, update_result
):
300 feed
= update_result
.feed
302 if update_result
.error
:
303 feed
.update_result
= update_result
304 self
._set
_feed
_status
(feed
, FS_ERROR
)
307 self
._set
_feed
_status
(feed
, FS_IDLE
)
311 # Some metadata could change between the updates.
314 for item
in feed
.items
:
315 item
.feed_id
= feed
.id
317 if not ItemManager
.feed_item_exists(item
):
319 feed
.props
.unread_count
+= 1
320 self
.emit("item-added", item
)
324 def save_category(self
, category
):
325 debug("saving category %s with parent %s" % (category
.name
, str(category
.parent_id
)))
327 if category
.parent
and not category
.parent_id
:
328 category
.parent_id
= category
.parent
.id
332 self
.dao
.save(category
)
333 self
._model
_add
_node
(category
)
334 self
.dao
.save(category
)
338 self
.nodes
[category
.id] = category
340 self
._setup
_node
_signals
(category
)
341 self
.emit("category-added", category
)
343 def on_parent_changed(self
, obj
, old_parent
):
344 #debug("parent changed, saving %d" % (obj.id))
347 def on_norder_changed(self
, obj
, old_norder
):
348 #debug("norder changed, saving %d" % (obj.id))
351 def on_mark_all_items_as_read(self
, node
):
353 result
= self
.dao
.query("UPDATE items SET is_read = 1 WHERE feed_id = %d" % node
.id)
356 node
.props
.unread_count
-= node
.props
.unread_count
363 _instance
= FeedManager()