From 7aa7f9b8dc741fc9580f143fb9b45b7d97704c46 Mon Sep 17 00:00:00 2001 From: Peter Stockings Date: Sat, 19 Apr 2025 21:10:34 +1000 Subject: [PATCH] Partial refactor of tags functionality Still need to move tags db logic to BP and move workout tag logic to BP as well --- app.py | 33 +-------- routes/tags.py | 103 ++++++++++++++++++++++++++ templates/changelog/changelog.html | 15 ++++ templates/dashboard.html | 6 +- templates/partials/tags.html | 111 ++++++++++++++--------------- templates/person_overview.html | 4 +- 6 files changed, 180 insertions(+), 92 deletions(-) create mode 100644 routes/tags.py diff --git a/app.py b/app.py index 79fe730..93644bf 100644 --- a/app.py +++ b/app.py @@ -13,6 +13,7 @@ from routes.workout import workout_bp # Import the new workout blueprint from routes.sql_explorer import sql_explorer_bp # Import the new SQL explorer blueprint from routes.endpoints import endpoints_bp # Import the new endpoints blueprint from routes.export import export_bp # Import the new export blueprint +from routes.tags import tags_bp # Import the new tags blueprint from extensions import db from utils import convert_str_to_date, generate_plot from flask_htmx import HTMX @@ -46,6 +47,7 @@ app.register_blueprint(workout_bp) # Register the workout blueprint app.register_blueprint(sql_explorer_bp) # Register the SQL explorer blueprint (prefix defined in blueprint file) app.register_blueprint(endpoints_bp) # Register the endpoints blueprint (prefix defined in blueprint file) app.register_blueprint(export_bp) # Register the export blueprint (prefix defined in blueprint file) +app.register_blueprint(tags_bp) # Register the tags blueprint (prefix defined in blueprint file) @app.after_request def response_minify(response): @@ -212,36 +214,7 @@ def settings(): return render_template('settings.html', people=people, exercises=exercises) -@ app.route("/tag/redirect", methods=['GET']) -def goto_tag(): - person_id = request.args.get("person_id") - tag_filter = request.args.get('filter') - if person_id: - return redirect(url_for('person_overview', person_id=int(person_id)) + tag_filter) - return redirect(url_for('dashboard') + tag_filter) - - -@ app.route("/tag/add", methods=['GET']) -def add_tag(): - person_id = request.args.get("person_id") - tag = request.args.get('tag') - tag_filter = request.args.get('filter') - if person_id: - db.add_or_update_tag_for_person(person_id, tag, tag_filter) - else: - db.add_or_update_tag_for_dashboard(tag, tag_filter) - return "" - - -@ app.route("/tag//delete", methods=['GET']) -def delete_tag(tag_id): - person_id = request.args.get("person_id") - tag_filter = request.args.get("filter") - if person_id: - db.delete_tag_for_person(person_id=person_id, tag_id=tag_id) - return redirect(url_for('get_person', person_id=person_id) + tag_filter) - db.delete_tag_for_dashboard(tag_id) - return redirect(url_for('dashboard') + tag_filter) +# Routes moved to routes/tags.py blueprint @ app.route("/person//exercise//sparkline", methods=['GET']) def get_exercise_progress_for_user(person_id, exercise_id): diff --git a/routes/tags.py b/routes/tags.py new file mode 100644 index 0000000..f12445b --- /dev/null +++ b/routes/tags.py @@ -0,0 +1,103 @@ +from flask import Blueprint, request, redirect, url_for, render_template, current_app +from urllib.parse import urlencode, parse_qs, unquote_plus +from flask_login import current_user +from extensions import db +from jinja2_fragments import render_block + +tags_bp = Blueprint('tags', __name__, url_prefix='/tag') + +# Helper function to get tags (assuming similar logic exists in dashboard/person_overview) +# NOTE: This is a placeholder and might need adjustment based on actual data fetching logic +def _get_tags_data(person_id=None): + if person_id: + # Logic to fetch tags for a specific person + tags_raw = db.get_tags_for_person(person_id) + # Assuming get_tags_for_person returns list like [{'tag_id': 1, 'tag_name': 'Bulk', 'tag_filter': '?tag=Bulk'}] + return tags_raw # Adjust based on actual return format + else: + tags_raw = db.get_tags_for_dashboard() + return tags_raw + + +@tags_bp.route("/redirect", methods=['GET']) +def goto_tag(): + """Redirects or loads content based on tag filter.""" + person_id = request.args.get("person_id") + tag_filter = request.args.get("filter", "") # Default to empty string + + if person_id: + # Assuming person_overview handles HTMX requests to render blocks + # Corrected endpoint name for person overview + target_url = url_for('person_overview', person_id=person_id) + tag_filter + # Check if it's an HTMX request targeting #container + if request.headers.get('HX-Target') == 'container' or request.headers.get('HX-Target') == '#container': + # Need the actual function that renders person_overview content block + # Placeholder: Re-render the person overview block with the filter + # This requires knowing how person_overview fetches its data based on filters + # return render_block('person_overview.html', 'content_block', person_id=person_id, filter=tag_filter) + # For now, let's assume a full redirect might be simpler if block rendering is complex + return redirect(target_url) + else: + return redirect(target_url) + else: + # Assuming dashboard handles HTMX requests to render blocks + target_url = url_for('dashboard') + tag_filter + if request.headers.get('HX-Target') == 'container' or request.headers.get('HX-Target') == '#container': + # Need the actual function that renders dashboard content block + # Placeholder: Re-render the dashboard block with the filter + # This requires knowing how dashboard fetches its data based on filters + # return render_block('dashboard.html', 'content_block', filter=tag_filter) + # For now, let's assume a full redirect might be simpler + return redirect(target_url) + else: + return redirect(target_url) + + +@tags_bp.route("/add", methods=['POST']) # Changed to POST +def add_tag(): + """Adds a tag and returns the updated tags partial.""" + person_id = request.form.get("person_id") # Get from form data + tag_name = request.form.get('tag_name') + current_filter_str = request.form.get('current_filter', '') + + if not tag_name: + # Handle error - maybe return an error message partial? + # For now, just re-render tags without adding + tags = _get_tags_data(person_id) + return render_template('partials/tags.html', tags=tags, person_id=person_id) + + + # Parse the current filter string, add the new tag, and re-encode + parsed_params = parse_qs(current_filter_str) + # parse_qs returns lists for values, handle potential existing 'tag' param + parsed_params['tag'] = [tag_name] # Set/overwrite tag param with the new one + # Re-encode, ensuring proper handling of multiple values if needed (though 'tag' is likely single) + tag_filter_value = "?" + urlencode(parsed_params, doseq=True) + + if person_id: + db.add_or_update_tag_for_person(person_id, tag_name, tag_filter_value) + else: + db.add_or_update_tag_for_dashboard(tag_name, tag_filter_value) + + # Fetch updated tags and render the partial + tags = _get_tags_data(person_id) + return render_template('partials/tags.html', tags=tags, person_id=person_id) + + +@tags_bp.route("//delete", methods=['DELETE']) # Changed to DELETE +def delete_tag(tag_id): + """Deletes a tag and returns the updated tags partial.""" + # We might get person_id from request body/headers if needed, or assume context + # For simplicity, let's try deleting based on tag_id only first, assuming tags are unique enough or context is handled elsewhere + # If person_id is strictly required for deletion scope: + person_id = request.form.get("person_id") # Or from headers/session + + # Decide which delete function to call based on context (person page vs dashboard) + if person_id: + db.delete_tag_for_person(person_id=person_id, tag_id=tag_id) + else: + db.delete_tag_for_dashboard(tag_id=tag_id) + + # Fetch updated tags and render the partial + tags = _get_tags_data(person_id) + return render_template('partials/tags.html', tags=tags, person_id=person_id) \ No newline at end of file diff --git a/templates/changelog/changelog.html b/templates/changelog/changelog.html index 23d3156..c0b732c 100644 --- a/templates/changelog/changelog.html +++ b/templates/changelog/changelog.html @@ -10,6 +10,21 @@

