rpc.py 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. import logging
  2. import gc
  3. from copy import deepcopy
  4. from greenlet import greenlet
  5. from flask import current_app, Blueprint, Flask, jsonify, render_template, \
  6. request, abort
  7. from flask_bootstrap import Bootstrap
  8. from flask.ext.babel import Babel
  9. from werkzeug.contrib.cache import SimpleCache
  10. from .dbadmin import db
  11. from .config import get_conf
  12. rpc = Blueprint('rpc', __name__,
  13. url_prefix=get_conf()['ROUTE_PREFIX'] + '/api')
  14. viewui = Blueprint('view', __name__,
  15. url_prefix=get_conf()['ROUTE_PREFIX'] + '/view',
  16. template_folder='templates')
  17. def send_to_parent(kind, *args):
  18. '''similar to the behaviour of a ParentedLet'''
  19. if not hasattr(current_app, 'queue'):
  20. logging.debug('no parent queue; aborting send')
  21. return
  22. msg = {
  23. 'emitter': current_app._get_current_object(),
  24. 'class': current_app._get_current_object().__class__.__name__,
  25. 'kind': kind,
  26. 'args': args
  27. }
  28. current_app.queue.put(msg)
  29. @rpc.route('/')
  30. def rpc_index():
  31. rules = list(current_app.url_map.iter_rules())
  32. return jsonify({'rules': [r.rule for r in rules
  33. if r.rule.startswith('/api')]})
  34. @rpc.route('/refresh')
  35. def rpc_refresh():
  36. send_to_parent('refresh')
  37. return jsonify(dict(status='ok'))
  38. @rpc.route('/audiospec', methods=['GET'])
  39. def get_audiospec():
  40. return jsonify(current_app.larigira.controller.player.continous_audiospec)
  41. @rpc.route('/audiospec', methods=['PUT'])
  42. def change_audiospec():
  43. player = current_app.larigira.controller.player
  44. if request.json is None:
  45. abort(400, "Must send application/json data")
  46. if 'spec' not in request.json or type(request.json['spec']) is not dict:
  47. abort(400, "Object must have a key 'spec' whose value is an object")
  48. player.continous_audiospec = request.json['spec']
  49. if 'kind' not in request.json['spec']:
  50. abort(400, "invalid audiospec")
  51. return jsonify(player.continous_audiospec)
  52. @rpc.route('/audiospec', methods=['DELETE'])
  53. def reset_audiospec():
  54. player = current_app.larigira.controller.player
  55. player.continous_audiospec = None
  56. return jsonify(player.continous_audiospec)
  57. @rpc.route('/eventsenabled/toggle', methods=['POST'])
  58. def toggle_events_enabled():
  59. status = current_app.larigira.controller.player.events_enabled
  60. current_app.larigira.controller.player.events_enabled = not status
  61. return jsonify(dict(events_enabled=not status))
  62. @rpc.route('/eventsenabled', methods=['GET'])
  63. def get_events_enabled():
  64. status = current_app.larigira.controller.player.events_enabled
  65. return jsonify(dict(events_enabled=status))
  66. @rpc.route('/eventsenabled', methods=['PUT'])
  67. def set_events_enabled():
  68. player = current_app.larigira.controller.player
  69. if request.json is None:
  70. abort(400, "Must send application/json data")
  71. if type(request.json) is not bool:
  72. abort(400, "Content must be a JSON boolean")
  73. player.events_enabled = request.json
  74. return jsonify(dict(events_enabled=request.json))
  75. def get_scheduled_audiogen():
  76. larigira = current_app.larigira
  77. running = larigira.controller.monitor.running
  78. events = {t: {} for t in running.keys()}
  79. for timespec_eid in events:
  80. orig_info = running[timespec_eid]
  81. info = events[timespec_eid]
  82. info['running_time'] = orig_info['running_time'].isoformat()
  83. info['audiospecs'] = orig_info['audiospecs']
  84. info['timespec'] = orig_info['timespec']
  85. info['timespec']['actions'] = {aid: spec
  86. for aid, spec
  87. in zip(info['timespec']['actions'],
  88. info['audiospecs'])
  89. }
  90. info['greenlet'] = hex(id(orig_info['greenlet']))
  91. return events
  92. @viewui.route('/status/running')
  93. def ui_wip():
  94. audiogens = get_scheduled_audiogen()
  95. return render_template('running.html',
  96. audiogens=sorted(
  97. audiogens.items(),
  98. key=lambda x: x[1]['running_time'])
  99. )
  100. @rpc.route('/debug/running')
  101. def rpc_wip():
  102. def treeify(flat):
  103. roots = [obid for obid in flat if flat[obid]['parent'] not in flat]
  104. tree = deepcopy(flat)
  105. for obid in tree:
  106. tree[obid]['children'] = {}
  107. to_remove = []
  108. for obid in tree:
  109. if obid in roots:
  110. continue
  111. if obid not in tree:
  112. current_app.logger.warning('How strange, {} not in tree'
  113. .format(obid))
  114. continue
  115. tree[tree[obid]['parent']]['children'][obid] = tree[obid]
  116. to_remove.append(obid)
  117. for obid in to_remove:
  118. del tree[obid]
  119. return tree
  120. greenlets = {}
  121. for ob in filter(lambda obj: isinstance(obj, greenlet),
  122. gc.get_objects()):
  123. objrepr = {
  124. 'repr': repr(ob),
  125. 'class': ob.__class__.__name__,
  126. }
  127. if hasattr(ob, 'parent_greenlet') and ob.parent_greenlet is not None:
  128. objrepr['parent'] = hex(id(ob.parent_greenlet))
  129. else:
  130. objrepr['parent'] = hex(id(ob.parent)) \
  131. if ob.parent is not None else None
  132. if hasattr(ob, 'doc'):
  133. objrepr['doc'] = ob.doc.split('\n')[0]
  134. elif ob.__doc__:
  135. objrepr['doc'] = ob.__doc__.split('\n')[0]
  136. greenlets[hex(id(ob))] = objrepr
  137. # TODO: make it a tree
  138. return jsonify(dict(greenlets=greenlets,
  139. greenlets_tree=treeify(greenlets),
  140. audiogens=get_scheduled_audiogen(),
  141. ))
  142. def babel_get_locale():
  143. try:
  144. if request.accept_languages:
  145. return request.accept_languages[0][0]
  146. finally:
  147. return None
  148. def create_app(queue, larigira):
  149. app = Flask('larigira',
  150. static_url_path=get_conf()['ROUTE_PREFIX'] + '/static')
  151. app.config.update(get_conf())
  152. Bootstrap(app)
  153. babel = Babel(app)
  154. babel.localeselector(babel_get_locale)
  155. app.register_blueprint(rpc)
  156. app.register_blueprint(viewui)
  157. app.register_blueprint(db)
  158. app.queue = queue
  159. app.larigira = larigira
  160. app.cache = SimpleCache()
  161. return app