diff --git a/app.py b/app.py index b66b18c..e317d91 100644 --- a/app.py +++ b/app.py @@ -230,16 +230,17 @@ def settings(): people = db.get_people() exercises = db.get_all_exercises() all_attributes = db.exercises.get_attributes_by_category() + categories_list = db.exercises.get_all_attribute_categories() # Format options for custom_select formatted_options = {} for cat, attrs in all_attributes.items(): - formatted_options[cat] = [{"id": a['attribute_id'], "name": a['name']} for a in attrs] + formatted_options[cat] = [{"id": a['attribute_id'], "attribute_id": a['attribute_id'], "name": a['name'], "category_id": a['category_id']} for a in attrs] if htmx: return render_block(app.jinja_env, "settings.html", "content", - people=people, exercises=exercises, all_attributes=formatted_options), 200, {"HX-Trigger": "updatedPeople"} - return render_template('settings.html', people=people, exercises=exercises, all_attributes=formatted_options) + people=people, exercises=exercises, all_attributes=formatted_options, categories_list=categories_list), 200, {"HX-Trigger": "updatedPeople"} + return render_template('settings.html', people=people, exercises=exercises, all_attributes=formatted_options, categories_list=categories_list) @app.route("/settings/activity_logs") diff --git a/features/exercises.py b/features/exercises.py index b2a25bb..2547074 100644 --- a/features/exercises.py +++ b/features/exercises.py @@ -147,3 +147,38 @@ class Exercises: return distribution + # Category Management + def add_category(self, name): + result = self.execute('INSERT INTO exercise_attribute_category (name) VALUES (%s) RETURNING category_id, name', [name], commit=True, one=True) + return result + + def update_category(self, category_id, name): + self.execute('UPDATE exercise_attribute_category SET name = %s WHERE category_id = %s', [name, category_id], commit=True) + return {"category_id": category_id, "name": name} + + def delete_category(self, category_id): + # First delete all attributes in this category + attributes = self.execute('SELECT attribute_id FROM exercise_attribute WHERE category_id = %s', [category_id]) + for attr in attributes: + self.delete_attribute(attr['attribute_id']) + + self.execute('DELETE FROM exercise_attribute_category WHERE category_id = %s', [category_id], commit=True) + + # Attribute Management + def add_attribute(self, name, category_id): + result = self.execute('INSERT INTO exercise_attribute (name, category_id) VALUES (%s, %s) RETURNING attribute_id, name, category_id', [name, category_id], commit=True, one=True) + return result + + def update_attribute(self, attribute_id, name, category_id=None): + if category_id: + self.execute('UPDATE exercise_attribute SET name = %s, category_id = %s WHERE attribute_id = %s', [name, category_id, attribute_id], commit=True) + else: + self.execute('UPDATE exercise_attribute SET name = %s WHERE attribute_id = %s', [name, attribute_id], commit=True) + return self.execute('SELECT attribute_id, name, category_id FROM exercise_attribute WHERE attribute_id = %s', [attribute_id], one=True) + + def delete_attribute(self, attribute_id): + # Remove from all exercises first + self.execute('DELETE FROM exercise_to_attribute WHERE attribute_id = %s', [attribute_id], commit=True) + # Delete the attribute + self.execute('DELETE FROM exercise_attribute WHERE attribute_id = %s', [attribute_id], commit=True) + diff --git a/routes/exercises.py b/routes/exercises.py index 501bea2..480d9f0 100644 --- a/routes/exercises.py +++ b/routes/exercises.py @@ -115,3 +115,70 @@ def delete_exercise(exercise_id): db.exercises.delete_exercise(exercise_id) db.activityRequest.log(current_user.id, 'DELETE_EXERCISE', 'exercise', exercise_id, f"Deleted exercise: {exercise['name']}") return "" + +# Category Management Routes +@exercises_bp.route("/category", methods=['POST']) +@login_required +@admin_required +def create_category(): + name = request.form.get("name") + category = db.exercises.add_category(name) + db.activityRequest.log(current_user.id, 'CREATE_CATEGORY', 'category', category['category_id'], f"Created attribute category: {name}") + return render_template('partials/exercise/category_admin.html', category_id=category['category_id'], name=category['name'], attributes=[]) + +@exercises_bp.route("/category/", methods=['GET', 'PUT']) +@login_required +@admin_required +def update_category(category_id): + if request.method == 'GET': + category = db.exercises.execute('SELECT category_id, name FROM exercise_attribute_category WHERE category_id = %s', [category_id], one=True) + is_edit = request.args.get('is_edit') == 'true' + all_attrs = db.exercises.execute('SELECT attribute_id, name FROM exercise_attribute WHERE category_id = %s', [category_id]) + return render_template('partials/exercise/category_admin.html', category_id=category_id, name=category['name'], attributes=all_attrs, is_edit=is_edit) + + name = request.form.get("name") + category = db.exercises.update_category(category_id, name) + db.activityRequest.log(current_user.id, 'UPDATE_CATEGORY', 'category', category_id, f"Updated attribute category: {name}") + all_attrs = db.exercises.execute('SELECT attribute_id, name FROM exercise_attribute WHERE category_id = %s', [category_id]) + return render_template('partials/exercise/category_admin.html', category_id=category_id, name=name, attributes=all_attrs) + +@exercises_bp.route("/category/", methods=['DELETE']) +@login_required +@admin_required +def delete_category(category_id): + db.exercises.delete_category(category_id) + db.activityRequest.log(current_user.id, 'DELETE_CATEGORY', 'category', category_id, f"Deleted attribute category") + return "" + +# Attribute Management Routes +@exercises_bp.route("/attribute", methods=['POST']) +@login_required +@admin_required +def create_attribute(): + name = request.form.get("name") + category_id = request.form.get("category_id", type=int) + attribute = db.exercises.add_attribute(name, category_id) + db.activityRequest.log(current_user.id, 'CREATE_ATTRIBUTE', 'attribute', attribute['attribute_id'], f"Created attribute: {name}") + return render_template('partials/exercise/attribute_admin.html', attribute=attribute) + +@exercises_bp.route("/attribute/", methods=['GET', 'PUT']) +@login_required +@admin_required +def update_attribute(attribute_id): + if request.method == 'GET': + attribute = db.exercises.execute('SELECT attribute_id, name, category_id FROM exercise_attribute WHERE attribute_id = %s', [attribute_id], one=True) + is_edit = request.args.get('is_edit') == 'true' + return render_template('partials/exercise/attribute_admin.html', attribute=attribute, is_edit=is_edit) + + name = request.form.get("name") + attribute = db.exercises.update_attribute(attribute_id, name) + db.activityRequest.log(current_user.id, 'UPDATE_ATTRIBUTE', 'attribute', attribute_id, f"Updated attribute: {name}") + return render_template('partials/exercise/attribute_admin.html', attribute=attribute) + +@exercises_bp.route("/attribute/", methods=['DELETE']) +@login_required +@admin_required +def delete_attribute(attribute_id): + db.exercises.delete_attribute(attribute_id) + db.activityRequest.log(current_user.id, 'DELETE_ATTRIBUTE', 'attribute', attribute_id, "Deleted attribute") + return "" diff --git a/templates/partials/exercise/attribute_admin.html b/templates/partials/exercise/attribute_admin.html new file mode 100644 index 0000000..255bd0f --- /dev/null +++ b/templates/partials/exercise/attribute_admin.html @@ -0,0 +1,45 @@ +
+ {% if is_edit %} +
+ + + +
+ {% else %} + {{ attribute.name }} + + {% endif %} +
\ No newline at end of file diff --git a/templates/partials/exercise/category_admin.html b/templates/partials/exercise/category_admin.html new file mode 100644 index 0000000..87de152 --- /dev/null +++ b/templates/partials/exercise/category_admin.html @@ -0,0 +1,76 @@ +
+
+
+
+ + + + +
+ {% if is_edit %} +
+ + + +
+ {% else %} +
{{ name }}
+ + {% endif %} +
+ + +
+ +
+ {% for attr in attributes %} + {{ render_partial('partials/exercise/attribute_admin.html', attribute=attr) }} + {% endfor %} +
+ +
+ + + +
+
\ No newline at end of file diff --git a/templates/settings.html b/templates/settings.html index 948662e..b8eb2d1 100644 --- a/templates/settings.html +++ b/templates/settings.html @@ -279,6 +279,48 @@ + + +
+
+

Manage Categories & Options

+

Add or edit muscle groups, equipment types, and other exercise + attributes.

+
+ +
+ {% for cat in categories_list %} + {% set options = all_attributes.get(cat.name, []) %} + {{ render_partial('partials/exercise/category_admin.html', category_id=cat.category_id, + name=cat.name, attributes=options) }} + {% endfor %} +
+ + +
+
+
+ + +
+
+ +
+
+
+