rpc.py 5.8 KB

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