From b60655ed549eb8a2d8d8bc80703f51794cc64b4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9gis=20Behmo?= Date: Tue, 12 Aug 2025 21:30:24 +0200 Subject: [PATCH] fix: minor comments, TODO items and formatting This has almost no impact, outside of rewording some items. --- tutordeck/server/app.py | 22 ++++++++----- .../server/templates/_plugin_store_list.html | 2 +- tutordeck/server/templates/advanced.html | 4 --- tutordeck/server/templates/local_launch.html | 2 +- tutordeck/server/templates/plugin.html | 3 +- .../server/templates/plugin_installed.html | 2 +- tutordeck/server/tutorclient.py | 31 +++++++++++-------- 7 files changed, 37 insertions(+), 29 deletions(-) diff --git a/tutordeck/server/app.py b/tutordeck/server/app.py index 7570e2c..8005665 100644 --- a/tutordeck/server/app.py +++ b/tutordeck/server/app.py @@ -17,8 +17,8 @@ from quart import ( request, url_for, ) -from quart.helpers import WerkzeugResponse from quart.typing import ResponseTypes +from werkzeug.sansio.response import Response as BaseResponse from tutor.plugins.v1 import discover_package from tutordeck.server.utils import current_page_plugins, pagination_context @@ -80,7 +80,7 @@ async def plugin_store_list() -> str: "name": p.name, "url": p.url, "index": p.index, - "author": p.author.split("<")[0].strip(), + "author": tutorclient.Client.get_plugin_author(p), "description": p.short_description, "is_installed": p.name in g.installed_plugins, "is_enabled": p.name in g.enabled_plugins, @@ -102,13 +102,16 @@ async def plugin_store_list() -> str: @app.get("/plugin/installed/list") async def plugin_installed_list() -> str: + # TODO IMPORTANT this displays only the plugins that are in the store. When a plugin + # is installed locally but not in the store, we must display it here anyway. + # TODO this is duplicated code from plugin_store_list search_query = request.args.get("search", "") plugins: list[dict[str, t.Any]] = [ { "name": p.name, "url": p.url, "index": p.index, - "author": p.author.split("<")[0].strip(), + "author": tutorclient.Client.get_plugin_author(p), "description": p.short_description, "is_enabled": p.name in g.enabled_plugins, } @@ -129,15 +132,16 @@ async def plugin(name: str) -> Response: if not index_entry: return Response("Plugin not found", status=404) + # TODO this seq_command_executed argument is confusing and causing issues, for + # instance with the "unset" button. We need to get rid of it. seq_command_executed = request.args.get("seq_command_executed") - author = index_entry.author.split("<")[0].strip() description = markdown(index_entry.description) rendered_template = await render_template( "plugin.html", plugin_name=name, is_enabled=name in g.enabled_plugins, is_installed=name in g.installed_plugins, - author_name=author, + author_name=tutorclient.Client.get_plugin_author(index_entry), plugin_description=description, seq_command_executed=seq_command_executed, plugin_config_unique=tutorclient.Client.plugin_config_unique(name), @@ -146,6 +150,7 @@ async def plugin(name: str) -> Response: ) # Redirect to plugin page + # TODO this is useful only after a POST to plugin//update. I don't think these two things should be handled in the same place. response = Response(rendered_template, status=200, content_type="text/html") response.headers["HX-Redirect"] = url_for( "plugin", name=name, seq_command_executed=seq_command_executed @@ -188,11 +193,12 @@ async def plugin_toggle(name: str) -> Response: @app.post("/plugin//install") -async def plugin_install(name: str) -> WerkzeugResponse: +async def plugin_install(name: str) -> BaseResponse: async def bg_install_and_reload() -> None: tutorclient.CliPool.run_parallel(app, ["plugins", "install", name]) while tutorclient.CliPool.THREAD and tutorclient.CliPool.THREAD.is_alive(): await asyncio.sleep(0.1) + # TODO this is hackish. How can we improve? discover_package(importlib_metadata.entry_points().__getitem__(name)) asyncio.create_task(bg_install_and_reload()) @@ -205,7 +211,7 @@ async def plugin_install(name: str) -> WerkzeugResponse: @app.post("/plugin//upgrade") -async def plugin_upgrade(name: str) -> WerkzeugResponse: +async def plugin_upgrade(name: str) -> BaseResponse: tutorclient.CliPool.run_parallel(app, ["plugins", "upgrade", name]) return redirect( url_for( @@ -344,7 +350,7 @@ async def suggest() -> Response: @app.post("/command") -async def command() -> WerkzeugResponse: +async def command() -> BaseResponse: form = await request.form command_string = form.get("command", "") command_args = command_string.split() diff --git a/tutordeck/server/templates/_plugin_store_list.html b/tutordeck/server/templates/_plugin_store_list.html index dbf06ee..a171805 100644 --- a/tutordeck/server/templates/_plugin_store_list.html +++ b/tutordeck/server/templates/_plugin_store_list.html @@ -46,7 +46,7 @@ {% endif %} - {% for page_number in range(1, pagination.total_pages + 1) %} + {% for page_number in range(1, pagination.total_pages + 1) %}
{{ page_number }} diff --git a/tutordeck/server/templates/advanced.html b/tutordeck/server/templates/advanced.html index 1e2c5d5..306c729 100644 --- a/tutordeck/server/templates/advanced.html +++ b/tutordeck/server/templates/advanced.html @@ -8,16 +8,12 @@ Developer Mode Search for any tutor command and execute it with a single click. {% endblock %} -{% block page_button %} -{% endblock %} - {% block searchbar %} {% endblock %} {% block warning %} {% endblock %} - {% set sidebar_active_tab = "advanced" %} {% block workspace_content %} diff --git a/tutordeck/server/templates/local_launch.html b/tutordeck/server/templates/local_launch.html index 852f7ea..3caa7cc 100644 --- a/tutordeck/server/templates/local_launch.html +++ b/tutordeck/server/templates/local_launch.html @@ -39,4 +39,4 @@ This will run Launch Platform to apply all plugin changes. This may take a few m } ShowRunCommandButton(); -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/tutordeck/server/templates/plugin.html b/tutordeck/server/templates/plugin.html index 5b70596..1154534 100644 --- a/tutordeck/server/templates/plugin.html +++ b/tutordeck/server/templates/plugin.html @@ -40,7 +40,7 @@ {% else %}

