rpc.py 5.9 KB

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