4 import util.misc as misc_utils
5 from util import go_utils
9 _moduleLogger = logging.getLogger(__name__)
12 SOURCE_RADIO = "radio"
13 SOURCE_CONFERENCES = "conferences"
14 SOURCE_MAGAZINES = "magazines"
15 SOURCE_SCRIPTURES = "scriptures"
18 class Connection(object):
21 self._backend = backend.Backend()
22 self._indexing = go_utils.AsyncPool()
25 self._indexing.start()
30 def download(self, func, on_success, on_error, args = None, kwds = None):
36 self._indexing.clear_tasks()
37 self._indexing.add_task(
38 getattr(self._backend, func),
46 class AudioIndex(object):
49 self._connection = Connection()
50 self._languages = None
51 self._languagesRequest = None
55 self._connection.start()
58 self._connection.stop()
60 def get_languages(self, on_success, on_error):
61 if self._languages is None:
62 assert self._languagesRequest is None
63 self._languagesRequest = on_success, on_error
64 self._connection.download(
66 self._on_get_languages,
67 self._on_languages_error
70 on_success(self._languages)
72 def get_source(self, source, langId = None):
73 key = (source, langId)
74 if key in self._sources:
75 node = self._sources[key]
77 if source == SOURCE_RADIO:
78 node = RadioNode(self._connection)
79 elif source == SOURCE_CONFERENCES:
80 assert langId is not None
81 node = ConferencesNode(self._connection, langId)
83 raise NotImplementedError(source)
84 self._sources[key] = node
88 @misc_utils.log_exception(_moduleLogger)
89 def _on_get_languages(self, languages):
90 assert self._languages is None
91 assert self._languagesRequest is not None
92 r = self._languagesRequest
93 self._languagesRequest = None
94 self._languages = languages
97 @misc_utils.log_exception(_moduleLogger)
98 def _on_languages_error(self, e):
99 assert self._languages is None
100 assert self._languagesRequest is not None
101 r = self._languagesRequest
102 self._languagesRequest = None
103 r[1](self._languages)
108 def __init__(self, connection, parent, data, id):
109 self._connection = connection
110 self._parent = weakref.ref(parent) if parent is not None else None
112 self._children = None
115 def get_children(self, on_success, on_error):
116 if self._children is None:
117 self._get_children(on_success, on_error)
119 on_success(self._children)
121 def get_parent(self):
122 if self._parent is None:
123 raise RuntimeError("")
124 parent = self._parent()
127 def get_properties(self):
132 raise NotImplementedError("On %s" % type(self))
139 raise NotImplementedError("")
141 def _get_children(self, on_success, on_error):
142 raise NotImplementedError("")
145 class ParentNode(Node):
147 def __init__(self, connection, parent, data, id):
148 Node.__init__(self, connection, parent, data, id)
154 def _get_children(self, on_success, on_error):
155 assert self._request is None
156 assert self._children is None
157 self._request = on_success, on_error
159 func, args, kwds = self._get_func()
161 self._connection.download(
170 raise NotImplementedError()
172 def _create_child(self, data, id):
173 raise NotImplementedError()
175 @misc_utils.log_exception(_moduleLogger)
176 def _on_success(self, data):
181 self._create_child(child, i)
182 for i, child in enumerate(data)
185 _moduleLogger.exception("Translating error")
186 self._children = None
191 @misc_utils.log_exception(_moduleLogger)
192 def _on_error(self, error):
198 class LeafNode(Node):
200 def __init__(self, connection, parent, data, id):
201 Node.__init__(self, connection, parent, data, id)
207 def can_navigate(self):
208 raise NotImplementedError("On %s" % type(self))
212 raise NotImplementedError("On %s" % type(self))
216 raise NotImplementedError("On %s" % type(self))
218 def _get_children(self, on_success, on_error):
219 raise RuntimeError("Not is a leaf")
222 class RadioNode(ParentNode):
224 def __init__(self, connection):
225 ParentNode.__init__(self, connection, None, {}, SOURCE_RADIO)
232 return "get_radio_channels", (), {}
234 def _create_child(self, data, id):
235 return RadioChannelNode(self._connection, self, data, id)
238 class RadioChannelNode(LeafNode):
240 def __init__(self, connection, parent, data, id):
241 LeafNode.__init__(self, connection, parent, data, id)
242 self._extendedData = {}
246 def can_navigate(self):
259 return self._data["url"]
261 def get_programming(self, date, on_success, on_error):
262 date = date.strftime("%Y-%m-%d")
264 programming = self._extendedData[date]
266 self._get_programming(date, on_success, on_error)
268 on_success(programming)
270 def _get_programming(self, date, on_success, on_error):
271 assert self._request is None
272 assert date not in self._extendedData
273 self._request = on_success, on_error, date
275 self._connection.download(
276 "get_radio_channel_programming",
279 (self._data["id"], date),
283 @misc_utils.log_exception(_moduleLogger)
284 def _on_success(self, data):
289 self._extendedData[date] = [
294 _moduleLogger.exception("Translating error")
295 del self._extendedData[date]
298 r[0](self._extendedData[date])
300 @misc_utils.log_exception(_moduleLogger)
301 def _on_error(self, error):
307 class ConferencesNode(ParentNode):
309 def __init__(self, connection, langId):
310 ParentNode.__init__(self, connection, None, {}, SOURCE_CONFERENCES)
311 self._langId = langId
318 return "get_conferences", (self._langId, ), {}
320 def _create_child(self, data, id):
321 return ConferenceNode(self._connection, self, data, id)
324 class ConferenceNode(ParentNode):
326 def __init__(self, connection, parent, data, id):
327 ParentNode.__init__(self, connection, parent, data, id)
331 return self._data["title"]
334 return "get_conference_sessions", (self._data["id"], ), {}
336 def _create_child(self, data, id):
337 return SessionNode(self._connection, self, data, id)
340 class SessionNode(ParentNode):
342 def __init__(self, connection, parent, data, id):
343 ParentNode.__init__(self, connection, parent, data, id)
347 return self._data["title"]
350 return "get_conference_talks", (self._data["id"], ), {}
352 def _create_child(self, data, id):
353 return TalkNode(self._connection, self, data, id)
356 class TalkNode(LeafNode):
358 def __init__(self, connection, parent, data, id):
359 LeafNode.__init__(self, connection, parent, data, id)
362 def can_navigate(self):
367 return self._data["title"]
371 speaker = self._data["speaker"]
372 if speaker is not None:
379 return self._data["url"]
382 def walk_ancestors(node):
386 node = node.get_parent()
391 def common_paths(targetNode, currentNode):
392 targetNodePath = list(walk_ancestors(targetNode))
393 targetNodePath.reverse()
394 currentNodePath = list(walk_ancestors(currentNode))
395 currentNodePath.reverse()
400 for i, (t, c) in enumerate(zip(targetNodePath, currentNodePath)):
402 return ancestors, None, descendants
407 for child in targetNodePath[i+1:]
410 return ancestors, currentNode, descendants
413 class AsyncWalker(object):
415 def __init__(self, func):
419 def start(self, *args, **kwds):
420 assert self._run is None
421 self._run = self._func(*args, **kwds)
422 node = self._run.send(None) # priming the function
423 node.get_children(self.on_success, self.on_error)
425 @misc_utils.log_exception(_moduleLogger)
426 def on_success(self, children):
427 _moduleLogger.debug("Processing success for: %r", self._func)
429 node = self._run.send(children)
430 except StopIteration, e:
433 node.get_children(self.on_success, self.on_error)
435 @misc_utils.log_exception(_moduleLogger)
436 def on_error(self, error):
437 _moduleLogger.debug("Processing error for: %r", self._func)
439 node = self._run.throw(error)
440 except StopIteration, e:
443 node.get_children(self.on_success, self.on_error)
446 def get_next(node, on_success, on_error):
448 assert node.is_leaf(), node
453 parent = childNode.get_parent()
454 siblings = yield parent
455 for i, sibling in enumerate(siblings):
456 if sibling is childNode:
459 if i < len(siblings):
460 sibling = siblings[i]
465 # dig into that branch to find the first leaf
472 children = yield child
473 nodes[0:0] = children
474 raise RuntimeError("Ran out of nodes when hunting for first leaf of %s" % node)
479 def get_previous(node, on_success, on_error):
481 assert node.is_leaf(), node
486 parent = childNode.get_parent()
487 siblings = yield parent
488 for i, sibling in enumerate(siblings):
489 if sibling is childNode:
493 sibling = siblings[i]
498 # dig into that branch to find the first leaf
501 child = nodes.pop(-1)
505 children = yield child
506 nodes[0:0] = children
507 raise RuntimeError("Ran out of nodes when hunting for first leaf of %s" % node)