From ee8245bb4c41e831e57d5e2c65a763635754d070 Mon Sep 17 00:00:00 2001 From: Peter Stockings Date: Wed, 20 Jul 2022 14:15:11 +1000 Subject: [PATCH] Refactor route validations to decorators --- app.py | 442 ++++++------------------------------- config.py | 5 + db.py | 170 ++++++++++++++ decorators.py | 42 ++++ swagger/create_topset.yml | 17 ++ swagger/create_workout.yml | 13 ++ swagger/dashboard.yml | 8 + swagger/delete_topset.yml | 21 ++ swagger/delete_workout.yml | 17 ++ swagger/get_person.yml | 13 ++ swagger/get_topset.yml | 21 ++ swagger/get_workout.yml | 17 ++ templates/base.html | 2 +- templates/index.html | 4 +- templates/topset.html | 6 +- templates/workout.html | 20 +- templates/workouts.html | 25 ++- utils.py | 28 +++ 18 files changed, 464 insertions(+), 407 deletions(-) create mode 100644 config.py create mode 100644 db.py create mode 100644 decorators.py create mode 100644 swagger/create_topset.yml create mode 100644 swagger/create_workout.yml create mode 100644 swagger/dashboard.yml create mode 100644 swagger/delete_topset.yml create mode 100644 swagger/delete_workout.yml create mode 100644 swagger/get_person.yml create mode 100644 swagger/get_topset.yml create mode 100644 swagger/get_workout.yml create mode 100644 utils.py diff --git a/app.py b/app.py index 8c7b4c2..fefdc9f 100644 --- a/app.py +++ b/app.py @@ -1,7 +1,10 @@ -from flask import Flask, jsonify, render_template, g, redirect, request, url_for -from flasgger import Swagger +from flask import Flask, abort, render_template, g, redirect, request, url_for +from flasgger import Swagger, swag_from +from functools import wraps import sqlite3 -import datetime + +from db import DataBase +from decorators import validate_person, validate_topset, validate_workout template = { "swagger": "2.0", @@ -24,394 +27,88 @@ template = { } app = Flask(__name__) +app.config.from_pyfile('config.py') swagger = Swagger(app, template=template) -DATABASE = 'workout.db' +db = DataBase(app) -def get_db(): - db = getattr(g, '_database', None) - if db is None: - db = g._database = sqlite3.connect(DATABASE) - db.row_factory = sqlite3.Row - return db - - -def query_db(query, args=(), one=False): - cur = get_db().execute(query, args) - rv = cur.fetchall() - cur.connection.commit() - cur.close() - return (rv[0] if rv else None) if one else rv - - -@app.route("/") +@ app.route("/") +@ swag_from('swagger/dashboard.yml') def dashboard(): - """Dashboard page - Displays stats and a list of all people and there rep maxes for each exercise - --- - tags: - - Dashboard - responses: - 200: - description: A list of all people and there rep maxes for each exercise - """ return render_template('index.html') -@app.route("/person/") -def display_workouts_for_person(person_id): - """Display all workouts for a person - Displays stats and a list of all people and there rep maxes for each exercise - --- - tags: - - Person - parameters: - - name: person_id - in: path - type: number - required: true - - responses: - 200: - description: A list of all people and there rep maxes for each exercise - """ - person = query_db('SELECT * FROM Person WHERE PersonId=?', - [person_id], one=True) - - if person is None: - return render_template('error.html', error='404', message=f'Unable to find Person({person_id})', url='/') - - workouts = query_db(""" - SELECT - W.WorkoutId, - W.StartDate, - T.TopSetId, - E.ExcerciseId, - E.Name, - T.Repetitions || ' x ' || T.Weight || 'kg' AS TopSet - FROM - Workout W - LEFT JOIN TopSet T ON W.WorkoutId = T.WorkoutId - LEFT JOIN Excercise E ON T.ExcerciseId = E.ExcerciseId - WHERE - W.PersonId = ?""", - [person_id]) - - exercises = query_db('select * from Excercise') - - unique_workout_ids = set([w['WorkoutId'] for w in workouts]) - - transformed_workouts = [] - for workout_id in unique_workout_ids: - # topsets = [w for w in workouts if w['WorkoutId' == workout_id]] - topsets = [] - for workout in workouts: - if workout['WorkoutId'] == workout_id: - topsets.append(workout) - topset_exercises = {} - for exercise in exercises: - for topset in topsets: - if topset['ExcerciseId'] == exercise['ExcerciseId']: - topset_exercises[exercise['ExcerciseId'] - ] = topset['TopSet'] - - workout = [w for w in workouts if w['WorkoutId'] == workout_id][0] - transformed_workouts.append( - {"workout_id": workout['WorkoutId'], "start_date": workout['StartDate'], "topset_exercises": topset_exercises}) - - return render_template('workouts.html', person_id=person_id, person=person, workouts=transformed_workouts, exercises=exercises) +@ app.route("/person/") +@ swag_from('swagger/get_person.yml') +@ validate_person +def get_person(person_id): + person = db.get_person_final(person_id) + return render_template('workouts.html', person=person) -@app.route("/person//workout", methods=['POST']) -def new_workout_for_person(person_id): - """Create new workout - Creates a workout with current date and then redirects to newly created workout - --- - tags: - - Workout - parameters: - - name: person_id - in: path - type: number - required: true - - responses: - 200: - description: View of newly created workout - """ - person = query_db('SELECT * FROM Person WHERE PersonId=?', - [person_id], one=True) - - if person is None: - return render_template('error.html', error='404', message=f'Unable to find Person({person_id})', url='/') - - now = datetime.datetime.now() - date_string = now.strftime('%Y-%m-%d') - print(f'Creating workout for {person_id} at {date_string}') - query_db('INSERT INTO Workout (PersonId, StartDate) VALUES (?, ?)', [ - person_id, date_string]) - w = query_db('SELECT MAX(WorkoutId) AS WorkoutId FROM Workout WHERE PersonId=?', [ - person_id], one=True) - - return redirect(url_for('show_workout_for_person', person_id=person_id, workout_id=w['WorkoutId'])) +@ app.route("/person//workout", methods=['POST']) +@ swag_from('swagger/create_workout.yml') +@ validate_person +def create_workout(person_id): + new_workout_id = db.create_workout(person_id) + return redirect(url_for('get_workout', person_id=person_id, workout_id=new_workout_id)) -@app.route("/person//workout/") -def show_workout_for_person(person_id, workout_id): - """Display a workout - Displays a selected workout with options to edit/delete existing and add new topsets - --- - tags: - - Workout - parameters: - - name: person_id - in: path - type: number - required: true - - name: workout_id - in: path - type: number - required: true - responses: - 200: - description: A list of topsets in a selected workout - """ - workout_info = query_db(""" - SELECT - P.Name, - W.StartDate - FROM - Person P - LEFT JOIN Workout W ON P.PersonId = W.PersonId - WHERE - P.PersonId = ? - AND W.WorkoutId = ? - LIMIT 1""", - [person_id, workout_id], one=True) - - if workout_info is None: - return render_template('error.html', error='404', message=f'Unable to find Workout({workout_id}) completed by Person({person_id})', url=url_for('display_workouts_for_person', person_id=person_id)) - - top_sets = query_db(""" - SELECT - T.TopSetId, - E.Name, - T.Repetitions || ' x ' || T.Weight || 'kg' AS TopSet - FROM - Person P - LEFT JOIN Workout W ON P.PersonId = W.PersonId - INNER JOIN TopSet T ON W.WorkoutId = T.WorkoutId - INNER JOIN Excercise E ON T.ExcerciseId = E.ExcerciseId - WHERE - P.PersonId = ? - AND W.WorkoutId = ?""", - [person_id, workout_id]) - - return render_template('workout.html', person_id=person_id, workout_id=workout_id, workout_info=workout_info, top_sets=top_sets, exercises=query_db('select * from Excercise')) +@ app.route("/person//workout/") +@ swag_from('swagger/get_workout.yml') +@ validate_workout +def get_workout(person_id, workout_id): + workout = db.get_workout_final(person_id, workout_id) + return render_template('workout.html', workout=workout) -@app.route("/person//workout//delete", methods=['GET', 'DELETE']) -def delete_workout_from_person(person_id, workout_id): - """Delete workout - Deletes selected workout completed by a person - --- - tags: - - Workout - parameters: - - name: person_id - in: path - type: number - required: true - - name: workout_id - in: path - type: number - required: true - responses: - 200: - description: Redirect to workouts list page for person - """ - - workout_info = query_db(""" - SELECT - P.Name, - W.StartDate - FROM - Person P - LEFT JOIN Workout W ON P.PersonId = W.PersonId - WHERE - P.PersonId = ? - AND W.WorkoutId = ? - LIMIT 1""", - [person_id, workout_id], one=True) - - if workout_info is None: - return render_template('error.html', error='404', message=f'Unable to find Workout({workout_id}) completed by Person({person_id})', url=url_for('display_workouts_for_person', person_id=person_id)) - - query_db('DELETE FROM Workout WHERE WorkoutId=?', - [workout_id]) - - return redirect(url_for('display_workouts_for_person', person_id=person_id)) +@ app.route("/person//workout//delete", methods=['GET', 'DELETE']) +@ swag_from('swagger/delete_workout.yml') +@ validate_workout +def delete_workout(person_id, workout_id): + db.delete_workout(workout_id) + return redirect(url_for('get_person', person_id=person_id)) -@app.route("/person//workout//topset/", methods=['GET', 'POST']) -def show_topset_from_workout_for_person(person_id, workout_id, topset_id): - """Display/Create new top set - Displays stats and a list of all people and there rep maxes for each exercise - --- - tags: - - Topset - parameters: - - name: person_id - in: path - type: number - required: true - - name: workout_id - in: path - type: number - required: true - - name: topset_id - in: path - type: number - required: true - responses: - 200: - description: A list of topsets in a selected workout - """ - topset = query_db(""" - SELECT - P.Name, - W.StartDate, - E.ExcerciseId, - E.Name, - T.Repetitions, - T.Weight - FROM - Person P - LEFT JOIN Workout W ON P.PersonId = W.PersonId - INNER JOIN TopSet T ON W.WorkoutId = T.WorkoutId - INNER JOIN Excercise E ON T.ExcerciseId = E.ExcerciseId - WHERE - P.PersonId = ? - AND W.WorkoutId = ? - AND T.TopSetId = ?""", - [person_id, workout_id, topset_id], one=True) - - if topset is None: - return render_template('error.html', error='404', message=f'Unable to find TopSet({topset_id}) in Workout({workout_id}) completed by Person({person_id})', url=url_for('show_workout_for_person', person_id=person_id, workout_id=workout_id)) - +@ app.route("/person//workout//topset/", methods=['GET', 'POST']) +@ swag_from('swagger/get_topset.yml') +@ validate_topset +def get_topset(person_id, workout_id, topset_id): if request.method == 'POST': exercise_id = request.form.get("exercise_id") repetitions = request.form.get("repetitions") weight = request.form.get("weight") - query_db('UPDATE TopSet SET ExcerciseId=?, Repetitions=?, Weight=? WHERE TopSetId=?', [ - exercise_id, repetitions, weight, topset_id]) + db.update_topset(exercise_id, repetitions, weight, topset_id) - return redirect(url_for('show_workout_for_person', person_id=person_id, workout_id=workout_id)) + return redirect(url_for('get_workout', person_id=person_id, workout_id=workout_id)) - return render_template('topset.html', person_id=person_id, workout_id=workout_id, topset_id=topset_id, topset=topset, exercises=query_db('select * from Excercise')) + topset = db.get_topset_final(person_id, workout_id, topset_id) + return render_template('topset.html', topset=topset) -@app.route("/person//workout//topset", methods=['POST']) -def add_topset_to_workout_for_person(person_id, workout_id): - """Add top set to workout - Add a topset to a workout completed by a person - --- - tags: - - Topset - parameters: - - name: person_id - in: path - type: number - required: true - - name: workout_id - in: path - type: number - required: true - responses: - 200: - description: A list of topsets in a selected workout - """ - workout_info = query_db(""" - SELECT - P.Name, - W.StartDate - FROM - Person P - LEFT JOIN Workout W ON P.PersonId = W.PersonId - WHERE - P.PersonId = ? - AND W.WorkoutId = ? - LIMIT 1""", - [person_id, workout_id], one=True) - - if workout_info is None: - return render_template('error.html', error='404', message=f'Unable to find Workout({workout_id}) completed by Person({person_id})', url=url_for('display_workouts_for_person', person_id=person_id)) - +@ app.route("/person//workout//topset", methods=['POST']) +@ swag_from('swagger/create_topset.yml') +@ validate_workout +def create_topset(person_id, workout_id): exercise_id = request.form.get("exercise_id") repetitions = request.form.get("repetitions") weight = request.form.get("weight") - query_db('INSERT INTO TopSet (WorkoutId, ExcerciseId, Repetitions, Weight) VALUES (?, ?, ?, ?)', [ - workout_id, exercise_id, repetitions, weight]) - - return redirect(url_for('show_workout_for_person', person_id=person_id, workout_id=workout_id)) + db.create_topset(workout_id, exercise_id, repetitions, weight) + return redirect(url_for('get_workout', person_id=person_id, workout_id=workout_id)) -@app.route("/person//workout//topset//delete", methods=['GET', 'DELETE']) -def delete_topset_from_workout_for_person(person_id, workout_id, topset_id): - """Delete top set - Add a topset to a workout completed by a person - --- - tags: - - Topset - parameters: - - name: person_id - in: path - type: number - required: true - - name: workout_id - in: path - type: number - required: true - - name: topset_id - in: path - type: number - required: true - responses: - 200: - description: A list of topsets in a selected workout - """ - topset = query_db(""" - SELECT - P.Name, - W.StartDate, - E.ExcerciseId, - E.Name, - T.Repetitions, - T.Weight - FROM - Person P - LEFT JOIN Workout W ON P.PersonId = W.PersonId - INNER JOIN TopSet T ON W.WorkoutId = T.WorkoutId - INNER JOIN Excercise E ON T.ExcerciseId = E.ExcerciseId - WHERE - P.PersonId = ? - AND W.WorkoutId = ? - AND T.TopSetId = ?""", - [person_id, workout_id, topset_id], one=True) - - if topset is None: - return render_template('error.html', error='404', message=f'Unable to find TopSet({topset_id}) in Workout({workout_id}) completed by Person({person_id})', url=url_for('show_workout_for_person', person_id=person_id, workout_id=workout_id)) - - query_db('DELETE FROM TopSet WHERE TopSetId=?', [ - topset_id]) - - return redirect(url_for('show_workout_for_person', person_id=person_id, workout_id=workout_id)) +@ app.route("/person//workout//topset//delete", methods=['GET', 'DELETE']) +@ swag_from('swagger/delete_topset.yml') +@ validate_topset +def delete_topset(person_id, workout_id, topset_id): + db.delete_topset(topset_id) + return redirect(url_for('get_workout', person_id=person_id, workout_id=workout_id)) -@app.context_processor +@ app.context_processor def my_utility_processor(): def is_selected_page(url): @@ -424,27 +121,12 @@ def my_utility_processor(): if 'person_id' in request.view_args: person_id = request.view_args['person_id'] - return query_db(""" - SELECT - P.PersonId, - P.Name, - COUNT(W.WorkoutId) AS NumberOfWorkouts, - CASE P.PersonId - WHEN ? - THEN 1 - ELSE 0 - END IsActive - FROM - Person P - LEFT JOIN Workout W ON P.PersonId = W.PersonId - GROUP BY - P.PersonId""", [person_id]) + return db.get_people_and_workout_count(person_id) - return dict(get_list_of_people_and_workout_count=get_list_of_people_and_workout_count, is_selected_page=is_selected_page) + def get_first_element_from_list_with_matching_attribute(list, attribute, value): + for element in list: + if element[attribute] == value: + return element + return None - -@app.teardown_appcontext -def close_connection(exception): - db = getattr(g, '_database', None) - if db is not None: - db.close() + return dict(get_list_of_people_and_workout_count=get_list_of_people_and_workout_count, is_selected_page=is_selected_page, get_first_element_from_list_with_matching_attribute=get_first_element_from_list_with_matching_attribute) diff --git a/config.py b/config.py new file mode 100644 index 0000000..38a2e9e --- /dev/null +++ b/config.py @@ -0,0 +1,5 @@ +TESTING = True +DEBUG = True +FLASK_ENV = 'development' +SECRET_KEY = '' +DATABASE_URI = 'workout.db' diff --git a/db.py b/db.py new file mode 100644 index 0000000..28cf80e --- /dev/null +++ b/db.py @@ -0,0 +1,170 @@ +import datetime +import sqlite3 + +from utils import get_all_exercises_from_topsets, get_workouts + + +class DataBase(): + def __init__(self, app): + self.DATABASE_URI = app.config['DATABASE_URI'] + + def execute(self, query, args=(), one=False, commit=False): + conn = sqlite3.connect(self.DATABASE_URI) + conn.row_factory = sqlite3.Row + cur = conn.execute(query, args) + rv = cur.fetchall() + if commit: + conn.commit() + cur.close() + return (rv[0] if rv else None) if one else rv + + def get_exercises(self): + exercises = self.execute('SELECT * FROM Excercise') + return [{"ExcerciseId": e['ExcerciseId'], "Name": e['Name']} for e in exercises] + + def get_person(self, person_id): + person = self.execute( + 'SELECT * FROM Person WHERE PersonId=? LIMIT 1', [person_id], one=True) + return person + + def get_workout(self, person_id, workout_id): + workout = self.execute('SELECT W.WorkoutId FROM Person P, Workout W WHERE P.PersonId=W.PersonId AND P.PersonId=? AND W.WorkoutId=? LIMIT 1', [ + person_id, workout_id], one=True) + return workout + + def get_topset(self, person_id, workout_id, topset_id): + topset = self.execute(""" + SELECT T.TopSetId + FROM Person P, Workout W, TopSet T + WHERE W.PersonId=W.PersonId AND W.WorkoutId=T.WorkoutId AND P.PersonId=? AND W.WorkoutId = ? AND T.TopSetId = ? + LIMIT 1""", [person_id, workout_id, topset_id], one=True) + return topset + + def delete_workout(self, workout_id): + self.execute('DELETE FROM Workout WHERE WorkoutId=?', + [workout_id], commit=True) + + def update_topset(self, exercise_id, repetitions, weight, topset_id): + self.execute('UPDATE TopSet SET ExcerciseId=?, Repetitions=?, Weight=? WHERE TopSetId=?', [ + exercise_id, repetitions, weight, topset_id], commit=True) + + def create_topset(self, workout_id, exercise_id, repetitions, weight): + self.execute('INSERT INTO TopSet (WorkoutId, ExcerciseId, Repetitions, Weight) VALUES (?, ?, ?, ?)', [ + workout_id, exercise_id, repetitions, weight], commit=True) + + def delete_topset(self, topset_id): + self.execute('DELETE FROM TopSet WHERE TopSetId=?', [ + topset_id], commit=True) + + def create_workout(self, person_id): + now = datetime.datetime.now() + date_string = now.strftime('%Y-%m-%d') + print(f'Creating workout for {person_id} at {date_string}') + self.execute('INSERT INTO Workout (PersonId, StartDate) VALUES (?, ?)', [ + person_id, date_string], commit=True) + w = self.execute('SELECT MAX(WorkoutId) AS WorkoutId FROM Workout WHERE PersonId=?', [ + person_id], one=True) + return w['WorkoutId'] + + def get_people_and_workout_count(self, person_id): + return self.execute(""" + SELECT + P.PersonId, + P.Name, + COUNT(W.WorkoutId) AS NumberOfWorkouts, + CASE P.PersonId + WHEN ? + THEN 1 + ELSE 0 + END IsActive + FROM + Person P + LEFT JOIN Workout W ON P.PersonId = W.PersonId + GROUP BY + P.PersonId""", [person_id]) + + def get_person_final(self, person_id): + topsets = self.execute(""" + SELECT + P.PersonId, + P.Name AS PersonName, + W.WorkoutId, + W.StartDate, + T.TopSetId, + E.ExcerciseId, + E.Name AS ExerciseName, + T.Repetitions, + T.Weight + FROM Person P + LEFT JOIN Workout W ON P.PersonId=W.PersonId + LEFT JOIN TopSet T ON W.WorkoutId=T.WorkoutId + LEFT JOIN Excercise E ON T.ExcerciseId=E.ExcerciseId + WHERE P.PersonId=?""", [person_id]) + + return { + 'PersonId': next((t['PersonId'] for t in topsets), -1), + 'PersonName': next((t['PersonName'] for t in topsets), 'Unknown'), + 'Exercises': get_all_exercises_from_topsets(topsets), + 'Workouts': get_workouts(topsets) + } + + def get_workout_final(self, person_id, workout_id): + topsets = self.execute(""" + SELECT + P.PersonId, + P.Name AS PersonName, + W.WorkoutId, + W.StartDate, + T.TopSetId, + E.ExcerciseId, + E.Name AS ExerciseName, + T.Repetitions, + T.Weight + FROM Person P + LEFT JOIN Workout W ON P.PersonId=W.PersonId + LEFT JOIN TopSet T ON W.WorkoutId=T.WorkoutId + LEFT JOIN Excercise E ON T.ExcerciseId=E.ExcerciseId + WHERE P.PersonId=? + AND W.WorkoutId = ?""", [person_id, workout_id]) + + return { + 'PersonId': next((t['PersonId'] for t in topsets), -1), + 'PersonName': next((t['PersonName'] for t in topsets), 'Unknown'), + 'WorkoutId': workout_id, + 'StartDate': next((t['StartDate'] for t in topsets), 'Unknown'), + 'Exercises': self.get_exercises(), + 'TopSets': [{"TopSetId": t['TopSetId'], "ExcerciseId": t['ExcerciseId'], "ExerciseName": t['ExerciseName'], "Weight": t['Weight'], "Repetitions": t['Repetitions']} for t in topsets if t['TopSetId'] is not None] + } + + def get_topset_final(self, person_id, workout_id, topset_id): + topset = self.execute(""" + SELECT + P.PersonId, + P.Name AS PersonName, + W.WorkoutId, + W.StartDate, + T.TopSetId, + E.ExcerciseId, + E.Name AS ExerciseName, + T.Repetitions, + T.Weight + FROM Person P + INNER JOIN Workout W ON P.PersonId=W.PersonId + INNER JOIN TopSet T ON W.WorkoutId=T.WorkoutId + INNER JOIN Excercise E ON T.ExcerciseId=E.ExcerciseId + WHERE P.PersonId=? + AND W.WorkoutId = ? + AND T.TopSetId = ?""", [person_id, workout_id, topset_id], one=True) + + return { + 'PersonId': topset['PersonId'], + 'PersonName': topset['PersonName'], + 'WorkoutId': workout_id, + 'StartDate': topset['StartDate'], + 'Exercises': self.get_exercises(), + "TopSetId": topset['TopSetId'], + "ExcerciseId": topset['ExcerciseId'], + "ExerciseName": topset['ExerciseName'], + "Weight": topset['Weight'], + "Repetitions": topset['Repetitions'] + } diff --git a/decorators.py b/decorators.py new file mode 100644 index 0000000..e6cb587 --- /dev/null +++ b/decorators.py @@ -0,0 +1,42 @@ +from functools import wraps + +from flask import render_template, url_for + + +def validate_person(func): + @wraps(func) + def wrapper(*args, **kwargs): + person_id = kwargs.get('person_id') + from app import db + person = db.get_person(person_id) + if person is None: + return render_template('error.html', error='404', message=f'Unable to find Person({person_id})', url='/') + return func(*args, **kwargs) + return wrapper + + +def validate_workout(func): + @wraps(func) + def wrapper(*args, **kwargs): + person_id = kwargs.get('person_id') + workout_id = kwargs.get('workout_id') + from app import db + workout = db.get_workout(person_id, workout_id) + if workout is None: + return render_template('error.html', error='404', message=f'Unable to find Workout({workout_id}) completed by Person({person_id})', url=url_for('get_person', person_id=person_id)) + return func(*args, **kwargs) + return wrapper + + +def validate_topset(func): + @wraps(func) + def wrapper(*args, **kwargs): + person_id = kwargs.get('person_id') + workout_id = kwargs.get('workout_id') + topset_id = kwargs.get('topset_id') + from app import db + topset = db.get_topset(person_id, workout_id, topset_id) + if topset is None: + return render_template('error.html', error='404', message=f'Unable to find TopSet({topset_id}) in Workout({workout_id}) completed by Person({person_id})', url=url_for('get_workout', person_id=person_id, workout_id=workout_id)) + return func(*args, **kwargs) + return wrapper diff --git a/swagger/create_topset.yml b/swagger/create_topset.yml new file mode 100644 index 0000000..7d98a42 --- /dev/null +++ b/swagger/create_topset.yml @@ -0,0 +1,17 @@ +Add top set to workout +Add a topset to a workout completed by a person +--- +tags: + - Topset +parameters: + - name: person_id + in: path + type: number + required: true + - name: workout_id + in: path + type: number + required: true +responses: + 200: + description: A list of topsets in a selected workout diff --git a/swagger/create_workout.yml b/swagger/create_workout.yml new file mode 100644 index 0000000..a074109 --- /dev/null +++ b/swagger/create_workout.yml @@ -0,0 +1,13 @@ +Create new workout +Creates a workout with current date and then redirects to newly created workout +--- +tags: + - Workout +parameters: + - name: person_id + in: path + type: number + required: true +responses: + 200: + description: View of newly created workout diff --git a/swagger/dashboard.yml b/swagger/dashboard.yml new file mode 100644 index 0000000..6d00cd4 --- /dev/null +++ b/swagger/dashboard.yml @@ -0,0 +1,8 @@ +Dashboard page +Displays stats and a list of all people and there rep maxes for each exercise +--- +tags: + - Dashboard +responses: + 200: + description: A list of all people and there rep maxes for each exercise diff --git a/swagger/delete_topset.yml b/swagger/delete_topset.yml new file mode 100644 index 0000000..9b4edf5 --- /dev/null +++ b/swagger/delete_topset.yml @@ -0,0 +1,21 @@ +Delete top set +Add a topset to a workout completed by a person +--- +tags: + - Topset +parameters: + - name: person_id + in: path + type: number + required: true + - name: workout_id + in: path + type: number + required: true + - name: topset_id + in: path + type: number + required: true +responses: + 200: + description: A list of topsets in a selected workout diff --git a/swagger/delete_workout.yml b/swagger/delete_workout.yml new file mode 100644 index 0000000..6d39a2c --- /dev/null +++ b/swagger/delete_workout.yml @@ -0,0 +1,17 @@ +Delete workout +Deletes selected workout completed by a person +--- +tags: + - Workout +parameters: + - name: person_id + in: path + type: number + required: true + - name: workout_id + in: path + type: number + required: true +responses: + 200: + description: Redirect to workouts list page for person diff --git a/swagger/get_person.yml b/swagger/get_person.yml new file mode 100644 index 0000000..8ebe731 --- /dev/null +++ b/swagger/get_person.yml @@ -0,0 +1,13 @@ +Display all workouts for a person +Displays stats and a list of all people and there rep maxes for each exercise +--- +tags: + - Person +parameters: + - name: person_id + in: path + type: number + equired: true +responses: + 200: + description: A list of all people and there rep maxes for each exercise diff --git a/swagger/get_topset.yml b/swagger/get_topset.yml new file mode 100644 index 0000000..c8a94da --- /dev/null +++ b/swagger/get_topset.yml @@ -0,0 +1,21 @@ +Display/Create new top set +Displays stats and a list of all people and there rep maxes for each exercise +--- +tags: + - Topset +parameters: + - name: person_id + in: path + type: number + required: true + - name: workout_id + in: path + type: number + required: true + - name: topset_id + in: path + type: number + required: true +responses: + 200: + description: A list of topsets in a selected workout diff --git a/swagger/get_workout.yml b/swagger/get_workout.yml new file mode 100644 index 0000000..bf9876b --- /dev/null +++ b/swagger/get_workout.yml @@ -0,0 +1,17 @@ +Display a workout +Displays a selected workout with options to edit/delete existing and add new topsets +--- +tags: + - Workout +parameters: + - name: person_id + in: path + type: number + required: true + - name: workout_id + in: path + type: number + required: true +responses: + 200: + description: A list of topsets in a selected workout diff --git a/templates/base.html b/templates/base.html index 90508fa..c7f1af0 100644 --- a/templates/base.html +++ b/templates/base.html @@ -86,7 +86,7 @@ {% for p in get_list_of_people_and_workout_count() %}
  • - diff --git a/templates/index.html b/templates/index.html index 0c96808..d108194 100644 --- a/templates/index.html +++ b/templates/index.html @@ -11,7 +11,7 @@ Current rep maxes @@ -173,7 +173,7 @@ Current rep maxes diff --git a/templates/topset.html b/templates/topset.html index 698a071..c81bc37 100644 --- a/templates/topset.html +++ b/templates/topset.html @@ -6,10 +6,10 @@
    -

    {{ topset['Name'] }}

    +

    {{ topset['PersonName'] }}

    {{ topset['StartDate'] }}
    - Delete topset @@ -30,7 +30,7 @@ - {% for e in exercises %} + {% for e in workout['Exercises'] %} {% endfor %} diff --git a/templates/workouts.html b/templates/workouts.html index 074ce63..a638020 100644 --- a/templates/workouts.html +++ b/templates/workouts.html @@ -6,7 +6,7 @@
    -

    {{ person['Name'] }}

    +

    {{ person['PersonName'] }}

    List of workouts
    @@ -22,10 +22,10 @@ class="p-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"> Date - {% for e in exercises %} + {% for e in person['Exercises'] %} - {{ e['Name'] }} + {{ e['ExerciseName'] }} {% endfor %} - {% for w in workouts %} + {% for w in person['Workouts'] %} - {{ w['start_date'] }} + {{ w['StartDate'] }} - {% for e in exercises %} + {% for e in person['Exercises'] %} - {% if e['ExcerciseId'] in w['topset_exercises'] %} - {{ w['topset_exercises'][e['ExcerciseId']] }} + {% set topset_exercise = + get_first_element_from_list_with_matching_attribute(w['TopSets'], 'ExcerciseId', + e['ExcerciseId']) %} + {% if topset_exercise %} + {{ topset_exercise['Repetitions'] }} x {{ topset_exercise['Weight'] }}kg {% endif %} {% endfor %} - Edit
    diff --git a/utils.py b/utils.py new file mode 100644 index 0000000..93800f2 --- /dev/null +++ b/utils.py @@ -0,0 +1,28 @@ +def get_workouts(topsets): + # Get all unique workout_ids (No duplicates) + workout_ids = set([t['WorkoutId'] + for t in topsets if t['WorkoutId'] is not None]) + + # Group topsets into workouts + workouts = [] + for workout_id in workout_ids: + topsets_in_workout = [ + t for t in topsets if t['WorkoutId'] == workout_id] + workouts.append({ + 'WorkoutId': workout_id, + 'StartDate': topsets_in_workout[0]['StartDate'], + 'TopSets': [{"TopSetId": t['TopSetId'], "ExcerciseId": t['ExcerciseId'], "ExerciseName": t['ExerciseName'], "Weight": t['Weight'], "Repetitions": t['Repetitions']} for t in topsets_in_workout] + }) + return workouts + + +def get_all_exercises_from_topsets(topsets): + exercise_ids = set([t['ExcerciseId'] + for t in topsets if t['ExcerciseId'] is not None]) + exercises = [] + for exercise_id in exercise_ids: + exercises.append({ + 'ExcerciseId': exercise_id, + 'ExerciseName': next((t['ExerciseName'] for t in topsets if t['ExcerciseId'] == exercise_id), 'Unknown') + }) + return exercises