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)
82 elif source == SOURCE_MAGAZINES:
83 assert langId is not None
84 node = MagazinesNode(self._connection, langId)
85 elif source == SOURCE_SCRIPTURES:
86 assert langId is not None
87 node = ScripturesNode(self._connection, langId)
89 raise NotImplementedError(source)
90 self._sources[key] = node
94 @misc_utils.log_exception(_moduleLogger)
95 def _on_get_languages(self, languages):
96 assert self._languages is None
97 assert self._languagesRequest is not None
98 r = self._languagesRequest
99 self._languagesRequest = None
100 self._languages = languages
101 r[0](self._languages)
103 @misc_utils.log_exception(_moduleLogger)
104 def _on_languages_error(self, e):
105 assert self._languages is None
106 assert self._languagesRequest is not None
107 r = self._languagesRequest
108 self._languagesRequest = None
109 r[1](self._languages)
114 def __init__(self, connection, parent, data, id):
115 self._connection = connection
116 self._parent = weakref.ref(parent) if parent is not None else None
118 self._children = None
121 def get_children(self, on_success, on_error):
122 if self._children is None:
123 self._get_children(on_success, on_error)
125 on_success(self._children)
127 def get_parent(self):
128 if self._parent is None:
129 raise RuntimeError("")
130 parent = self._parent()
133 def get_properties(self):
138 raise NotImplementedError("On %s" % type(self))
145 raise NotImplementedError("")
147 def _get_children(self, on_success, on_error):
148 raise NotImplementedError("")
151 class ParentNode(Node):
153 def __init__(self, connection, parent, data, id):
154 Node.__init__(self, connection, parent, data, id)
160 def _get_children(self, on_success, on_error):
161 assert self._request is None
162 assert self._children is None
163 self._request = on_success, on_error
165 func, args, kwds = self._get_func()
167 self._connection.download(
176 raise NotImplementedError()
178 def _create_child(self, data, id):
179 raise NotImplementedError()
181 @misc_utils.log_exception(_moduleLogger)
182 def _on_success(self, data):
187 self._create_child(child, i)
188 for i, child in enumerate(data)
191 _moduleLogger.exception("Translating error")
192 self._children = None
197 @misc_utils.log_exception(_moduleLogger)
198 def _on_error(self, error):
204 class LeafNode(Node):
206 def __init__(self, connection, parent, data, id):
207 Node.__init__(self, connection, parent, data, id)
213 def can_navigate(self):
214 raise NotImplementedError("On %s" % type(self))
218 raise NotImplementedError("On %s" % type(self))
222 raise NotImplementedError("On %s" % type(self))
224 def _get_children(self, on_success, on_error):
225 raise RuntimeError("Not is a leaf")
228 class RadioNode(ParentNode):
230 def __init__(self, connection):
231 ParentNode.__init__(self, connection, None, {}, SOURCE_RADIO)
238 return "get_radio_channels", (), {}
240 def _create_child(self, data, id):
241 return RadioChannelNode(self._connection, self, data, id)
244 class RadioChannelNode(LeafNode):
246 def __init__(self, connection, parent, data, id):
247 LeafNode.__init__(self, connection, parent, data, id)
248 self._extendedData = {}
252 def can_navigate(self):
265 return self._data["url"]
267 def get_programming(self, date, on_success, on_error):
268 date = date.strftime("%Y-%m-%d")
270 programming = self._extendedData[date]
272 self._get_programming(date, on_success, on_error)
274 on_success(programming)
276 def _get_programming(self, date, on_success, on_error):
277 assert self._request is None
278 assert date not in self._extendedData
279 self._request = on_success, on_error, date
281 self._connection.download(
282 "get_radio_channel_programming",
285 (self._data["id"], date),
289 @misc_utils.log_exception(_moduleLogger)
290 def _on_success(self, data):
295 self._extendedData[date] = [
300 _moduleLogger.exception("Translating error")
301 del self._extendedData[date]
304 r[0](self._extendedData[date])
306 @misc_utils.log_exception(_moduleLogger)
307 def _on_error(self, error):
313 class ConferencesNode(ParentNode):
315 def __init__(self, connection, langId):
316 ParentNode.__init__(self, connection, None, {}, SOURCE_CONFERENCES)
317 self._langId = langId
324 return "get_conferences", (self._langId, ), {}
326 def _create_child(self, data, id):
327 return ConferenceNode(self._connection, self, data, id)
330 class ConferenceNode(ParentNode):
332 def __init__(self, connection, parent, data, id):
333 ParentNode.__init__(self, connection, parent, data, id)
337 return self._data["title"]
340 return "get_conference_sessions", (self._data["id"], ), {}
342 def _create_child(self, data, id):
343 return SessionNode(self._connection, self, data, id)
346 class SessionNode(ParentNode):
348 def __init__(self, connection, parent, data, id):
349 ParentNode.__init__(self, connection, parent, data, id)
353 return self._data["title"]
356 return "get_conference_talks", (self._data["id"], ), {}
358 def _create_child(self, data, id):
359 return TalkNode(self._connection, self, data, id)
362 class TalkNode(LeafNode):
364 def __init__(self, connection, parent, data, id):
365 LeafNode.__init__(self, connection, parent, data, id)
368 def can_navigate(self):
373 return self._data["title"]
377 speaker = self._data["speaker"]
378 if speaker is not None:
385 return self._data["url"]
388 class MagazinesNode(ParentNode):
390 def __init__(self, connection, langId):
391 ParentNode.__init__(self, connection, None, {}, SOURCE_MAGAZINES)
392 self._langId = langId
399 return "get_magazines", (self._langId, ), {}
401 def _create_child(self, data, id):
402 return MagazineNode(self._connection, self, data, id)
405 class MagazineNode(ParentNode):
407 def __init__(self, connection, parent, data, id):
408 ParentNode.__init__(self, connection, parent, data, id)
412 return self._data["title"]
415 return "get_magazine_issues", (self._data["id"], ), {}
417 def _create_child(self, data, id):
418 return IssueNode(self._connection, self, data, id)
421 class IssueNode(ParentNode):
423 def __init__(self, connection, parent, data, id):
424 ParentNode.__init__(self, connection, parent, data, id)
428 return self._data["title"]
431 return "get_magazine_articles", (self._data["id"], ), {}
433 def _create_child(self, data, id):
434 return ArticleNode(self._connection, self, data, id)
437 class ArticleNode(LeafNode):
439 def __init__(self, connection, parent, data, id):
440 LeafNode.__init__(self, connection, parent, data, id)
443 def can_navigate(self):
448 return self._data["title"]
452 speaker = self._data["author"]
453 if speaker is not None:
460 return self._data["url"]
463 class ScripturesNode(ParentNode):
465 def __init__(self, connection, langId):
466 ParentNode.__init__(self, connection, None, {}, SOURCE_SCRIPTURES)
467 self._langId = langId
474 return "get_scriptures", (self._langId, ), {}
476 def _create_child(self, data, id):
477 return ScriptureNode(self._connection, self, data, id)
480 class ScriptureNode(ParentNode):
482 def __init__(self, connection, parent, data, id):
483 ParentNode.__init__(self, connection, parent, data, id)
487 return self._data["title"]
490 return "get_scripture_books", (self._data["id"], ), {}
492 def _create_child(self, data, id):
493 return BookNode(self._connection, self, data, id)
496 class BookNode(ParentNode):
498 def __init__(self, connection, parent, data, id):
499 ParentNode.__init__(self, connection, parent, data, id)
503 return self._data["title"]
506 return "get_scripture_chapters", (self._data["id"], ), {}
508 def _create_child(self, data, id):
509 return ChapterNode(self._connection, self, data, id)
512 class ChapterNode(LeafNode):
514 def __init__(self, connection, parent, data, id):
515 LeafNode.__init__(self, connection, parent, data, id)
518 def can_navigate(self):
523 return self._data["title"]
531 return self._data["url"]
534 def walk_ancestors(node):
538 node = node.get_parent()
543 def common_paths(targetNode, currentNode):
544 targetNodePath = list(walk_ancestors(targetNode))
545 targetNodePath.reverse()
546 currentNodePath = list(walk_ancestors(currentNode))
547 currentNodePath.reverse()
552 for i, (t, c) in enumerate(zip(targetNodePath, currentNodePath)):
554 return ancestors, None, descendants
559 for child in targetNodePath[i+1:]
562 return ancestors, currentNode, descendants
565 class AsyncWalker(object):
567 def __init__(self, func):
571 def start(self, *args, **kwds):
572 assert self._run is None
573 self._run = self._func(*args, **kwds)
574 node = self._run.send(None) # priming the function
575 node.get_children(self.on_success, self.on_error)
577 @misc_utils.log_exception(_moduleLogger)
578 def on_success(self, children):
579 _moduleLogger.debug("Processing success for: %r", self._func)
581 node = self._run.send(children)
582 except StopIteration, e:
585 node.get_children(self.on_success, self.on_error)
587 @misc_utils.log_exception(_moduleLogger)
588 def on_error(self, error):
589 _moduleLogger.debug("Processing error for: %r", self._func)
591 node = self._run.throw(error)
592 except StopIteration, e:
595 node.get_children(self.on_success, self.on_error)
598 def get_next(node, on_success, on_error):
600 assert node.is_leaf(), node
605 parent = childNode.get_parent()
606 siblings = yield parent
607 for i, sibling in enumerate(siblings):
608 if sibling is childNode:
611 if i < len(siblings):
612 sibling = siblings[i]
617 # dig into that branch to find the first leaf
624 children = yield child
625 nodes[0:0] = children
626 raise RuntimeError("Ran out of nodes when hunting for first leaf of %s" % node)
631 def get_previous(node, on_success, on_error):
633 assert node.is_leaf(), node
638 parent = childNode.get_parent()
639 siblings = yield parent
640 for i, sibling in enumerate(siblings):
641 if sibling is childNode:
645 sibling = siblings[i]
650 # dig into that branch to find the first leaf
653 child = nodes.pop(-1)
657 children = yield child
658 nodes[0:0] = children
659 raise RuntimeError("Ran out of nodes when hunting for first leaf of %s" % node)