diff --git a/db.py b/db.py index 6ca15b7..3029d09 100644 --- a/db.py +++ b/db.py @@ -341,88 +341,6 @@ class DataBase(): def delete_tag_for_dashboard(self, tag_id): self.execute('DELETE FROM Tag WHERE tag_id=%s', [tag_id], commit=True) - # Note update logic moved to routes/notes.py - - def add_tag_for_workout(self, workout_id, tags_id): - # If tags_id is not empty, delete tags that are not in the new selection - if tags_id: - self.execute( - """ - DELETE FROM workout_tag - WHERE workout_id = %s AND tag_id NOT IN %s - """, - [workout_id, tuple(tags_id)], commit=True - ) - else: - # If tags_id is empty, delete all tags for this workout - self.execute( - """ - DELETE FROM workout_tag - WHERE workout_id = %s - """, - [workout_id], commit=True - ) - - # Then, attempt to insert the new tags - for tag_id in tags_id: - self.execute( - """ - INSERT INTO workout_tag (workout_id, tag_id) - VALUES (%s, %s) - ON CONFLICT (workout_id, tag_id) DO NOTHING - """, - [workout_id, tag_id], commit=True - ) - - # Now fetch updated list of workout tags - workout_tags = self.execute(""" - SELECT - T.tag_id AS "tag_id", - T.person_id AS "person_id", - T.name AS "tag_name", - T.filter AS "tag_filter", - TRUE AS "is_selected" - FROM Workout_Tag WT - LEFT JOIN Tag T ON WT.tag_id=T.tag_id - WHERE WT.workout_id=%s""", [workout_id]) - - return workout_tags - - def create_tag_for_workout(self, person_id, workout_id, tag_name): - workout_exercises = self.execute(""" - SELECT - E.exercise_id AS "exercise_id", - E.name AS "exercise_name" - FROM Workout W - LEFT JOIN TopSet T ON W.workout_id=T.workout_id - LEFT JOIN Exercise E ON T.exercise_id=E.exercise_id - WHERE W.workout_id=%s""", [workout_id]) - - tag_filter = "?" + \ - "&".join( - f"exercise_id={e['exercise_id']}" for e in workout_exercises) - - # create tag for person - row = self.execute('INSERT INTO Tag (person_id, name, filter) VALUES (%s, %s, %s) RETURNING tag_id AS "tag_id"', [ - person_id, tag_name, tag_filter], commit=True, one=True) - - # add tag to workout - self.execute('INSERT INTO Workout_Tag (workout_id, tag_id) VALUES (%s, %s)', [ - workout_id, row['tag_id']], commit=True) - - # Now fetch updated list of workout tags - workout_tags = self.execute(""" - SELECT - T.tag_id AS "tag_id", - T.person_id AS "person_id", - T.name AS "tag_name", - T.filter AS "tag_filter" - FROM Workout_Tag WT - LEFT JOIN Tag T ON WT.tag_id=T.tag_id - WHERE WT.workout_id=%s""", [workout_id]) - - return workout_tags - def get_workout_tags(self, person_id, workout_id): person_tags = self.execute(""" SELECT diff --git a/routes/tags.py b/routes/tags.py index f12445b..b672ef0 100644 --- a/routes/tags.py +++ b/routes/tags.py @@ -100,4 +100,164 @@ def delete_tag(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 + return render_template('partials/tags.html', tags=tags, person_id=person_id) + +# --- Workout Specific Tag Routes --- + +@tags_bp.route("/workout//add", methods=['POST']) +def add_tag_to_workout(workout_id): + """Adds existing tags to a specific workout.""" + # Note: Authorization (checking if the current user can modify this workout) might be needed here. + tags_id = [int(i) for i in request.form.getlist('tag_id')] + + # --- Start: DB logic from db.add_tag_for_workout --- + # If tags_id is not empty, delete tags that are not in the new selection + if tags_id: + db.execute( + """ + DELETE FROM workout_tag + WHERE workout_id = %s AND tag_id NOT IN %s + """, + [workout_id, tuple(tags_id)], commit=True + ) + else: + # If tags_id is empty, delete all tags for this workout + db.execute( + """ + DELETE FROM workout_tag + WHERE workout_id = %s + """, + [workout_id], commit=True + ) + + # Then, attempt to insert the new tags + for tag_id in tags_id: + db.execute( + """ + INSERT INTO workout_tag (workout_id, tag_id) + VALUES (%s, %s) + ON CONFLICT (workout_id, tag_id) DO NOTHING + """, + [workout_id, tag_id], commit=True + ) + + # Now fetch updated list of workout tags + workout_tags = db.execute(""" + SELECT + T.tag_id AS "tag_id", + T.person_id AS "person_id", + T.name AS "tag_name", + T.filter AS "tag_filter", + TRUE AS "is_selected" -- Mark these as selected since they are now associated + FROM Workout_Tag WT + LEFT JOIN Tag T ON WT.tag_id=T.tag_id + WHERE WT.workout_id=%s + ORDER BY T.name ASC -- Keep consistent ordering + """, [workout_id]) + # --- End: DB logic from db.add_tag_for_workout --- + + # We need person_id for rendering the partial if it requires it, fetch it. + workout_info = db.execute("SELECT person_id FROM workout WHERE workout_id = %s", [workout_id], one=True) + person_id = workout_info['person_id'] if workout_info else None + + # The partial likely needs the full tag list for the workout, including unselected ones. + # Let's fetch all relevant tags for the person and mark selected ones. + all_person_tags = db.execute(""" + SELECT + tag.tag_id, + tag.name AS tag_name, + tag.filter as tag_filter, + EXISTS ( + SELECT 1 FROM workout_tag wt_check + WHERE wt_check.workout_id = %s AND wt_check.tag_id = tag.tag_id + ) AS is_selected + FROM tag + WHERE tag.person_id = %s OR tag.person_id IS NULL -- Include global tags + ORDER BY tag.name ASC; + """, [workout_id, person_id]) + + + # Render the partial with the complete list of tags (selected and unselected) + return render_template('partials/workout_tags_list.html', tags=all_person_tags, person_id=person_id, workout_id=workout_id) + +@tags_bp.route("/workout//new", methods=['POST']) +def create_new_tag_for_workout(workout_id): + """Creates a new tag and associates it with a specific workout.""" + # Note: Authorization might be needed here. + tag_name = request.form.get('tag_name') + if not tag_name: + # Handle error: Tag name cannot be empty + # Consider returning an error message via HTMX + return "Tag name cannot be empty", 400 + + # Fetch person_id associated with the workout_id first + workout_info = db.execute("SELECT person_id FROM workout WHERE workout_id = %s", [workout_id], one=True) + if not workout_info: + # Handle error: Workout not found + return "Workout not found", 404 + person_id = workout_info['person_id'] + + # --- Start: DB logic from db.create_tag_for_workout --- + # Determine tag filter based on exercises in the workout (if any) + workout_exercises = db.execute(""" + SELECT DISTINCT E.exercise_id AS "exercise_id" + FROM Workout W + LEFT JOIN TopSet T ON W.workout_id=T.workout_id + LEFT JOIN Exercise E ON T.exercise_id=E.exercise_id + WHERE W.workout_id=%s AND E.exercise_id IS NOT NULL""", [workout_id]) + + tag_filter = "" + if workout_exercises: + tag_filter = "?" + "&".join( + f"exercise_id={e['exercise_id']}" for e in workout_exercises) + + # Create tag for person (or update if exists with same name for this person) + # Check if tag already exists for this person + existing_tag = db.execute( + "SELECT tag_id FROM Tag WHERE person_id = %s AND name = %s", + [person_id, tag_name], one=True + ) + + new_tag_id = None + if existing_tag: + new_tag_id = existing_tag['tag_id'] + # Optionally update the filter if it's different? For now, just use existing tag. + # db.execute("UPDATE Tag SET filter = %s WHERE tag_id = %s", [tag_filter, new_tag_id], commit=True) + else: + # Create the new tag + row = db.execute( + 'INSERT INTO Tag (person_id, name, filter) VALUES (%s, %s, %s) RETURNING tag_id AS "tag_id"', + [person_id, tag_name, tag_filter], commit=True, one=True + ) + if row: + new_tag_id = row['tag_id'] + + if not new_tag_id: + # Handle error: Failed to create or find tag + return "Error creating tag", 500 + + # Add tag to workout (handle conflict just in case) + db.execute( + 'INSERT INTO Workout_Tag (workout_id, tag_id) VALUES (%s, %s) ON CONFLICT (workout_id, tag_id) DO NOTHING', + [workout_id, new_tag_id], commit=True + ) + + # Now fetch updated list of all workout tags (selected and unselected) + all_person_tags = db.execute(""" + SELECT + tag.tag_id, + tag.name AS tag_name, + tag.filter as tag_filter, + EXISTS ( + SELECT 1 FROM workout_tag wt_check + WHERE wt_check.workout_id = %s AND wt_check.tag_id = tag.tag_id + ) AS is_selected + FROM tag + WHERE tag.person_id = %s OR tag.person_id IS NULL -- Include global tags + ORDER BY tag.name ASC; + """, [workout_id, person_id]) + # --- End: DB logic from db.create_tag_for_workout --- + + # Render the partial with the complete list of tags + # Use 'tags' as the variable name consistent with the other endpoint and likely the partial + return render_template('partials/workout_tags_list.html', tags=all_person_tags, person_id=person_id, workout_id=workout_id) \ No newline at end of file diff --git a/routes/workout.py b/routes/workout.py index f3f773e..6d7c939 100644 --- a/routes/workout.py +++ b/routes/workout.py @@ -208,18 +208,6 @@ def delete_topset(person_id, workout_id, topset_id): db.delete_topset(topset_id) return "" -@workout_bp.route("/person//workout//tag/add", methods=['POST']) -def add_tag_to_workout(person_id, workout_id): - tags_id = [int(i) for i in request.form.getlist('tag_id')] - tags = db.add_tag_for_workout(workout_id, tags_id) # Keep using db.py for complex tag logic for now - return render_template('partials/workout_tags_list.html', tags=tags) - -@workout_bp.route("/person//workout//tag/new", methods=['POST']) -def create_new_tag_for_workout(person_id, workout_id): - tag_name = request.form.get('tag_name') - workout_tags = db.create_tag_for_workout(person_id, workout_id, tag_name) # Keep using db.py for complex tag logic for now - return render_template('partials/workout_tags_list.html', workout_tags=workout_tags) - @workout_bp.route("/person//workout//exercise/most_recent_topset_for_exercise", methods=['GET']) def get_most_recent_topset_for_exercise(person_id, workout_id): exercise_id = request.args.get('exercise_id', type=int) diff --git a/templates/partials/workout_tags.html b/templates/partials/workout_tags.html index b248802..06d042c 100644 --- a/templates/partials/workout_tags.html +++ b/templates/partials/workout_tags.html @@ -35,7 +35,7 @@