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)
158 def _get_children(self, on_success, on_error):
159 assert self._children is None
161 func, args, kwds = self._get_func()
163 self._connection.download(
165 lambda data: self._on_success(data, on_success, on_error),
172 raise NotImplementedError()
174 def _create_child(self, data, id):
175 raise NotImplementedError()
177 @misc_utils.log_exception(_moduleLogger)
178 def _on_success(self, data, on_success, on_error):
181 self._create_child(child, i)
182 for i, child in enumerate(data)
185 _moduleLogger.exception("Translating error")
186 self._children = None
189 on_success(self._children)
192 class LeafNode(Node):
194 def __init__(self, connection, parent, data, id):
195 Node.__init__(self, connection, parent, data, id)
201 def can_navigate(self):
202 raise NotImplementedError("On %s" % type(self))
206 raise NotImplementedError("On %s" % type(self))
210 raise NotImplementedError("On %s" % type(self))
212 def _get_children(self, on_success, on_error):
213 raise RuntimeError("Not is a leaf")
216 class RadioNode(ParentNode):
218 def __init__(self, connection):
219 ParentNode.__init__(self, connection, None, {}, SOURCE_RADIO)
226 return "get_radio_channels", (), {}
228 def _create_child(self, data, id):
229 return RadioChannelNode(self._connection, self, data, id)
232 class RadioChannelNode(LeafNode):
234 def __init__(self, connection, parent, data, id):
235 LeafNode.__init__(self, connection, parent, data, id)
236 self._extendedData = {}
240 def can_navigate(self):
253 return self._data["url"]
255 def get_programming(self, date, on_success, on_error):
256 date = date.strftime("%Y-%m-%d")
258 programming = self._extendedData[date]
260 self._get_programming(date, on_success, on_error)
262 on_success(programming)
264 def _get_programming(self, date, on_success, on_error):
265 assert self._request is None
266 assert date not in self._extendedData
267 self._request = on_success, on_error, date
269 self._connection.download(
270 "get_radio_channel_programming",
273 (self._data["id"], date),
277 @misc_utils.log_exception(_moduleLogger)
278 def _on_success(self, data):
283 self._extendedData[date] = [
288 _moduleLogger.exception("Translating error")
289 del self._extendedData[date]
292 r[0](self._extendedData[date])
294 @misc_utils.log_exception(_moduleLogger)
295 def _on_error(self, error):
301 class ConferencesNode(ParentNode):
303 def __init__(self, connection, langId):
304 ParentNode.__init__(self, connection, None, {}, SOURCE_CONFERENCES)
305 self._langId = langId
312 return "get_conferences", (self._langId, ), {}
314 def _create_child(self, data, id):
315 return ConferenceNode(self._connection, self, data, id)
318 class ConferenceNode(ParentNode):
320 def __init__(self, connection, parent, data, id):
321 ParentNode.__init__(self, connection, parent, data, id)
325 return self._data["title"]
328 return "get_conference_sessions", (self._data["id"], ), {}
330 def _create_child(self, data, id):
331 return SessionNode(self._connection, self, data, id)
334 class SessionNode(ParentNode):
336 def __init__(self, connection, parent, data, id):
337 ParentNode.__init__(self, connection, parent, data, id)
341 return self._data["title"]
344 return "get_conference_talks", (self._data["id"], ), {}
346 def _create_child(self, data, id):
347 return TalkNode(self._connection, self, data, id)
350 class TalkNode(LeafNode):
352 def __init__(self, connection, parent, data, id):
353 LeafNode.__init__(self, connection, parent, data, id)
356 def can_navigate(self):
361 return self._data["title"]
365 speaker = self._data["speaker"]
366 if speaker is not None:
373 return self._data["url"]
376 class MagazinesNode(ParentNode):
378 def __init__(self, connection, langId):
379 ParentNode.__init__(self, connection, None, {}, SOURCE_MAGAZINES)
380 self._langId = langId
387 return "get_magazines", (self._langId, ), {}
389 def _create_child(self, data, id):
390 return MagazineNode(self._connection, self, data, id)
393 class MagazineNode(ParentNode):
395 def __init__(self, connection, parent, data, id):
396 ParentNode.__init__(self, connection, parent, data, id)
400 return self._data["title"]
403 return "get_magazine_issues", (self._data["id"], ), {}
405 def _create_child(self, data, id):
406 return IssueNode(self._connection, self, data, id)
409 class IssueNode(ParentNode):
411 def __init__(self, connection, parent, data, id):
412 ParentNode.__init__(self, connection, parent, data, id)
416 return self._data["title"]
419 return "get_magazine_articles", (self._data["id"], ), {}
421 def _create_child(self, data, id):
422 return ArticleNode(self._connection, self, data, id)
425 class ArticleNode(LeafNode):
427 def __init__(self, connection, parent, data, id):
428 LeafNode.__init__(self, connection, parent, data, id)
431 def can_navigate(self):
436 return self._data["title"]
440 speaker = self._data["author"]
441 if speaker is not None:
448 return self._data["url"]
451 class ScripturesNode(ParentNode):
453 def __init__(self, connection, langId):
454 ParentNode.__init__(self, connection, None, {}, SOURCE_SCRIPTURES)
455 self._langId = langId
462 return "get_scriptures", (self._langId, ), {}
464 def _create_child(self, data, id):
465 return ScriptureNode(self._connection, self, data, id)
468 class ScriptureNode(ParentNode):
470 def __init__(self, connection, parent, data, id):
471 ParentNode.__init__(self, connection, parent, data, id)
475 return self._data["title"]
478 return "get_scripture_books", (self._data["id"], ), {}
480 def _create_child(self, data, id):
481 return BookNode(self._connection, self, data, id)
484 class BookNode(ParentNode):
486 def __init__(self, connection, parent, data, id):
487 ParentNode.__init__(self, connection, parent, data, id)
491 return self._data["title"]
494 return "get_scripture_chapters", (self._data["id"], ), {}
496 def _create_child(self, data, id):
497 return ChapterNode(self._connection, self, data, id)
500 class ChapterNode(LeafNode):
502 def __init__(self, connection, parent, data, id):
503 LeafNode.__init__(self, connection, parent, data, id)
506 def can_navigate(self):
511 return self._data["title"]
519 return self._data["url"]
522 def walk_ancestors(node):
526 node = node.get_parent()
531 def common_paths(targetNode, currentNode):
532 targetNodePath = list(walk_ancestors(targetNode))
533 targetNodePath.reverse()
534 currentNodePath = list(walk_ancestors(currentNode))
535 currentNodePath.reverse()
540 for i, (t, c) in enumerate(zip(targetNodePath, currentNodePath)):
542 return ancestors, None, descendants
547 for child in targetNodePath[i+1:]
550 return ancestors, currentNode, descendants
553 class AsyncWalker(object):
555 def __init__(self, func):
559 def start(self, *args, **kwds):
560 assert self._run is None
561 self._run = self._func(*args, **kwds)
562 node = self._run.send(None) # priming the function
563 node.get_children(self.on_success, self.on_error)
565 @misc_utils.log_exception(_moduleLogger)
566 def on_success(self, children):
567 _moduleLogger.debug("Processing success for: %r", self._func)
569 node = self._run.send(children)
570 except StopIteration, e:
573 node.get_children(self.on_success, self.on_error)
575 @misc_utils.log_exception(_moduleLogger)
576 def on_error(self, error):
577 _moduleLogger.debug("Processing error for: %r", self._func)
579 node = self._run.throw(error)
580 except StopIteration, e:
583 node.get_children(self.on_success, self.on_error)
586 def get_next(node, on_success, on_error):
588 assert node.is_leaf(), node
593 parent = childNode.get_parent()
594 siblings = yield parent
595 for i, sibling in enumerate(siblings):
596 if sibling is childNode:
599 if i < len(siblings):
600 sibling = siblings[i]
605 # dig into that branch to find the first leaf
612 children = yield child
613 nodes[0:0] = children
614 raise RuntimeError("Ran out of nodes when hunting for first leaf of %s" % node)
619 def get_previous(node, on_success, on_error):
621 assert node.is_leaf(), node
626 parent = childNode.get_parent()
627 siblings = yield parent
628 for i, sibling in enumerate(siblings):
629 if sibling is childNode:
633 sibling = siblings[i]
638 # dig into that branch to find the first leaf
641 child = nodes.pop(-1)
645 children = yield child
646 nodes[0:0] = children
647 raise RuntimeError("Ran out of nodes when hunting for first leaf of %s" % node)