From 9927473dad17b1298b533f416acb924df15fd4e0 Mon Sep 17 00:00:00 2001 From: zoeyande2 Date: Sun, 8 Mar 2026 21:10:55 -0400 Subject: [PATCH] Worked on views and models --- planning.md | 2 +- workouts/app/database.sqlite | Bin 0 -> 139264 bytes workouts/app/exlist.py | 24 +++++++ workouts/app/migrations/0001_initial.py | 31 +++++++++ ...ercise_distance_alter_exercise_timemins.py | 24 +++++++ workouts/app/migrations/__init__.py | 0 workouts/app/models.py | 40 ++++++++++++ workouts/app/views.py | 61 ++++++++++++++++++ 8 files changed, 181 insertions(+), 1 deletion(-) create mode 100644 workouts/app/database.sqlite create mode 100644 workouts/app/exlist.py create mode 100644 workouts/app/migrations/0001_initial.py create mode 100644 workouts/app/migrations/0002_alter_exercise_distance_alter_exercise_timemins.py create mode 100644 workouts/app/migrations/__init__.py create mode 100644 workouts/app/models.py create mode 100644 workouts/app/views.py diff --git a/planning.md b/planning.md index 6255aa8..f0d6ad2 100644 --- a/planning.md +++ b/planning.md @@ -5,7 +5,7 @@ The app's target user is a person who struggles with planning what exercises to ## 2. What need or problem will your app solve? How do you know this is really a need for your target user? How does the target user currently deal with the problem? -This solves the problem that my current workout tracking app costs money (or I have to watch annoying ads while I'm trying to log information). Currently, I just turn off my data and internet when I'm in the gym to override the app and keep the ad from loading, but this means I don't get notifications from my other apps and I can only listen to the music I have downloaded. Currently, I just do the same exercises over and over, and it gets really boring, or I waste time searching for new workouts that I don't ever try. Eliminating the mental load of choosing a specific workout will help me (and others) be more likely to actually go to the gym. +Currently, I just do the same exercises over and over, and it gets really boring, or I waste time searching for new workouts that I don't ever try. Eliminating the mental load of choosing a specific workout will help me (and others) be more likely to actually go to the gym. ## 3. How will your app's design meet the need you have identified? I will create a bank of exercises sorted by what muscle group they target and that have attributes for weight, reps, sets, or time. I will have a path that allows the user to add their own exercises as well. I will support returning a random exercise based on the muscle group it works. I will also support updating the recommended weight, reps, time etc for each exercise. diff --git a/workouts/app/database.sqlite b/workouts/app/database.sqlite new file mode 100644 index 0000000000000000000000000000000000000000..d14166f2563fe30a90c95da2da3de8f637ff33a5 GIT binary patch literal 139264 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 literal 0 HcmV?d00001 diff --git a/workouts/app/exlist.py b/workouts/app/exlist.py new file mode 100644 index 0000000..96fd269 --- /dev/null +++ b/workouts/app/exlist.py @@ -0,0 +1,24 @@ +from app.models import Exercise +import csv + +starting_exs = { + "Treadmill Walking":{"name":"Treadmill Walking","musclegroup":"cardio","weight":0,"reps":1,"sets":1,"timemins":20,"distance":1}, + "Treadmill Running":{"name":"Treadmill Running","musclegroup":"cardio","weight":0,"reps":1,"sets":1,"timemins":13,"distance":1}, + "Bicep Curls":{"name":"Bicep Curls","musclegroup":"arms","weight":10,"reps":10,"sets":3,"timemins":0,"distance":0}, + "Hammer Curls":{"name":"Hammer Curls","musclegroup":"arms","weight":15,"reps":8,"sets":3,"timemins":0,"distance":0}, + "Leg Press":{"name":"Leg Press","musclegroup":"legs","weight":90,"reps":10,"sets":3,"timemins":0,"distance":0}, + "Calf Raises":{"name":"Calf Raises","musclegroup":"legs","weight":0,"reps":20,"sets":3,"timemins":0,"distance":0}, + "Sit-ups":{"name":"Sit-ups","musclegroup":"abs","weight":0,"reps":15,"sets":3,"timemins":0,"distance":0}, + "Weighted Twists":{"name":"Weighted Twists","musclegroup":"abs","weight":5,"reps":20,"sets":3,"timemins":0,"distance":0}, + "Planks":{"name":"Planks","musclegroup":"abs","weight":0,"reps":1,"sets":3,"timemins":0,"distance":0}, + "Lat Pulldowns":{"name":"Lat Pulldowns","musclegroup":"back","weight":50,"reps":8,"sets":3,"timemins":0,"distance":0}, + "Bench Press":{"name":"Bench Press","musclegroup":"chest","weight":45,"reps":4,"sets":3,"timemins":0,"distance":0}, + "Push-ups":{"name":"Push-ups","musclegroup":"chest","weight":0,"reps":10,"sets":3,"timemins":0,"distance":0}, + "Seated Rows":{"name":"Seated Rows","musclegroup":"back","weight":40,"reps":10,"sets":3,"timemins":0,"distance":0}} + +def init_starting_exs(): + for ex in starting_exs.keys(): + if not (Exercise.objects.filter(name=ex).exists()): + info = starting_exs[ex] + Exercise.from_dict(info).save() + print ("Successfully imported library of exercises.") diff --git a/workouts/app/migrations/0001_initial.py b/workouts/app/migrations/0001_initial.py new file mode 100644 index 0000000..03251ff --- /dev/null +++ b/workouts/app/migrations/0001_initial.py @@ -0,0 +1,31 @@ +# Generated by Django 5.1.4 on 2026-03-08 16:44 + +import banjo.models +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Exercise', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', banjo.models.StringField(default='')), + ('musclegroup', banjo.models.StringField(default='')), + ('weight', banjo.models.IntegerField(default=0)), + ('reps', banjo.models.IntegerField(default=0)), + ('sets', banjo.models.IntegerField(default=0)), + ('timemins', banjo.models.IntegerField(default=0)), + ('distance', banjo.models.IntegerField(default=0)), + ], + options={ + 'abstract': False, + }, + ), + ] diff --git a/workouts/app/migrations/0002_alter_exercise_distance_alter_exercise_timemins.py b/workouts/app/migrations/0002_alter_exercise_distance_alter_exercise_timemins.py new file mode 100644 index 0000000..ed27f87 --- /dev/null +++ b/workouts/app/migrations/0002_alter_exercise_distance_alter_exercise_timemins.py @@ -0,0 +1,24 @@ +# Generated by Django 5.1.4 on 2026-03-09 00:49 + +import banjo.models +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('app', '0001_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='exercise', + name='distance', + field=banjo.models.FloatField(default=0.0), + ), + migrations.AlterField( + model_name='exercise', + name='timemins', + field=banjo.models.FloatField(default=0.0), + ), + ] diff --git a/workouts/app/migrations/__init__.py b/workouts/app/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/workouts/app/models.py b/workouts/app/models.py new file mode 100644 index 0000000..936ed3c --- /dev/null +++ b/workouts/app/models.py @@ -0,0 +1,40 @@ +from banjo.models import Model, StringField, IntegerField, FloatField, ForeignKey +from banjo.http import BadRequest + +class Exercise(Model): + name = StringField() + musclegroup = StringField() + weight = IntegerField() + reps = IntegerField() + sets = IntegerField() + timemins = FloatField() + distance = FloatField() + + + def instructions(self): + instr = '' + if self.musclegroup == "Cardio": + timemins = str(self.timemins) + distance = str(self.distance) + instr = "Do exercise for "+timemins+" minute(s) or until "+distance+" mile(s) has been completed." + else: + weight = str(self.weight) + reps = str(self.reps) + sets = str(self.sets) + if self.weight == 0: + instr = "Do exercise with bodyweight for "+reps+" rep(s). Repeat for "+sets+" set(s)" + else: + instr = "Do exercise with "+weight+" lb(s) for "+reps+" rep(s). Repeat for "+sets+" set(s)" + if self.timemins != 0: + timemins = str(self.timemins) + instr += " or for "+timemins+" minute(s)" + instr += "." + return instr + + def __repr__(self): + name = self.name + musclegroup = self.musclegroup + instr = self.instructions() + return name+": This is a(n) "+musclegroup+" exercise. Instructions: "+instr + + diff --git a/workouts/app/views.py b/workouts/app/views.py new file mode 100644 index 0000000..02a9858 --- /dev/null +++ b/workouts/app/views.py @@ -0,0 +1,61 @@ +from banjo.urls import route_get, route_post +from banjo.http import BadRequest, NotFound +from app.models import Exercise +from random import choice, sample +from app.exlist import init_starting_exs + +init_starting_exs() + +#Done +@route_get("all", args = {}) +def list_all_exercises_sorted(params): + exercises = Exercise.objects.all() + all_ex = {'cardio':[],'legs':[],'arms':[],'back':[],'chest':[],"abs":[]} + musclegroups = all_ex.keys() + for mg in musclegroups: + for ex in exercises: + if ex.musclegroup == mg: + all_ex[mg].append(ex.__repr__()) + return all_ex + +#Done +@route_get("workout_length", args= {"length": int}) #Returns n many exercises & instructions at random +def workout_with_length(params): + if Exercise.objects.all().count() >= params["length"]: + workout = [] + xlist = sample(list(Exercise.objects.all()),params["length"]) + for ex in xlist: + xinfo = ex.__repr__() + workout.append(xinfo) + return workout + else: + raise BadRequest("There are not enough exercises to pick that many.") + +@route_get("full_body", args={}) #Returns one random exercise from each muscle group +def workout_full_body(params): + return "in progress" + +@route_post("edit_exercise", args={"name":str,"category to edit":str,"new value":int}) #Allows a change to an exercise weight, reps, sets, time, or distance (numbers) +def edit_exercise(params): + return "in progress" + +#Done +@route_post("new_exercise",args={"name":str,"musclegroup":str,"weight":int,"reps":int,"sets":int,"timemins":int,"distance":int}) +def add_new_exercise(params): + if Exercise.objects.filter(name=params["name"]).exists(): + return "This exercise already exists." + else: + exercise = Exercise.from_dict(params) + exercise.save() + return exercise.__repr__() + +#Done +@route_get("delete_exercise", args ={"name":str}) #Done +def delete_exercise(params): + try: + ex = Exercise.objects.get(name=params["name"]) + ex.delete() + return "Successfully deleted!" + except Exercise.DoesNotExist: + raise NotFound("No such exercise exists.") +