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.add_task(
37 getattr(self._backend, func),
45 class AudioIndex(object):
48 self._connection = Connection()
49 self._languages = None
50 self._languagesRequest = None
54 self._connection.start()
57 self._connection.stop()
59 def get_languages(self, on_success, on_error):
60 if self._languages is None:
61 assert self._languagesRequest is None
62 self._languagesRequest = on_success, on_error
63 self._connection.download(
65 self._on_get_languages,
66 self._on_languages_error
69 on_success(self._languages)
71 def get_source(self, source, langId = None):
72 key = (source, langId)
73 if key in self._sources:
74 node = self._sources[key]
76 if source == SOURCE_RADIO:
77 node = RadioNode(self._connection)
78 elif source == SOURCE_CONFERENCES:
79 assert langId is not None
80 node = ConferencesNode(self._connection, langId)
81 elif source == SOURCE_MAGAZINES:
82 assert langId is not None
83 node = MagazinesNode(self._connection, langId)
84 elif source == SOURCE_SCRIPTURES:
85 assert langId is not None
86 node = ScripturesNode(self._connection, langId)
88 raise NotImplementedError(source)
89 self._sources[key] = node
93 @misc_utils.log_exception(_moduleLogger)
94 def _on_get_languages(self, languages):
95 assert self._languages is None
96 assert self._languagesRequest is not None
97 r = self._languagesRequest
98 self._languagesRequest = None
99 self._languages = languages
100 r[0](self._languages)
102 @misc_utils.log_exception(_moduleLogger)
103 def _on_languages_error(self, e):
104 assert self._languages is None
105 assert self._languagesRequest is not None
106 r = self._languagesRequest
107 self._languagesRequest = None
108 r[1](self._languages)
113 def __init__(self, connection, parent, data, id):
114 self._connection = connection
115 self._parent = weakref.ref(parent) if parent is not None else None
117 self._children = None
120 def get_children(self, on_success, on_error):
121 if self._children is None:
122 self._get_children(on_success, on_error)
124 on_success(self._children)
126 def get_parent(self):
127 if self._parent is None:
128 raise RuntimeError("")
129 parent = self._parent()
132 def get_properties(self):
137 raise NotImplementedError("On %s" % type(self))
144 raise NotImplementedError("")
146 def _get_children(self, on_success, on_error):
147 raise NotImplementedError("")
150 class ParentNode(Node):
152 def __init__(self, connection, parent, data, id):
153 Node.__init__(self, connection, parent, data, id)
159 def _get_children(self, on_success, on_error):
160 assert self._request is None
161 assert self._children is None
162 self._request = on_success, on_error
164 func, args, kwds = self._get_func()
166 self._connection.download(
175 raise NotImplementedError()
177 def _create_child(self, data, id):
178 raise NotImplementedError()
180 @misc_utils.log_exception(_moduleLogger)
181 def _on_success(self, data):
186 self._create_child(child, i)
187 for i, child in enumerate(data)
190 _moduleLogger.exception("Translating error")
191 self._children = None
196 @misc_utils.log_exception(_moduleLogger)
197 def _on_error(self, error):
203 class LeafNode(Node):
205 def __init__(self, connection, parent, data, id):
206 Node.__init__(self, connection, parent, data, id)
212 def can_navigate(self):
213 raise NotImplementedError("On %s" % type(self))
217 raise NotImplementedError("On %s" % type(self))
221 raise NotImplementedError("On %s" % type(self))
223 def _get_children(self, on_success, on_error):
224 raise RuntimeError("Not is a leaf")
227 class RadioNode(ParentNode):
229 def __init__(self, connection):
230 ParentNode.__init__(self, connection, None, {}, SOURCE_RADIO)
237 return "get_radio_channels", (), {}
239 def _create_child(self, data, id):
240 return RadioChannelNode(self._connection, self, data, id)
243 class RadioChannelNode(LeafNode):
245 def __init__(self, connection, parent, data, id):
246 LeafNode.__init__(self, connection, parent, data, id)
247 self._extendedData = {}
251 def can_navigate(self):
264 return self._data["url"]
266 def get_programming(self, date, on_success, on_error):
267 date = date.strftime("%Y-%m-%d")
269 programming = self._extendedData[date]
271 self._get_programming(date, on_success, on_error)
273 on_success(programming)
275 def _get_programming(self, date, on_success, on_error):
276 assert self._request is None
277 assert date not in self._extendedData
278 self._request = on_success, on_error, date
280 self._connection.download(
281 "get_radio_channel_programming",
284 (self._data["id"], date),
288 @misc_utils.log_exception(_moduleLogger)
289 def _on_success(self, data):
294 self._extendedData[date] = [
299 _moduleLogger.exception("Translating error")
300 del self._extendedData[date]
303 r[0](self._extendedData[date])
305 @misc_utils.log_exception(_moduleLogger)
306 def _on_error(self, error):
312 class ConferencesNode(ParentNode):
314 def __init__(self, connection, langId):
315 ParentNode.__init__(self, connection, None, {}, SOURCE_CONFERENCES)
316 self._langId = langId
323 return "get_conferences", (self._langId, ), {}
325 def _create_child(self, data, id):
326 return ConferenceNode(self._connection, self, data, id)
329 class ConferenceNode(ParentNode):
331 def __init__(self, connection, parent, data, id):
332 ParentNode.__init__(self, connection, parent, data, id)
336 return self._data["title"]
339 return "get_conference_sessions", (self._data["id"], ), {}
341 def _create_child(self, data, id):
342 return SessionNode(self._connection, self, data, id)
345 class SessionNode(ParentNode):
347 def __init__(self, connection, parent, data, id):
348 ParentNode.__init__(self, connection, parent, data, id)
352 return self._data["title"]
355 return "get_conference_talks", (self._data["id"], ), {}
357 def _create_child(self, data, id):
358 return TalkNode(self._connection, self, data, id)
361 class TalkNode(LeafNode):
363 def __init__(self, connection, parent, data, id):
364 LeafNode.__init__(self, connection, parent, data, id)
367 def can_navigate(self):
372 return self._data["title"]
376 speaker = self._data["speaker"]
377 if speaker is not None:
384 return self._data["url"]
387 class MagazinesNode(ParentNode):
389 def __init__(self, connection, langId):
390 ParentNode.__init__(self, connection, None, {}, SOURCE_MAGAZINES)
391 self._langId = langId
398 return "get_magazines", (self._langId, ), {}
400 def _create_child(self, data, id):
401 return MagazineNode(self._connection, self, data, id)
404 class MagazineNode(ParentNode):
406 def __init__(self, connection, parent, data, id):
407 ParentNode.__init__(self, connection, parent, data, id)
411 return self._data["title"]
414 return "get_magazine_issues", (self._data["id"], ), {}
416 def _create_child(self, data, id):
417 return IssueNode(self._connection, self, data, id)
420 class IssueNode(ParentNode):
422 def __init__(self, connection, parent, data, id):
423 ParentNode.__init__(self, connection, parent, data, id)
427 return self._data["title"]
430 return "get_magazine_articles", (self._data["id"], ), {}
432 def _create_child(self, data, id):
433 return ArticleNode(self._connection, self, data, id)
436 class ArticleNode(LeafNode):
438 def __init__(self, connection, parent, data, id):
439 LeafNode.__init__(self, connection, parent, data, id)
442 def can_navigate(self):
447 return self._data["title"]
451 speaker = self._data["author"]
452 if speaker is not None:
459 return self._data["url"]
462 class ScripturesNode(ParentNode):
464 def __init__(self, connection, langId):
465 ParentNode.__init__(self, connection, None, {}, SOURCE_SCRIPTURES)
466 self._langId = langId
473 return "get_scriptures", (self._langId, ), {}
475 def _create_child(self, data, id):
476 return ScriptureNode(self._connection, self, data, id)
479 class ScriptureNode(ParentNode):
481 def __init__(self, connection, parent, data, id):
482 ParentNode.__init__(self, connection, parent, data, id)
486 return self._data["title"]
489 return "get_scripture_books", (self._data["id"], ), {}
491 def _create_child(self, data, id):
492 return BookNode(self._connection, self, data, id)
495 class BookNode(ParentNode):
497 def __init__(self, connection, parent, data, id):
498 ParentNode.__init__(self, connection, parent, data, id)
502 return self._data["title"]
505 return "get_scripture_chapters", (self._data["id"], ), {}
507 def _create_child(self, data, id):
508 return ChapterNode(self._connection, self, data, id)
511 class ChapterNode(LeafNode):
513 def __init__(self, connection, parent, data, id):
514 LeafNode.__init__(self, connection, parent, data, id)
517 def can_navigate(self):
522 return self._data["title"]
530 return self._data["url"]
533 def walk_ancestors(node):
537 node = node.get_parent()
542 def common_paths(targetNode, currentNode):
543 targetNodePath = list(walk_ancestors(targetNode))
544 targetNodePath.reverse()
545 currentNodePath = list(walk_ancestors(currentNode))
546 currentNodePath.reverse()
551 for i, (t, c) in enumerate(zip(targetNodePath, currentNodePath)):
553 return ancestors, None, descendants
558 for child in targetNodePath[i+1:]
561 return ancestors, currentNode, descendants
564 class AsyncWalker(object):
566 def __init__(self, func):
570 def start(self, *args, **kwds):
571 assert self._run is None
572 self._run = self._func(*args, **kwds)
573 node = self._run.send(None) # priming the function
574 node.get_children(self.on_success, self.on_error)
576 @misc_utils.log_exception(_moduleLogger)
577 def on_success(self, children):
578 _moduleLogger.debug("Processing success for: %r", self._func)
580 node = self._run.send(children)
581 except StopIteration, e:
584 node.get_children(self.on_success, self.on_error)
586 @misc_utils.log_exception(_moduleLogger)
587 def on_error(self, error):
588 _moduleLogger.debug("Processing error for: %r", self._func)
590 node = self._run.throw(error)
591 except StopIteration, e:
594 node.get_children(self.on_success, self.on_error)
597 def get_next(node, on_success, on_error):
599 assert node.is_leaf(), node
604 parent = childNode.get_parent()
605 siblings = yield parent
606 for i, sibling in enumerate(siblings):
607 if sibling is childNode:
610 if i < len(siblings):
611 sibling = siblings[i]
616 # dig into that branch to find the first leaf
623 children = yield child
624 nodes[0:0] = children
625 raise RuntimeError("Ran out of nodes when hunting for first leaf of %s" % node)
630 def get_previous(node, on_success, on_error):
632 assert node.is_leaf(), node
637 parent = childNode.get_parent()
638 siblings = yield parent
639 for i, sibling in enumerate(siblings):
640 if sibling is childNode:
644 sibling = siblings[i]
649 # dig into that branch to find the first leaf
652 child = nodes.pop(-1)
656 children = yield child
657 nodes[0:0] = children
658 raise RuntimeError("Ran out of nodes when hunting for first leaf of %s" % node)