Updates and changes to the site will be documented here, with the most recent changes listed first.

+ +
+

April 19, 2025

+
    +
  • Refactored tag management functionality:
  • +
      +
    • Moved tag-related routes (`add_tag`, `delete_tag`, `goto_tag`) from `app.py` to a new blueprint + `routes/tags.py`.
    • +
    • Changed `add_tag` endpoint to use `POST` and `delete_tag` to use `DELETE`.
    • +
    • Updated `add_tag` and `delete_tag` to return the updated `tags.html` partial via HTMX swap.
    • +
    • Wrapped the inclusion of `tags.html` in `dashboard.html` and `person_overview.html` with + `div#container` for correct HTMX targeting.
    • +
    +
+

April 15, 2025

diff --git a/templates/dashboard.html b/templates/dashboard.html index f24a38b..fc8143b 100644 --- a/templates/dashboard.html +++ b/templates/dashboard.html @@ -92,7 +92,9 @@
- {{ render_partial('partials/tags.html',person_id=None, tags=tags) }} +
+ {{ render_partial('partials/tags.html', person_id=None, tags=tags) }} +
- {{ render_partial('partials/tags.html',person_id=person_id, tags=tags) }} +
+ {{ render_partial('partials/tags.html', person_id=person_id, tags=tags) }} +