Add actions to feed view

This commit is contained in:
Tim Van Baak 2023-06-01 17:03:30 -07:00
parent 1b16a48f31
commit dd114ab0ae
4 changed files with 32 additions and 21 deletions

View File

@ -5,7 +5,7 @@ import time
from flask import Flask, render_template, request, jsonify, abort, redirect, url_for from flask import Flask, render_template, request, jsonify, abort, redirect, url_for
from intake.source import LocalSource from intake.source import LocalSource, execute_action
# Globals # Globals
app = Flask(__name__) app = Flask(__name__)
@ -70,7 +70,7 @@ def source_feed(source_name):
if count_arg.isdigit() and page_arg.isdigit(): if count_arg.isdigit() and page_arg.isdigit():
count = int(count_arg) count = int(count_arg)
page = int(page_arg) page = int(page_arg)
sorted_items = sorted_items[count * page:count * page + count] sorted_items = sorted_items[count * page : count * page + count]
return render_template( return render_template(
"feed.jinja2", "feed.jinja2",
@ -126,6 +126,13 @@ def mass_deactivate():
return jsonify({}) return jsonify({})
@app.post("/action/<string:source_name>/<string:item_id>/<string:action>")
def action(source_name, item_id, action):
source = LocalSource(intake_data_dir(), source_name)
item = execute_action(source, item_id, action)
return jsonify(item)
def wsgi(): def wsgi():
# init_default_logging() # init_default_logging()
return app return app

View File

@ -75,7 +75,7 @@ class LocalSource:
yield json.loads(filepath.read_text(encoding="utf8")) yield json.loads(filepath.read_text(encoding="utf8"))
def read_stdout(process: Popen, output: list): def _read_stdout(process: Popen, output: list) -> None:
""" """
Read the subprocess's stdout into memory. Read the subprocess's stdout into memory.
This prevents the process from blocking when the pipe fills up. This prevents the process from blocking when the pipe fills up.
@ -89,7 +89,7 @@ def read_stdout(process: Popen, output: list):
break break
def read_stderr(process: Popen): def _read_stderr(process: Popen) -> None:
""" """
Read the subprocess's stderr stream and pass it to logging. Read the subprocess's stderr stream and pass it to logging.
This prevents the process from blocking when the pipe fills up. This prevents the process from blocking when the pipe fills up.
@ -102,12 +102,12 @@ def read_stderr(process: Popen):
break break
def execute_source_action( def _execute_source_action(
source: LocalSource, action: str, input: str, timeout: timedelta source: LocalSource, action: str, input: str, timeout: timedelta
): ) -> List[str]:
""" """
Execute the action from a given source. If stdin is specified, pass it Execute the action from a given source. If stdin is specified, pass it
along to the process. along to the process. Returns lines from stdout.
""" """
# Gather the information necessary to launch the process # Gather the information necessary to launch the process
config = source.get_config() config = source.get_config()
@ -141,9 +141,9 @@ def execute_source_action(
# Kick off monitoring threads # Kick off monitoring threads
output = [] output = []
t_stdout: Thread = Thread(target=read_stdout, args=(process, output), daemon=True) t_stdout: Thread = Thread(target=_read_stdout, args=(process, output), daemon=True)
t_stdout.start() t_stdout.start()
t_stderr: Thread = Thread(target=read_stderr, args=(process,), daemon=True) t_stderr: Thread = Thread(target=_read_stderr, args=(process,), daemon=True)
t_stderr.start() t_stderr.start()
# Send input to the process, if provided # Send input to the process, if provided
@ -168,7 +168,7 @@ def execute_source_action(
return output return output
def fetch_items(source: LocalSource, timeout: int = 60): def fetch_items(source: LocalSource, timeout: int = 60) -> List[dict]:
""" """
Execute the feed source and return the current feed items. Execute the feed source and return the current feed items.
Returns a list of feed items on success. Returns a list of feed items on success.
@ -176,7 +176,7 @@ def fetch_items(source: LocalSource, timeout: int = 60):
""" """
items = [] items = []
output = execute_source_action(source, "fetch", None, timedelta(timeout)) output = _execute_source_action(source, "fetch", None, timedelta(timeout))
for line in output: for line in output:
try: try:
@ -188,13 +188,17 @@ def fetch_items(source: LocalSource, timeout: int = 60):
return items return items
def execute_action(source: LocalSource, item_id: str, action: str, timeout: int = 60): def execute_action(
source: LocalSource, item_id: str, action: str, timeout: int = 60
) -> dict:
""" """
Execute the action for a feed source. Execute the action for a feed source.
""" """
item = source.get_item(item_id) item = source.get_item(item_id)
output = execute_source_action(source, action, json.dumps(item), timedelta(timeout)) output = _execute_source_action(
source, action, json.dumps(item), timedelta(timeout)
)
if not output: if not output:
raise SourceUpdateException("no item") raise SourceUpdateException("no item")

View File

@ -99,14 +99,13 @@ var mdeactivate = function (items) {
location.reload(); location.reload();
}); });
}; };
var callback = function (source, itemid) { var doAction = function (source, itemid, action) {
document.getElementById(source + "-" + itemid + "-callback").disabled = true; document.getElementById(`${source}-${itemid}-action-${action}`).disabled = true;
fetch('/callback/', { fetch(`/action/${source}/${itemid}/${action}`, {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json; charset=UTF-8', 'Content-Type': 'application/json; charset=UTF-8',
}, },
body: JSON.stringify({source: source, itemid: itemid}),
}) })
.then(function (data) { .then(function (data) {
location.reload() location.reload()
@ -130,15 +129,15 @@ var callback = function (source, itemid) {
{% endif %} {% endif %}
{# The item title is a clickable <summary> if there is body content #} {# The item title is a clickable <summary> if there is body content #}
{% if item.body or item.callback %} {% if item.body or item.action %}
<details> <details>
<summary><span class="item-title">{{item.title}}</span></summary> <summary><span class="item-title">{{item.title}}</span></summary>
{% if item.body %} {% if item.body %}
<p>{{item.body|safe}}</p> <p>{{item.body|safe}}</p>
{% endif %} {% endif %}
{% if item.callback %} {% for action in item.action %}
<p><button id="{{item.source}}-{{item.id}}-callback" onclick="javascript:callback('{{item.source}}', '{{item.id}}')">Callback</button></p> <p><button id="{{item.source}}-{{item.id}}-action-{{action}}" onclick="javascript:doAction('{{item.source}}', '{{item.id}}', '{{action}}')">{{action}}</button></p>
{% endif %} {% endfor %}
</details> </details>
{% else %} {% else %}
<span class="item-title">{{item.title}}</span><br> <span class="item-title">{{item.title}}</span><br>

View File

@ -20,5 +20,6 @@ if args.action == "increment":
item = sys.stdin.readline() item = sys.stdin.readline()
item = json.loads(item) item = json.loads(item)
item["action"]["increment"] += 1 item["action"]["increment"] += 1
item["body"] = f"<p>{item['action']['increment']}</p>"
print(json.dumps(item)) print(json.dumps(item))
pass pass