N/A

{% endif %} - + {% endif %} @@ -79,6 +79,7 @@ bar.style.display = isPluginInstalled ? 'flex' : 'none'; } async function checkIfPluginInstalled(pluginName) { + // TODO let's avoid hard-coded urls const response = await fetch(`/plugin/${pluginName}/is-installed`); const data = await response.json(); return data.installed; diff --git a/tutordeck/server/templates/plugin_installed.html b/tutordeck/server/templates/plugin_installed.html index 1522256..7db3d6f 100644 --- a/tutordeck/server/templates/plugin_installed.html +++ b/tutordeck/server/templates/plugin_installed.html @@ -9,7 +9,7 @@ View all your installed plugins in one place. {% endblock %} {% block page_button %} - + {% endblock %} {% set sidebar_active_tab = "my-plugins" %} diff --git a/tutordeck/server/tutorclient.py b/tutordeck/server/tutorclient.py index fe43f8a..b78b73b 100644 --- a/tutordeck/server/tutorclient.py +++ b/tutordeck/server/tutorclient.py @@ -114,7 +114,8 @@ class Cli: self.log_to_file(e.args[0]) self.log_to_file("\nCancelled!\n") except SystemExit: - # TODO Is there a better way to notify command completion??? + # TODO Is there a better way to notify command completion??? The + # frontend relies on this hard-coded string to detect launch completion. self.log_to_file("\nSuccess!") def stop(self) -> None: @@ -326,9 +327,7 @@ class Client: @classmethod def plugins_matching_pattern(cls, pattern: str) -> list[str]: return [ - plugin._data["name"] - for plugin in cls.plugins_in_store() - if plugin.match(pattern) + plugin.name for plugin in cls.plugins_in_store() if plugin.match(pattern) ] @classmethod @@ -356,19 +355,25 @@ class Client: key: user_config.get(key, value) for key, value in config_defaults.items() } + @classmethod + def get_plugin_author(cls, index_entry: tutor.plugins.indexes.IndexEntry) -> str: + return index_entry.author.split("<")[0].strip() + @classmethod def autocomplete(cls, partial_command: str) -> list[dict[str, str]]: + """ + Handle CLI command completion via click_repl.ClickCompleter + https://github.com/click-contrib/click-repl/blob/master/click_repl/_completer.py + """ cli = tutor.commands.cli.cli ctx = click.Context(cli, info_name=cli.name, parent=None) completer = click_repl.ClickCompleter(cli, ctx) document = Document(partial_command, len(partial_command)) completions = list(completer.get_completions(document, None)) - suggestions = [] - for completion in completions: - suggestions.append( - { - "text": completion.text, - "display": completion.display, - } - ) - return suggestions + return [ + { + "text": completion.text, + "display": completion.display, + } + for completion in completions + ]