simple-web-app

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

commit 8947b13c46e88af2725825994bf49f293ff8b054
parent 818ddc0b52103182f28e445d0d743324fce787ca
Author: Silas Brack <silasbrack@gmail.com>
Date:   Sat,  4 Oct 2025 16:57:39 +0200

refactor: use url_for

Diffstat:
Msrc/simple_web_app/app.py | 12++++++------
Msrc/simple_web_app/templates/index.html | 6+++---
Msrc/simple_web_app/templates/load_more.html | 2+-
Msrc/simple_web_app/templates/news_card.html | 2+-
Msrc/simple_web_app/templates/settings.html | 12++++++------
Mtests/test_simple_web_app/test_app.py | 12++++++------
6 files changed, 23 insertions(+), 23 deletions(-)

diff --git a/src/simple_web_app/app.py b/src/simple_web_app/app.py @@ -94,7 +94,7 @@ async def show_home_page(request: Request): page = request.query_params.get("page", default=0) page = int(page) if page is not None else page current_time = request.query_params.get("current_time", default=datetime.datetime.now(tz=datetime.UTC)) - category_id = request.query_params.get("category-id") + category_id = request.query_params.get("category_id") category_id = int(category_id) if category_id is not None else None limit = 5 @@ -175,11 +175,11 @@ async def lifespan(app: Starlette): routes = [ - Route("/", methods=["GET"], endpoint=show_home_page), - Route("/search", methods=["GET"], endpoint=open_search), - Route("/search", methods=["POST"], endpoint=search), - Route("/settings", methods=["GET"], endpoint=open_settings), - Route("/settings/tab", methods=["GET"], endpoint=open_settings_tab), + Route("/", methods=["GET"], endpoint=show_home_page, name="news_page"), + Route("/search", methods=["GET"], endpoint=open_search, name="search_page"), + Route("/search", methods=["POST"], endpoint=search, name="search"), + Route("/settings", methods=["GET"], endpoint=open_settings, name="settings_page"), + Route("/settings/tab", methods=["GET"], endpoint=open_settings_tab, name="settings_tab"), Mount("/static", StaticFiles(directory=STATIC_DIR), name="static"), ] middleware = [ diff --git a/src/simple_web_app/templates/index.html b/src/simple_web_app/templates/index.html @@ -8,9 +8,9 @@ <li><strong style="cursor: pointer;" data-tooltip="Choose another date" data-placement="bottom">Tuesday, September 30</strong></li> </ul> <ul> - <li><a id="search-btn" style="cursor: pointer;" data-tooltip="Search" hx-trigger="click, keyup[ctrlKey&&shiftKey&&key=='O']" hx-get="/search" hx-target="#modal-placeholder" hx-swap="innerHTML" preload class="secondary"><svg height="20pt" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--!Font Awesome Free v7.0.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M480 272C480 317.9 465.1 360.3 440 394.7L566.6 521.4C579.1 533.9 579.1 554.2 566.6 566.7C554.1 579.2 533.8 579.2 521.3 566.7L394.7 440C360.3 465.1 317.9 480 272 480C157.1 480 64 386.9 64 272C64 157.1 157.1 64 272 64C386.9 64 480 157.1 480 272zM272 416C351.5 416 416 351.5 416 272C416 192.5 351.5 128 272 128C192.5 128 128 192.5 128 272C128 351.5 192.5 416 272 416z"/></svg></a></li> + <li><a id="search-btn" style="cursor: pointer;" data-tooltip="Search" hx-trigger="click, keyup[ctrlKey&&shiftKey&&key=='O']" hx-get="{{ url_for('search_page') }}" hx-target="#modal-placeholder" hx-swap="innerHTML" preload class="secondary"><svg height="20pt" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--!Font Awesome Free v7.0.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M480 272C480 317.9 465.1 360.3 440 394.7L566.6 521.4C579.1 533.9 579.1 554.2 566.6 566.7C554.1 579.2 533.8 579.2 521.3 566.7L394.7 440C360.3 465.1 317.9 480 272 480C157.1 480 64 386.9 64 272C64 157.1 157.1 64 272 64C386.9 64 480 157.1 480 272zM272 416C351.5 416 416 351.5 416 272C416 192.5 351.5 128 272 128C192.5 128 128 192.5 128 272C128 351.5 192.5 416 272 416z"/></svg></a></li> <li><a id="font-size-btn" data-tooltip="Change font size" class="secondary"><svg height="20pt" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--!Font Awesome Free v7.0.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M349.1 114.7C343.9 103.3 332.5 96 320 96C307.5 96 296.1 103.3 290.9 114.7L123.5 480L112 480C94.3 480 80 494.3 80 512C80 529.7 94.3 544 112 544L200 544C217.7 544 232 529.7 232 512C232 494.3 217.7 480 200 480L193.9 480L215.9 432L424.2 432L446.2 480L440.1 480C422.4 480 408.1 494.3 408.1 512C408.1 529.7 422.4 544 440.1 544L528.1 544C545.8 544 560.1 529.7 560.1 512C560.1 494.3 545.8 480 528.1 480L516.6 480L349.2 114.7zM394.8 368L245.2 368L320 204.8L394.8 368z"/></svg></a></li> - <li><a id="settings-btn" style="cursor: pointer;" data-tooltip="Settings" hx-get="/settings" hx-target="#modal-placeholder" hx-swap="innerHTML" preload class="secondary"><svg height="20pt" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--!Font Awesome Free v7.0.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M259.1 73.5C262.1 58.7 275.2 48 290.4 48L350.2 48C365.4 48 378.5 58.7 381.5 73.5L396 143.5C410.1 149.5 423.3 157.2 435.3 166.3L503.1 143.8C517.5 139 533.3 145 540.9 158.2L570.8 210C578.4 223.2 575.7 239.8 564.3 249.9L511 297.3C511.9 304.7 512.3 312.3 512.3 320C512.3 327.7 511.8 335.3 511 342.7L564.4 390.2C575.8 400.3 578.4 417 570.9 430.1L541 481.9C533.4 495 517.6 501.1 503.2 496.3L435.4 473.8C423.3 482.9 410.1 490.5 396.1 496.6L381.7 566.5C378.6 581.4 365.5 592 350.4 592L290.6 592C275.4 592 262.3 581.3 259.3 566.5L244.9 496.6C230.8 490.6 217.7 482.9 205.6 473.8L137.5 496.3C123.1 501.1 107.3 495.1 99.7 481.9L69.8 430.1C62.2 416.9 64.9 400.3 76.3 390.2L129.7 342.7C128.8 335.3 128.4 327.7 128.4 320C128.4 312.3 128.9 304.7 129.7 297.3L76.3 249.8C64.9 239.7 62.3 223 69.8 209.9L99.7 158.1C107.3 144.9 123.1 138.9 137.5 143.7L205.3 166.2C217.4 157.1 230.6 149.5 244.6 143.4L259.1 73.5zM320.3 400C364.5 399.8 400.2 363.9 400 319.7C399.8 275.5 363.9 239.8 319.7 240C275.5 240.2 239.8 276.1 240 320.3C240.2 364.5 276.1 400.2 320.3 400z"/></svg></a></li> + <li><a id="settings-btn" style="cursor: pointer;" data-tooltip="Settings" hx-get="{{ url_for('settings_page') }}" hx-target="#modal-placeholder" hx-swap="innerHTML" preload class="secondary"><svg height="20pt" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--!Font Awesome Free v7.0.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M259.1 73.5C262.1 58.7 275.2 48 290.4 48L350.2 48C365.4 48 378.5 58.7 381.5 73.5L396 143.5C410.1 149.5 423.3 157.2 435.3 166.3L503.1 143.8C517.5 139 533.3 145 540.9 158.2L570.8 210C578.4 223.2 575.7 239.8 564.3 249.9L511 297.3C511.9 304.7 512.3 312.3 512.3 320C512.3 327.7 511.8 335.3 511 342.7L564.4 390.2C575.8 400.3 578.4 417 570.9 430.1L541 481.9C533.4 495 517.6 501.1 503.2 496.3L435.4 473.8C423.3 482.9 410.1 490.5 396.1 496.6L381.7 566.5C378.6 581.4 365.5 592 350.4 592L290.6 592C275.4 592 262.3 581.3 259.3 566.5L244.9 496.6C230.8 490.6 217.7 482.9 205.6 473.8L137.5 496.3C123.1 501.1 107.3 495.1 99.7 481.9L69.8 430.1C62.2 416.9 64.9 400.3 76.3 390.2L129.7 342.7C128.8 335.3 128.4 327.7 128.4 320C128.4 312.3 128.9 304.7 129.7 297.3L76.3 249.8C64.9 239.7 62.3 223 69.8 209.9L99.7 158.1C107.3 144.9 123.1 138.9 137.5 143.7L205.3 166.2C217.4 157.1 230.6 149.5 244.6 143.4L259.1 73.5zM320.3 400C364.5 399.8 400.2 363.9 400 319.7C399.8 275.5 363.9 239.8 319.7 240C275.5 240.2 239.8 276.1 240 320.3C240.2 364.5 276.1 400.2 320.3 400z"/></svg></a></li> </ul> </nav> @@ -18,7 +18,7 @@ <table> <tr> {% for category in categories %} - <th><div hx-get="/?category-id={{ category.id }}" hx-target="#news-cards" preload style="cursor: pointer;">{{ category.name }}</div></th> + <th><div hx-get="{{ url_for('news_page').include_query_params(category_id=category.id) }}" hx-target="#news-cards" preload style="cursor: pointer;">{{ category.name }}</div></th> {% endfor %} </tr> </table> diff --git a/src/simple_web_app/templates/load_more.html b/src/simple_web_app/templates/load_more.html @@ -1 +1 @@ -<button id="load-more-btn" {%- if reached_end %} disabled{% endif %} {% if oob %}hx-swap-oob="true"{% endif %} hx-target="#news-cards" hx-swap="beforeend" hx-get="/?page={{ page + 1 }}&current_time={{ current_time }}{% if category_id %}&category-id={{ category_id }}{% endif %}" preload class="secondary">Load more</button> +<button id="load-more-btn" {%- if reached_end %} disabled{% endif %} {% if oob %}hx-swap-oob="true"{% endif %} hx-target="#news-cards" hx-swap="beforeend" hx-get="{{ url_for('news_page').include_query_params(page=page + 1, current_time=current_time, category_id=category_id) }}" preload class="secondary">Load more</button> diff --git a/src/simple_web_app/templates/news_card.html b/src/simple_web_app/templates/news_card.html @@ -1,7 +1,7 @@ <header> <nav><ul> {% for category in new.categories %} -<li><div style="cursor: pointer;" hx-get="/?category-id={{ category.id }}" hx-target="#news-cards" preload><small>{{ category.name }}</small></div></small></li> +<li><div style="cursor: pointer;" hx-get="{{ url_for('news_page').include_query_params(category_id=category.id) }}" hx-target="#news-cards" preload><small>{{ category.name }}</small></div></small></li> {% endfor %} </ul></nav> </header> diff --git a/src/simple_web_app/templates/settings.html b/src/simple_web_app/templates/settings.html @@ -5,12 +5,12 @@ <div class="overflow-auto" style="text-wrap: nowrap;"> <nav> <ul> - <li><div id="general-tab" hx-get="/settings/tab?tab=general" hx-target="#settings-tab" hx-swap="innerHTML" preload style="cursor: pointer;">General</div></li> - <li><div id="categories-tab" hx-get="/settings/tab?tab=categories" hx-target="#settings-tab" hx-swap="innerHTML" preload style="cursor: pointer;">Categories</div></li> - <li><div id="sections-tab" hx-get="/settings/tab?tab=sections" hx-target="#settings-tab" hx-swap="innerHTML" preload style="cursor: pointer;">Sections</div></li> - <li><div id="content-filter-tab" hx-get="/settings/tab?tab=content-filter" hx-target="#settings-tab" hx-swap="innerHTML" preload style="cursor: pointer;">Content Filter</div></li> - <li><div id="syncing-tab" hx-get="/settings/tab?tab=syncing" hx-target="#settings-tab" hx-swap="innerHTML" preload style="cursor: pointer;">Syncing</div></li> - <li><div id="experimental-tab" hx-get="/settings/tab?tab=experimental" hx-target="#settings-tab" hx-swap="innerHTML" preload style="cursor: pointer;">Experimental</div></li> + <li><div id="general-tab" hx-get="{{ url_for('settings_tab').include_query_params(tab='general') }}" hx-target="#settings-tab" hx-swap="innerHTML" preload style="cursor: pointer;">General</div></li> + <li><div id="categories-tab" hx-get="{{ url_for('settings_tab').include_query_params(tab='categories') }}" hx-target="#settings-tab" hx-swap="innerHTML" preload style="cursor: pointer;">Categories</div></li> + <li><div id="sections-tab" hx-get="{{ url_for('settings_tab').include_query_params(tab='sections') }}" hx-target="#settings-tab" hx-swap="innerHTML" preload style="cursor: pointer;">Sections</div></li> + <li><div id="content-filter-tab" hx-get="{{ url_for('settings_tab').include_query_params(tab='content-filter') }}" hx-target="#settings-tab" hx-swap="innerHTML" preload style="cursor: pointer;">Content Filter</div></li> + <li><div id="syncing-tab" hx-get="{{ url_for('settings_tab').include_query_params(tab='syncing') }}" hx-target="#settings-tab" hx-swap="innerHTML" preload style="cursor: pointer;">Syncing</div></li> + <li><div id="experimental-tab" hx-get="{{ url_for('settings_tab').include_query_params(tab='experimental') }}" hx-target="#settings-tab" hx-swap="innerHTML" preload style="cursor: pointer;">Experimental</div></li> </ul> </nav> </div> diff --git a/tests/test_simple_web_app/test_app.py b/tests/test_simple_web_app/test_app.py @@ -104,21 +104,21 @@ async def test_select_chain(async_test_client, test_data): soup = BeautifulSoup(response.text, features="html.parser") load_more_button = soup.find("button", {"id": "load-more-btn"}) assert "page=1" in load_more_button.attrs["hx-get"] - assert "category-id" not in load_more_button.attrs["hx-get"] + assert "category_id" not in load_more_button.attrs["hx-get"] # Filter for category 2, which doesn't exist # TODO: should this instead throw an error? - response = await client.get("/?category-id=2", headers={"HX-Request": "true"}) + response = await client.get("/?category_id=2", headers={"HX-Request": "true"}) assert response.template.name == "oob_swap.html" assert len(response.context["news"]) == 0 assert response.context["category_id"] == 2 soup = BeautifulSoup(response.text, features="html.parser") load_more_button = soup.find("button", {"id": "load-more-btn"}) assert "page=1" in load_more_button.attrs["hx-get"] - assert "category-id=2" in load_more_button.attrs["hx-get"] + assert "category_id=2" in load_more_button.attrs["hx-get"] # Filter for category 1, show only news with that category - response = await client.get("/?category-id=1", headers={"HX-Request": "true"}) + response = await client.get("/?category_id=1", headers={"HX-Request": "true"}) assert response.template.name == "oob_swap.html" assert len(response.context["news"]) > 0 assert response.context["category_id"] == 1 @@ -127,7 +127,7 @@ async def test_select_chain(async_test_client, test_data): load_more_button = soup.find("button", {"id": "load-more-btn"}) assert "disabled" not in load_more_button.attrs assert "page=1" in load_more_button.attrs["hx-get"] - assert "category-id=1" in load_more_button.attrs["hx-get"] + assert "category_id=1" in load_more_button.attrs["hx-get"] # Load more news, should all still be category one; we only have 1 left so the load more button should be disabled path = load_more_button["hx-get"] @@ -140,7 +140,7 @@ async def test_select_chain(async_test_client, test_data): load_more_button = soup.find("button", {"id": "load-more-btn"}) assert "disabled" in load_more_button.attrs assert "page=2" in load_more_button.attrs["hx-get"] - assert "category-id=1" in load_more_button.attrs["hx-get"] + assert "category_id=1" in load_more_button.attrs["hx-get"] async def test_settings(async_test_client, test_data):