larigira/larigira/rpc.py

201 lines
6.1 KiB
Python
Raw Normal View History

import gc
2020-04-23 15:27:17 +02:00
import logging
from copy import deepcopy
2014-10-26 20:13:32 +01:00
2020-04-23 15:27:17 +02:00
from flask import (Blueprint, Flask, abort, current_app, jsonify, redirect,
render_template, request)
2024-08-25 23:56:31 +02:00
from flask_babel import Babel
2020-04-23 15:27:17 +02:00
from flask_bootstrap import Bootstrap
2025-04-15 18:13:33 +02:00
from flask_wtf.csrf import CSRFProtect
2020-04-23 15:28:28 +02:00
from cachelib import SimpleCache
2020-04-23 15:27:17 +02:00
from greenlet import greenlet
2015-02-01 19:23:33 +01:00
from .config import get_conf
2020-04-23 15:27:17 +02:00
from .dbadmin import db
2019-06-25 13:43:01 +02:00
rpc = Blueprint("rpc", __name__, url_prefix=get_conf()["ROUTE_PREFIX"] + "/api")
viewui = Blueprint(
"view",
__name__,
url_prefix=get_conf()["ROUTE_PREFIX"] + "/view",
template_folder="templates",
)
2014-10-26 20:13:32 +01:00
def send_to_parent(kind, *args):
2019-06-25 13:43:01 +02:00
"""similar to the behaviour of a ParentedLet"""
if not hasattr(current_app, "queue"):
logging.debug("no parent queue; aborting send")
2014-10-26 20:13:32 +01:00
return
msg = {
2019-06-25 13:43:01 +02:00
"emitter": current_app._get_current_object(),
"class": current_app._get_current_object().__class__.__name__,
"kind": kind,
"args": args,
2014-10-26 20:13:32 +01:00
}
current_app.queue.put(msg)
2019-06-25 13:43:01 +02:00
@rpc.route("/")
2014-10-26 20:13:32 +01:00
def rpc_index():
rules = list(current_app.url_map.iter_rules())
2019-06-25 13:43:01 +02:00
return jsonify({"rules": [r.rule for r in rules if r.rule.startswith("/api")]})
2014-10-26 20:13:32 +01:00
2019-06-25 13:43:01 +02:00
@rpc.route("/refresh")
2014-10-26 20:13:32 +01:00
def rpc_refresh():
2019-06-25 13:43:01 +02:00
send_to_parent("refresh")
return jsonify(dict(status="ok"))
2019-06-25 13:43:01 +02:00
@rpc.route("/audiospec", methods=["GET"])
def get_audiospec():
return jsonify(current_app.larigira.controller.player.continous_audiospec)
2019-06-25 13:43:01 +02:00
@rpc.route("/audiospec", methods=["PUT"])
def change_audiospec():
player = current_app.larigira.controller.player
if request.json is None:
abort(400, "Must send application/json data")
2019-06-25 13:43:01 +02:00
if "spec" not in request.json or type(request.json["spec"]) is not dict:
abort(400, "Object must have a key 'spec' whose value is an object")
2019-06-25 13:43:01 +02:00
player.continous_audiospec = request.json["spec"]
if "kind" not in request.json["spec"]:
abort(400, "invalid audiospec")
return jsonify(player.continous_audiospec)
2019-06-25 13:43:01 +02:00
@rpc.route("/audiospec", methods=["DELETE"])
def reset_audiospec():
player = current_app.larigira.controller.player
player.continous_audiospec = None
return jsonify(player.continous_audiospec)
2019-06-25 13:43:01 +02:00
@rpc.route("/eventsenabled/toggle", methods=["POST"])
def toggle_events_enabled():
status = current_app.larigira.controller.player.events_enabled
current_app.larigira.controller.player.events_enabled = not status
return jsonify(dict(events_enabled=not status))
2019-06-25 13:43:01 +02:00
@rpc.route("/eventsenabled", methods=["GET"])
def get_events_enabled():
status = current_app.larigira.controller.player.events_enabled
return jsonify(dict(events_enabled=status))
2019-06-25 13:43:01 +02:00
@rpc.route("/eventsenabled", methods=["PUT"])
def set_events_enabled():
player = current_app.larigira.controller.player
if request.json is None:
abort(400, "Must send application/json data")
if type(request.json) is not bool:
abort(400, "Content must be a JSON boolean")
player.events_enabled = request.json
return jsonify(dict(events_enabled=request.json))
2015-02-01 00:08:56 +01:00
def get_scheduled_audiogen():
larigira = current_app.larigira
running = larigira.controller.monitor.running
2015-02-01 00:08:56 +01:00
events = {t: {} for t in running.keys()}
for timespec_eid in events:
orig_info = running[timespec_eid]
info = events[timespec_eid]
2019-06-25 13:43:01 +02:00
info["running_time"] = orig_info["running_time"].isoformat()
info["audiospecs"] = orig_info["audiospecs"]
info["timespec"] = orig_info["timespec"]
info["timespec"]["actions"] = {
aid: spec
for aid, spec in zip(info["timespec"]["actions"], info["audiospecs"])
}
info["greenlet"] = hex(id(orig_info["greenlet"]))
2015-02-01 00:08:56 +01:00
return events
2019-06-25 13:43:01 +02:00
@viewui.route("/status/running")
def ui_wip():
audiogens = get_scheduled_audiogen()
2019-06-25 13:43:01 +02:00
return render_template(
"running.html",
audiogens=sorted(audiogens.items(), key=lambda x: x[1]["running_time"]),
)
2019-06-25 13:43:01 +02:00
@rpc.route("/debug/running")
def rpc_wip():
def treeify(flat):
2019-06-25 13:43:01 +02:00
roots = [obid for obid in flat if flat[obid]["parent"] not in flat]
tree = deepcopy(flat)
for obid in tree:
2019-06-25 13:43:01 +02:00
tree[obid]["children"] = {}
to_remove = []
for obid in tree:
if obid in roots:
continue
if obid not in tree:
2019-06-25 13:43:01 +02:00
current_app.logger.warning("How strange, {} not in tree".format(obid))
continue
2019-06-25 13:43:01 +02:00
tree[tree[obid]["parent"]]["children"][obid] = tree[obid]
to_remove.append(obid)
for obid in to_remove:
del tree[obid]
return tree
greenlets = {}
2019-06-25 13:43:01 +02:00
for ob in filter(lambda obj: isinstance(obj, greenlet), gc.get_objects()):
objrepr = {"repr": repr(ob), "class": ob.__class__.__name__}
if hasattr(ob, "parent_greenlet") and ob.parent_greenlet is not None:
objrepr["parent"] = hex(id(ob.parent_greenlet))
else:
2019-06-25 13:43:01 +02:00
objrepr["parent"] = hex(id(ob.parent)) if ob.parent is not None else None
if hasattr(ob, "doc"):
objrepr["doc"] = ob.doc.split("\n")[0]
elif ob.__doc__:
2019-06-25 13:43:01 +02:00
objrepr["doc"] = ob.__doc__.split("\n")[0]
greenlets[hex(id(ob))] = objrepr
# TODO: make it a tree
2019-06-25 13:43:01 +02:00
return jsonify(
dict(
greenlets=greenlets,
greenlets_tree=treeify(greenlets),
audiogens=get_scheduled_audiogen(),
)
)
2014-10-26 20:13:32 +01:00
2019-02-09 23:27:47 +01:00
def babel_get_locale():
try:
if request.accept_languages:
return request.accept_languages[0][0]
finally:
return None
2019-02-09 23:27:47 +01:00
2015-02-01 00:08:13 +01:00
def create_app(queue, larigira):
2019-06-25 13:43:01 +02:00
app = Flask("larigira", static_url_path=get_conf()["ROUTE_PREFIX"] + "/static")
2015-02-01 19:23:33 +01:00
app.config.update(get_conf())
Bootstrap(app)
2019-02-09 23:27:47 +01:00
babel = Babel(app)
2024-08-26 00:19:07 +02:00
babel.init_app(app, locale_selector=babel_get_locale)
2025-04-15 18:13:33 +02:00
csrf = CSRFProtect(app)
csrf.exempt(db)
2014-10-26 20:13:32 +01:00
app.register_blueprint(rpc)
app.register_blueprint(viewui)
app.register_blueprint(db)
2019-07-08 10:48:25 +02:00
app.route(get_conf()["ROUTE_PREFIX"] + "/")(
2019-06-25 13:43:01 +02:00
lambda: redirect(get_conf()["ROUTE_PREFIX"] + get_conf()["HOME_URL"])
)
2014-10-26 20:13:32 +01:00
app.queue = queue
2015-02-01 00:08:13 +01:00
app.larigira = larigira
2017-01-03 19:22:49 +01:00
app.cache = SimpleCache()
2014-10-26 20:13:32 +01:00
return app