Compare commits
10 Commits
d31dbfbb54
...
7bfa16c10a
Author | SHA1 | Date | |
---|---|---|---|
|
7bfa16c10a | ||
|
e23b136b3a | ||
|
f9ff40312b | ||
|
647531f96c | ||
|
d70de69f3d | ||
|
9ec636fbef | ||
|
f0a8f6b75c | ||
|
2f9fbc4d90 | ||
|
213e79a20f | ||
|
51a4126763 |
@ -1,3 +0,0 @@
|
||||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
@ -26,30 +26,24 @@ def import_fitnotes_db(filename, user, fitnotes_to_lift=DEFAULT_MAPPING):
|
||||
# lift name => id
|
||||
lift_ids = {_clean_name(l.name): l.id for l in Lift.objects.all()}
|
||||
|
||||
# build mapping FitNotes exercise id => our lift id
|
||||
lift_id_mapping = {}
|
||||
|
||||
conn = sqlite3.connect(filename)
|
||||
cur = conn.cursor()
|
||||
for fnid, ename in cur.execute('SELECT _id, name FROM exercise WHERE exercise_type_id=0'):
|
||||
cleaned = _clean_name(ename)
|
||||
|
||||
if cleaned not in fitnotes_to_lift:
|
||||
lift_id_mapping[fnid] = cleaned
|
||||
else:
|
||||
lift_id_mapping[fnid] = lift_ids[fitnotes_to_lift[cleaned]]
|
||||
|
||||
with transaction.atomic():
|
||||
Set.objects.filter(source='fitnotes').delete()
|
||||
for fnid, date, weight_kg, reps in cur.execute(
|
||||
'SELECT exercise_id, date, metric_weight, reps FROM training_log'):
|
||||
count = 0
|
||||
for fname, date, weight_kg, reps in cur.execute(
|
||||
'SELECT name, date, metric_weight, reps FROM training_log, exercise '
|
||||
'WHERE exercise_type_id=0 and exercise_id=exercise._id'
|
||||
):
|
||||
|
||||
# error if mapping wasn't found and there's a workout using it
|
||||
if isinstance(lift_id_mapping[fnid], str):
|
||||
raise ValueError('no known conversion for fitnotes exercise "{}"'.format(
|
||||
lift_id_mapping[fnid]))
|
||||
|
||||
lift_id = lift_id_mapping[fnid]
|
||||
try:
|
||||
lift_id = lift_ids[fitnotes_to_lift[_clean_name(fname)]]
|
||||
except KeyError:
|
||||
raise ValueError('no known conversion for fitnotes exercise "{}"'.format(fname))
|
||||
|
||||
Set.objects.create(lift_id=lift_id, date=date, weight_kg=weight_kg, reps=reps,
|
||||
source='fitnotes', user=user)
|
||||
count += 1
|
||||
|
||||
return count
|
||||
|
@ -1,3 +0,0 @@
|
||||
from django.db import models
|
||||
|
||||
# Create your models here.
|
BIN
fitnotes/testdata/baddata.fitnotes
vendored
BIN
fitnotes/testdata/baddata.fitnotes
vendored
Binary file not shown.
@ -31,7 +31,8 @@ class TestFitnotesImport(TestCase):
|
||||
|
||||
def test_basic_import(self):
|
||||
# ensure that the data comes in
|
||||
import_fitnotes_db('fitnotes/testdata/example.fitnotes', self.user, self.good_mapping)
|
||||
num = import_fitnotes_db('fitnotes/testdata/example.fitnotes', self.user, self.good_mapping)
|
||||
assert num == 9
|
||||
assert Set.objects.filter(lift=self.bench).count() == 4
|
||||
assert Set.objects.filter(lift=self.squat).count() == 5
|
||||
|
||||
@ -53,10 +54,9 @@ class TestFitnotesImport(TestCase):
|
||||
import_fitnotes_db('fitnotes/testdata/example.fitnotes', self.user, self.bad_mapping)
|
||||
assert Set.objects.filter(lift=self.bench).count() == 0
|
||||
|
||||
def test_bad_data_import(self):
|
||||
def test_bad_data_doesnt_overwrite(self):
|
||||
# good db then bad db, should fail without screwing up existing data
|
||||
import_fitnotes_db('fitnotes/testdata/example.fitnotes', self.user, self.good_mapping)
|
||||
with self.assertRaises(Exception):
|
||||
# baddata.fitnotes has all lift ids set to 9999
|
||||
import_fitnotes_db('fitnotes/testdata/baddata.fitnotes', self.user, self.good_mapping)
|
||||
with self.assertRaises(ValueError):
|
||||
import_fitnotes_db('fitnotes/testdata/example.fitnotes', self.user, self.bad_mapping)
|
||||
assert Set.objects.count() == 9
|
||||
|
@ -3,5 +3,5 @@ from django.conf.urls import url
|
||||
from . import views
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^upload/$', views.fitnotes_upload),
|
||||
url(r'^$', views.fitnotes_upload),
|
||||
]
|
||||
|
@ -16,6 +16,7 @@ class Migration(migrations.Migration):
|
||||
('id', models.AutoField(verbose_name='ID', auto_created=True, serialize=False, primary_key=True)),
|
||||
('name', models.CharField(max_length=100)),
|
||||
('weight_kg', models.DecimalField(max_digits=7, decimal_places=3)),
|
||||
('weight_lb', models.DecimalField(max_digits=7, decimal_places=3)),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
|
@ -7,8 +7,8 @@ from django.db import models, migrations
|
||||
def make_bars(apps, schema_editor):
|
||||
Bar = apps.get_model('inventory', 'Bar')
|
||||
Bar.objects.bulk_create([
|
||||
Bar(name="Women's Olympic", weight_kg='15'),
|
||||
Bar(name="Men's Olympic", weight_kg='20'),
|
||||
Bar(id=1, name="Men's Olympic", weight_kg='20', weight_lb='45'),
|
||||
Bar(id=2, name="Women's Olympic", weight_kg='15', weight_lb='35'),
|
||||
])
|
||||
|
||||
|
||||
|
@ -1,22 +1,24 @@
|
||||
from django.db import models
|
||||
from common import to_lb
|
||||
from common import remove_exponent, to_lb
|
||||
|
||||
|
||||
class Bar(models.Model):
|
||||
name = models.CharField(max_length=100)
|
||||
weight_kg = models.DecimalField(max_digits=7, decimal_places=3)
|
||||
weight_lb = models.DecimalField(max_digits=7, decimal_places=3)
|
||||
|
||||
def __str__(self):
|
||||
return '{} ({}lb / {}kg)'.format(self.name, self.weight_kg, self.weight_lb)
|
||||
|
||||
@property
|
||||
def weight_lb(self):
|
||||
return to_lb(self.weight_kg)
|
||||
return '{} ({}kg / {}lb)'.format(self.name, remove_exponent(self.weight_kg),
|
||||
self.weight_lb)
|
||||
|
||||
|
||||
class Lift(models.Model):
|
||||
name = models.CharField(max_length=200)
|
||||
|
||||
@property
|
||||
def display_name(self):
|
||||
return self.name
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
@ -1,3 +0,0 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
21
lifting/migrations/0002_liftingoptions_default_bar.py
Normal file
21
lifting/migrations/0002_liftingoptions_default_bar.py
Normal file
@ -0,0 +1,21 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('inventory', '3000_initial_data'),
|
||||
('lifting', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='liftingoptions',
|
||||
name='default_bar',
|
||||
field=models.ForeignKey(default=1, to='inventory.Bar'),
|
||||
preserve_default=False,
|
||||
),
|
||||
]
|
19
lifting/migrations/0003_auto_20150412_1739.py
Normal file
19
lifting/migrations/0003_auto_20150412_1739.py
Normal file
@ -0,0 +1,19 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('lifting', '0002_liftingoptions_default_bar'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='liftingoptions',
|
||||
name='default_bar',
|
||||
field=models.ForeignKey(default=1, to='inventory.Bar'),
|
||||
),
|
||||
]
|
@ -1,7 +1,8 @@
|
||||
from decimal import Decimal
|
||||
from django.db import models
|
||||
from django.contrib.auth.models import User
|
||||
from django.contrib.postgres.fields import ArrayField
|
||||
from inventory.models import Lift
|
||||
from inventory.models import Lift, Bar
|
||||
|
||||
SET_TYPES = (
|
||||
('warmup', 'Warmup'),
|
||||
@ -18,9 +19,37 @@ class LiftingOptions(models.Model):
|
||||
user = models.OneToOneField(User, related_name='lifting_options')
|
||||
|
||||
lifting_units = models.CharField(max_length=1, choices=UNITS, default='i')
|
||||
default_bar = models.ForeignKey(Bar, default=1)
|
||||
plate_pairs = ArrayField(models.DecimalField(max_digits=7, decimal_places=3),
|
||||
default=['45','45','25','10','5','5','2.5','1.25'])
|
||||
|
||||
def plates_for_weight(self, weight):
|
||||
side = []
|
||||
w = Decimal(weight)
|
||||
if self.lifting_units == 'i':
|
||||
w -= self.default_bar.weight_lb
|
||||
else:
|
||||
w -= self.default_bar.weight_kg
|
||||
initial_weight = w
|
||||
available = sorted(self.plate_pairs, reverse=True)
|
||||
while w and available:
|
||||
plate = available.pop(0)
|
||||
if plate * 2 <= w:
|
||||
w -= plate * 2
|
||||
side.append(plate)
|
||||
if sum(side) * 2 != initial_weight:
|
||||
raise ValueError('remaining weight {}'.format(initial_weight - sum(side) * 2))
|
||||
return side
|
||||
|
||||
|
||||
class LiftOptions(models.Model):
|
||||
user = models.ForeignKey(User, related_name='lift_options')
|
||||
lift = models.ForeignKey(Lift, related_name='options')
|
||||
|
||||
bar = models.ForeignKey(Bar, null=True)
|
||||
extra_start_weight = models.DecimalField(max_digits=7, decimal_places=3, default=0)
|
||||
increment = models.DecimalField(max_digits=5, decimal_places=3, default=5)
|
||||
|
||||
|
||||
class Set(models.Model):
|
||||
user = models.ForeignKey(User, related_name='sets')
|
||||
|
@ -1,10 +1,13 @@
|
||||
from django import template
|
||||
from django.template.defaultfilters import stringfilter
|
||||
from common import to_lb
|
||||
from common import to_lb, remove_exponent
|
||||
|
||||
register = template.Library()
|
||||
|
||||
|
||||
register.filter('decimal', remove_exponent)
|
||||
|
||||
|
||||
class MassNode(template.Node):
|
||||
def __init__(self, weight):
|
||||
self.weight = template.Variable(weight)
|
||||
|
@ -1,3 +1,4 @@
|
||||
from decimal import Decimal as D
|
||||
from django.test import TestCase
|
||||
from django.contrib.auth.models import User
|
||||
from lifting.models import LiftingOptions
|
||||
@ -5,6 +6,55 @@ from lifting.models import LiftingOptions
|
||||
|
||||
class TestLiftingOptions(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.user = User.objects.create_user(username='test', email='test@example.com', password='test')
|
||||
|
||||
def test_signal(self):
|
||||
u = User.objects.create_user(username='test', email='test@example.com', password='test')
|
||||
assert LiftingOptions.objects.filter(user=u).count() == 1
|
||||
assert LiftingOptions.objects.filter(user=self.user).count() == 1
|
||||
|
||||
def test_plates_for_weight(self):
|
||||
opts = self.user.lifting_options
|
||||
opts.plate_pairs = [D(45), D(45), D(25), D(10), D(5), D(5), D('2.5')]
|
||||
opts.default_bar_id = 1
|
||||
opts.lifting_units = 'i'
|
||||
|
||||
assert opts.plates_for_weight(45) == []
|
||||
assert opts.plates_for_weight(50) == [D('2.5')]
|
||||
assert opts.plates_for_weight(90) == [D(10), D(5), D(5), D('2.5')]
|
||||
assert opts.plates_for_weight(320) == [D(45), D(45), D(25), D(10), D(5), D(5), D('2.5')]
|
||||
|
||||
def test_plates_for_weight_womens_bar(self):
|
||||
opts = self.user.lifting_options
|
||||
opts.plate_pairs = [D(45), D(45), D(25), D(10), D(5), D(5), D('2.5')]
|
||||
opts.default_bar_id = 2
|
||||
opts.lifting_units = 'i'
|
||||
|
||||
assert opts.plates_for_weight(35) == []
|
||||
assert opts.plates_for_weight(40) == [D('2.5')]
|
||||
assert opts.plates_for_weight(80) == [D(10), D(5), D(5), D('2.5')]
|
||||
assert opts.plates_for_weight(310) == [D(45), D(45), D(25), D(10), D(5), D(5), D('2.5')]
|
||||
|
||||
def test_plates_for_weight_kg(self):
|
||||
opts = self.user.lifting_options
|
||||
opts.plate_pairs = [D(20), D(20), D(10), D(5), D('2.5')]
|
||||
opts.default_bar_id = 1
|
||||
opts.lifting_units = 'm'
|
||||
|
||||
assert opts.plates_for_weight(20) == []
|
||||
assert opts.plates_for_weight(25) == [D('2.5')]
|
||||
assert opts.plates_for_weight(55) == [D(10), D(5), D('2.5')]
|
||||
assert opts.plates_for_weight(135) == [D(20), D(20), D(10), D(5), D('2.5')]
|
||||
|
||||
def test_plates_for_weight_error(self):
|
||||
opts = self.user.lifting_options
|
||||
opts.plate_pairs = [D(45), D(45), D(25), D(10), D(5), D(5), D('2.5')]
|
||||
opts.default_bar_id = 1
|
||||
opts.lifting_units = 'i'
|
||||
|
||||
# TODO: check amounts
|
||||
with self.assertRaises(ValueError):
|
||||
opts.plates_for_weight('47.5')
|
||||
with self.assertRaises(ValueError):
|
||||
opts.plates_for_weight(325)
|
||||
with self.assertRaises(ValueError):
|
||||
opts.plates_for_weight(30)
|
||||
|
@ -10,5 +10,5 @@ urlpatterns = [
|
||||
url(r'^lifts/$', views.lift_list, name='lift-list'),
|
||||
url(r'^lifts/(?P<lift_id>\d+)/$', views.by_lift, name='lift-detail'),
|
||||
|
||||
url(r'^edit/$', views.edit_profile, name='edit-profile'),
|
||||
url(r'^options/$', views.edit_options, name='edit-options'),
|
||||
]
|
||||
|
@ -1,17 +1,17 @@
|
||||
import calendar
|
||||
import datetime
|
||||
import decimal
|
||||
import os
|
||||
import tempfile
|
||||
import calendar
|
||||
from collections import defaultdict
|
||||
from collections import defaultdict, Counter
|
||||
|
||||
from django.shortcuts import render
|
||||
from django import forms
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.views.generic import dates
|
||||
from django.db.models import Count, Max
|
||||
|
||||
from inventory.models import Lift, Bar
|
||||
from .models import Set
|
||||
from inventory.models import Lift
|
||||
|
||||
|
||||
@login_required
|
||||
@ -83,7 +83,25 @@ def by_lift(request, lift_id):
|
||||
|
||||
|
||||
@login_required
|
||||
def edit_profile(request):
|
||||
form = request.user.profile
|
||||
def edit_options(request):
|
||||
lifting_options = request.user.lifting_options
|
||||
|
||||
return render(request, 'profiles/edit.html', {'form': form})
|
||||
if request.method == 'POST':
|
||||
print(request.POST)
|
||||
plates = []
|
||||
for weight, number in zip(request.POST.getlist('plate_weight'),
|
||||
request.POST.getlist('plate_number')):
|
||||
if weight and number:
|
||||
plates += [decimal.Decimal(weight)] * int(number)
|
||||
|
||||
lifting_options.plate_pairs = plates
|
||||
lifting_options.default_bar_id = int(request.POST.get('barbell'))
|
||||
lifting_options.lifting_units = request.POST.get('lifting_units')
|
||||
lifting_options.save()
|
||||
|
||||
bars = Bar.objects.all()
|
||||
plates = sorted(Counter(lifting_options.plate_pairs).items())
|
||||
|
||||
return render(request, 'profiles/edit.html', {'lifting_options': lifting_options,
|
||||
'bars': bars, 'plates': plates,
|
||||
})
|
||||
|
15
old-shaft-files/lifting/lifting_tags.py
Normal file
15
old-shaft-files/lifting/lifting_tags.py
Normal file
@ -0,0 +1,15 @@
|
||||
from django import template
|
||||
from django.template.loader import render_to_string
|
||||
|
||||
register = template.Library()
|
||||
|
||||
|
||||
@register.filter
|
||||
def formfield(value, arg):
|
||||
if ',' in arg:
|
||||
size, units = arg.split(',')
|
||||
else:
|
||||
size = arg
|
||||
units = None
|
||||
return render_to_string('lifting/_form-group.html',
|
||||
{'field': value, 'size': size, 'units': units})
|
82
old-shaft-files/lifting/models.py
Normal file
82
old-shaft-files/lifting/models.py
Normal file
@ -0,0 +1,82 @@
|
||||
|
||||
|
||||
class Plan(models.Model):
|
||||
name = models.CharField(max_length=100)
|
||||
tags = models.ManyToManyField(PlanTag, related_name='plans')
|
||||
public = models.BooleanField(default=False)
|
||||
owner = models.ForeignKey(User, related_name='plans', null=True)
|
||||
cloned_from = models.ForeignKey('self', null=True)
|
||||
|
||||
objects = ByNameManager()
|
||||
|
||||
def __str__(self):
|
||||
return '{0}'.format(self.name)
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse('lifting.views.plan', args=[str(self.id)])
|
||||
|
||||
def natural_key(self):
|
||||
return (self.name,)
|
||||
|
||||
def get_days(self, user):
|
||||
exercise_rules = {}
|
||||
days = []
|
||||
for day in self.days.all():
|
||||
day_sets = []
|
||||
for exercise in day.exercises.all():
|
||||
for reps in exercise.get_sets():
|
||||
rules = exercise_rules.setdefault(exercise.exercise,
|
||||
exercise.exercise.rules.get(user=user))
|
||||
day_sets.append(
|
||||
Set(exercise=exercise.exercise,
|
||||
weight=round_to(rules.work_weight*exercise.percent, rules.increment),
|
||||
reps=reps,
|
||||
type='planned')
|
||||
)
|
||||
if exercise.raise_weight:
|
||||
rules.work_weight += rules.increment
|
||||
days.append(day_sets)
|
||||
return days
|
||||
|
||||
|
||||
class PlanDay(models.Model):
|
||||
plan = models.ForeignKey(Plan, related_name='days')
|
||||
name = models.CharField(max_length=100)
|
||||
order = models.PositiveIntegerField()
|
||||
|
||||
def __str__(self):
|
||||
return '{0}: {1}'.format(self.plan, self.name)
|
||||
|
||||
class Meta:
|
||||
ordering = ['order']
|
||||
|
||||
|
||||
class PlanExercise(models.Model):
|
||||
plan_day = models.ForeignKey(PlanDay, related_name='exercises')
|
||||
exercise = models.ForeignKey(Exercise)
|
||||
order = models.PositiveIntegerField()
|
||||
sets = models.CharField(max_length=100)
|
||||
percent = models.DecimalField(max_digits=4, decimal_places=3, default=1)
|
||||
raise_weight = models.BooleanField(default=False)
|
||||
|
||||
def get_sets(self):
|
||||
return [int(s) for s in self.sets.split(',')]
|
||||
|
||||
def get_set_display(self):
|
||||
sets = []
|
||||
last_reps = None
|
||||
last_reps_num = 0
|
||||
for s in self.get_sets():
|
||||
if last_reps and s != last_reps:
|
||||
sets.append('{0}x{1}'.format(last_reps_num, last_reps))
|
||||
last_reps_num = 0
|
||||
last_reps_num += 1
|
||||
last_reps = s
|
||||
sets.append('{0}x{1}'.format(last_reps_num, last_reps))
|
||||
return ', '.join(sets)
|
||||
|
||||
def get_percent_display(self):
|
||||
return '{0:%}'.format(self.percent)
|
||||
|
||||
class Meta:
|
||||
ordering = ['order']
|
31
old-shaft-files/lifting/plans.py
Normal file
31
old-shaft-files/lifting/plans.py
Normal file
@ -0,0 +1,31 @@
|
||||
from decimal import Decimal as D
|
||||
from .models import ExerciseRules, Set
|
||||
from .utils import round_to
|
||||
|
||||
|
||||
def warmup_ss(exercise, weight, user):
|
||||
"""
|
||||
Starting Strength Warmup
|
||||
========================
|
||||
(start)x5 x2
|
||||
(40%)x5
|
||||
(60%)x3
|
||||
(80%)x2
|
||||
"""
|
||||
|
||||
# get relevant rules
|
||||
rules, _ = ExerciseRules.objects.get_or_create(user=user, exercise=exercise)
|
||||
start = rules.start_weight
|
||||
|
||||
# empty bar sets
|
||||
sets = [Set(exercise=exercise, weight=start, reps=5, type='warmup')]*2
|
||||
|
||||
if D('0.8')*weight < start:
|
||||
return []
|
||||
|
||||
for reps, pct in ((5, D('0.4')), (3, D('0.6')), (2, D('0.8'))):
|
||||
ww = round_to(weight * pct, D('5'))
|
||||
if ww > start:
|
||||
sets.append(Set(exercise=exercise, weight=ww, reps=reps, type='warmup'))
|
||||
|
||||
return sets
|
147
old-shaft-files/lifting/tests.py
Normal file
147
old-shaft-files/lifting/tests.py
Normal file
@ -0,0 +1,147 @@
|
||||
from decimal import Decimal as D
|
||||
from django.test import TestCase
|
||||
from django.contrib.auth.models import User
|
||||
from .models import Exercise, UserSettings, ExerciseRules, Plan
|
||||
from .plans import warmup_ss
|
||||
from .utils import round_to
|
||||
|
||||
|
||||
def _seteq(s1, exercise, weight, reps):
|
||||
try:
|
||||
assert s1.exercise == exercise
|
||||
assert s1.weight == weight
|
||||
assert s1.reps == reps
|
||||
except AssertionError:
|
||||
raise AssertionError('{0} != {1}x{2}'.format(s1, weight, reps))
|
||||
|
||||
|
||||
|
||||
class PlanTests(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.user = User.objects.create_user(username='user')
|
||||
self.squat = Exercise.objects.create(name='squat')
|
||||
self.bench = Exercise.objects.create(name='bench press')
|
||||
ExerciseRules.objects.create(user=self.user, exercise=self.squat, work_weight=200)
|
||||
ExerciseRules.objects.create(user=self.user, exercise=self.bench, work_weight=100)
|
||||
self.tm = Plan.objects.create(name='Texas Method')
|
||||
|
||||
a1 = self.tm.days.create(name='A week - Volume day', order=1)
|
||||
a1.exercises.create(exercise=self.squat, order=1, sets='5,5,5,5,5', percent=D('0.85'))
|
||||
a1.exercises.create(exercise=self.bench, order=2, sets='5,5,5,5,5', percent=D('0.85'))
|
||||
|
||||
a2 = self.tm.days.create(name='A week - Recovery day', order=2)
|
||||
a2.exercises.create(exercise=self.squat, order=1, sets='5,5', percent=D('0.8'))
|
||||
|
||||
# exaggerated raise_weight for tests
|
||||
a3 = self.tm.days.create(name='A week - Record day', order=3)
|
||||
a3.exercises.create(exercise=self.squat, order=1, sets='5', percent=D(1),
|
||||
raise_weight=True)
|
||||
a3.exercises.create(exercise=self.bench, order=2, sets='5', percent=D(1),
|
||||
raise_weight=True)
|
||||
|
||||
b1 = self.tm.days.create(name='B week - Volume day', order=4)
|
||||
b1.exercises.create(exercise=self.squat, order=1, sets='5,5,5,5,5', percent=D('0.85'))
|
||||
|
||||
b2 = self.tm.days.create(name='B week - Recovery day', order=5)
|
||||
b2.exercises.create(exercise=self.squat, order=1, sets='5,5', percent=D('0.8'))
|
||||
b2.exercises.create(exercise=self.bench, order=1, sets='5,5', percent=D('0.8'))
|
||||
|
||||
b3 = self.tm.days.create(name='B week - Record day', order=6)
|
||||
b3.exercises.create(exercise=self.squat, order=2, sets='5', percent=D(1))
|
||||
|
||||
def test_get_days(self):
|
||||
a1, a2, a3, b1, b2, b3 = self.tm.get_days(self.user)
|
||||
|
||||
_seteq(a1[0], self.squat, 170, 5)
|
||||
_seteq(a1[1], self.squat, 170, 5)
|
||||
_seteq(a1[2], self.squat, 170, 5)
|
||||
_seteq(a1[3], self.squat, 170, 5)
|
||||
_seteq(a1[4], self.squat, 170, 5)
|
||||
_seteq(a1[5], self.bench, 85, 5)
|
||||
_seteq(a1[6], self.bench, 85, 5)
|
||||
_seteq(a1[7], self.bench, 85, 5)
|
||||
_seteq(a1[8], self.bench, 85, 5)
|
||||
_seteq(a1[9], self.bench, 85, 5)
|
||||
|
||||
_seteq(a2[0], self.squat, 160, 5)
|
||||
_seteq(a2[1], self.squat, 160, 5)
|
||||
|
||||
_seteq(a3[0], self.squat, 200, 5)
|
||||
_seteq(a3[1], self.bench, 100, 5)
|
||||
|
||||
# b record day raised by 5
|
||||
_seteq(b3[0], self.squat, 205, 5)
|
||||
|
||||
|
||||
class WarmupTests(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.exercise = Exercise.objects.create(name='bench press')
|
||||
self.user = User.objects.create_user(username='user')
|
||||
|
||||
def test_round_to(self):
|
||||
assert round_to(D('21'), D('2.5')) == D('20')
|
||||
assert round_to(D('23'), D('2.5')) == D('22.5')
|
||||
assert round_to(D('23'), D('5')) == D('20')
|
||||
assert round_to(D('24.5'), D('2.5')) == D('22.5')
|
||||
assert round_to(D('24.9'), D('5')) == D('25')
|
||||
|
||||
def test_warmup_ss(self):
|
||||
# <=55: no warmup
|
||||
assert warmup_ss(self.exercise, D('10'), self.user) == []
|
||||
assert warmup_ss(self.exercise, D('55'), self.user) == []
|
||||
|
||||
# 60: just the bar
|
||||
sets = warmup_ss(self.exercise, D('60'), self.user)
|
||||
assert len(sets) == 2, sets
|
||||
_seteq(sets[0], self.exercise, 45, 5)
|
||||
_seteq(sets[1], self.exercise, 45, 5)
|
||||
|
||||
sets = warmup_ss(self.exercise, D('70'), self.user)
|
||||
_seteq(sets[0], self.exercise, 45, 5)
|
||||
_seteq(sets[1], self.exercise, 45, 5)
|
||||
_seteq(sets[2], self.exercise, 55, 2)
|
||||
|
||||
sets = warmup_ss(self.exercise, D('95'), self.user)
|
||||
_seteq(sets[0], self.exercise, 45, 5)
|
||||
_seteq(sets[1], self.exercise, 45, 5)
|
||||
_seteq(sets[2], self.exercise, 55, 3)
|
||||
_seteq(sets[3], self.exercise, 75, 2)
|
||||
|
||||
sets = warmup_ss(self.exercise, D('135'), self.user)
|
||||
_seteq(sets[0], self.exercise, 45, 5)
|
||||
_seteq(sets[1], self.exercise, 45, 5)
|
||||
_seteq(sets[2], self.exercise, 50, 5)
|
||||
_seteq(sets[3], self.exercise, 80, 3)
|
||||
_seteq(sets[4], self.exercise, 105, 2)
|
||||
|
||||
sets = warmup_ss(self.exercise, D('170'), self.user)
|
||||
_seteq(sets[0], self.exercise, 45, 5)
|
||||
_seteq(sets[1], self.exercise, 45, 5)
|
||||
_seteq(sets[2], self.exercise, 65, 5)
|
||||
_seteq(sets[3], self.exercise, 100, 3)
|
||||
_seteq(sets[4], self.exercise, 135, 2)
|
||||
|
||||
sets = warmup_ss(self.exercise, D('250'), self.user)
|
||||
_seteq(sets[0], self.exercise, 45, 5)
|
||||
_seteq(sets[1], self.exercise, 45, 5)
|
||||
_seteq(sets[2], self.exercise, 100, 5)
|
||||
_seteq(sets[3], self.exercise, 150, 3)
|
||||
_seteq(sets[4], self.exercise, 200, 2)
|
||||
|
||||
# slightly crazy jump
|
||||
sets = warmup_ss(self.exercise, D('315'), self.user)
|
||||
_seteq(sets[0], self.exercise, 45, 5)
|
||||
_seteq(sets[1], self.exercise, 45, 5)
|
||||
_seteq(sets[2], self.exercise, 125, 5)
|
||||
_seteq(sets[3], self.exercise, 185, 3)
|
||||
_seteq(sets[4], self.exercise, 250, 2)
|
||||
|
||||
# this is probably crazy & should be fixed
|
||||
sets = warmup_ss(self.exercise, D('500'), self.user)
|
||||
_seteq(sets[0], self.exercise, 45, 5)
|
||||
_seteq(sets[1], self.exercise, 45, 5)
|
||||
_seteq(sets[2], self.exercise, 200, 5)
|
||||
_seteq(sets[3], self.exercise, 300, 3)
|
||||
_seteq(sets[4], self.exercise, 400, 2)
|
56
old-shaft-files/lifting/views.py
Normal file
56
old-shaft-files/lifting/views.py
Normal file
@ -0,0 +1,56 @@
|
||||
from decimal import Decimal as D
|
||||
from django.shortcuts import render, get_object_or_404
|
||||
from django.views.decorators.http import require_http_methods, require_GET
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from .models import UserSettings, ExerciseRules, Exercise, Plan
|
||||
from .forms import UserSettingsForm, PlanForm
|
||||
|
||||
|
||||
|
||||
@require_http_methods(["GET", "POST"])
|
||||
@login_required
|
||||
def plan_edit(request, id):
|
||||
""" edit details of a plan """
|
||||
plan = get_object_or_404(Plan, pk=id)
|
||||
exercises = Exercise.objects.all()
|
||||
if request.method == 'GET':
|
||||
pform = PlanForm(instance=plan)
|
||||
return render(request, 'lifting/plan_edit.html',
|
||||
{'pform': pform, 'plan': plan, 'exercises': exercises})
|
||||
|
||||
|
||||
@require_http_methods(["GET", "POST"])
|
||||
@login_required
|
||||
def rules(request):
|
||||
""" a view that allows a user to view/edit their list of rules """
|
||||
gr, _ = UserSettings.objects.get_or_create(user=request.user)
|
||||
ers = list(ExerciseRules.objects.filter(user=request.user))
|
||||
exercises = Exercise.objects.exclude(id__in=[er.exercise_id for er in ers])
|
||||
if request.method == 'GET':
|
||||
grform = UserSettingsForm(instance=gr)
|
||||
elif request.method == 'POST':
|
||||
grform = UserSettingsForm(request.POST, instance=gr)
|
||||
if grform.is_valid():
|
||||
grform.save()
|
||||
for exercise, start, inc, work in zip(request.POST.getlist('er.exercise'),
|
||||
request.POST.getlist('er.start_weight'),
|
||||
request.POST.getlist('er.increment'),
|
||||
request.POST.getlist('er.work_weight')):
|
||||
exercise = int(exercise)
|
||||
start = D(start)
|
||||
inc = D(inc)
|
||||
work = D(work)
|
||||
|
||||
er, _ = ExerciseRules.objects.get_or_create(user=request.user,
|
||||
exercise_id=int(exercise))
|
||||
er.start_weight = start
|
||||
er.increment = inc
|
||||
er.work_weight = work
|
||||
er.save()
|
||||
return render(request, 'lifting/rules.html',
|
||||
{'grform': grform, 'exercise_rules': ers, 'exercises': exercises, })
|
||||
|
||||
|
||||
def er_row(request, eid):
|
||||
return render(request, 'lifting/_er-row.html',
|
||||
{'er': ExerciseRules(user=request.user, exercise=Exercise.objects.get(pk=eid))})
|
24
old-shaft-files/templates/lifting/_er-row.html
Normal file
24
old-shaft-files/templates/lifting/_er-row.html
Normal file
@ -0,0 +1,24 @@
|
||||
<tr>
|
||||
<input type="hidden" name="er.exercise" value="{{er.exercise.id}}" />
|
||||
<td class="col-md-3">
|
||||
{{er.exercise}}
|
||||
</td>
|
||||
<td class="col-md-2">
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control" name="er.start_weight" value="{{er.start_weight}}" />
|
||||
<span class="input-group-addon">lbs</span>
|
||||
</div>
|
||||
</td>
|
||||
<td class="col-md-2">
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control" name="er.increment" value="{{er.increment}}" />
|
||||
<span class="input-group-addon">lbs</span>
|
||||
</div>
|
||||
</td>
|
||||
<td class="col-md-2">
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control" name="er.work_weight" value="{{er.work_weight}}" />
|
||||
<span class="input-group-addon">lbs</span>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
7
old-shaft-files/templates/lifting/_form-group.html
Normal file
7
old-shaft-files/templates/lifting/_form-group.html
Normal file
@ -0,0 +1,7 @@
|
||||
<div class="form-group {% if field.errors %}has-error{% endif %}">
|
||||
<label class="col-md-2 control-label" for="{{field.id_for_label}}">{{field.label}}: </label>
|
||||
<div class="col-md-{{size}}{% if units %}input-group{% endif %}">
|
||||
<input type="text" class="form-control" id="{{field.id_for_label}}" name="{{field.html_name}}" value="{{field.value}}" />
|
||||
{% if units %}<span class="input-group-addon">{{units}}</span>{% endif %}
|
||||
</div>
|
||||
</div>
|
34
old-shaft-files/templates/lifting/edit_plan.html
Normal file
34
old-shaft-files/templates/lifting/edit_plan.html
Normal file
@ -0,0 +1,34 @@
|
||||
{% extends "lifting/base.html" %}
|
||||
{% load lifting %}
|
||||
|
||||
{% block content %}
|
||||
<input type="text" name="plan.name">
|
||||
|
||||
<div class="row">
|
||||
{% for day in plan.days.all %}
|
||||
<div class="col-md-4">
|
||||
<input type="day.name">
|
||||
{{day.name}}
|
||||
<table class="table">
|
||||
<thead>
|
||||
<th>Exercise</th><th>Sets</th><th>Percent</th>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for e in day.exercises.all %}
|
||||
<tr>
|
||||
<td>{{e.exercise}}</td>
|
||||
<td>{{e.get_set_display}}</td>
|
||||
<td>{{e.get_percent_display}}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block script %}
|
||||
<script>
|
||||
</script>
|
||||
{% endblock %}
|
33
old-shaft-files/templates/lifting/plan.html
Normal file
33
old-shaft-files/templates/lifting/plan.html
Normal file
@ -0,0 +1,33 @@
|
||||
{% extends "lifting/base.html" %}
|
||||
{% load lifting %}
|
||||
|
||||
{% block content %}
|
||||
<h2>{{plan}}</h2>
|
||||
|
||||
<div class="row">
|
||||
{% for day in plan.days.all %}
|
||||
<div class="col-md-4">
|
||||
<h4>{{day.name}}</h4>
|
||||
<table class="table">
|
||||
<thead>
|
||||
<th>Exercise</th><th>Sets</th><th>Percent</th>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for e in day.exercises.all %}
|
||||
<tr>
|
||||
<td>{{e.exercise}}</td>
|
||||
<td>{{e.get_set_display}}</td>
|
||||
<td>{{e.get_percent_display}}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block script %}
|
||||
<script>
|
||||
</script>
|
||||
{% endblock %}
|
54
old-shaft-files/templates/lifting/plan_edit.html
Normal file
54
old-shaft-files/templates/lifting/plan_edit.html
Normal file
@ -0,0 +1,54 @@
|
||||
{% extends "lifting/base.html" %}
|
||||
{% load lifting %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<form class="form-horizontal" method="POST">
|
||||
|
||||
{{pform.name|formfield:"3"}}
|
||||
{{pform.tags|formfield:"5"}}
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="row">
|
||||
{% for day in plan.days.all %}
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label class="col-md-2 control-label" for="day.name">Day: </label>
|
||||
<div class="col-md-9">
|
||||
<input type="text" class="form-control" id="day.name" name="day.name" value="{{day.name}}" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<th>Exercise</th><th>Sets</th><th>Percent</th>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for e in day.exercises.all %}
|
||||
<tr>
|
||||
<td>
|
||||
<select id="new-exercise" class="form-control">
|
||||
{% for exercise in exercises %}
|
||||
<option value="{{exercise.id}}" {% if e.exercise_id == exercise.id %}selected{% endif %}>{{exercise}}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</td>
|
||||
<td>{{e.get_set_display}}</td>
|
||||
<td>{{e.get_percent_display}}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
</form>
|
||||
{% endblock %}
|
||||
|
||||
{% block script %}
|
||||
<script>
|
||||
</script>
|
||||
{% endblock %}
|
36
old-shaft-files/templates/lifting/plans.html
Normal file
36
old-shaft-files/templates/lifting/plans.html
Normal file
@ -0,0 +1,36 @@
|
||||
{% extends "lifting/base.html" %}
|
||||
{% load lifting %}
|
||||
|
||||
{% block content %}
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Plan</th>
|
||||
<th>Tags</th>
|
||||
<th>Popularity</th>
|
||||
<th> </th>
|
||||
<th> </th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="exercise-rules">
|
||||
{% for plan in plans %}
|
||||
<tr>
|
||||
<td><a href="{{plan.get_absolute_url}}">{{plan}}</a></td>
|
||||
<td>
|
||||
{% for tag in plan.tags.all %}
|
||||
<span style="background-color: #{{tag.color}};" class="label">{{tag.name}}</span>
|
||||
{% endfor %}
|
||||
</td>
|
||||
<td>{{plan.popularity}}</td>
|
||||
<td>clone</td>
|
||||
<td>use plan</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endblock content %}
|
||||
|
||||
{% block script %}
|
||||
<script>
|
||||
</script>
|
||||
{% endblock %}
|
56
old-shaft-files/templates/lifting/rules.html
Normal file
56
old-shaft-files/templates/lifting/rules.html
Normal file
@ -0,0 +1,56 @@
|
||||
{% extends "lifting/base.html" %}
|
||||
{% load lifting %}
|
||||
|
||||
{% block content %}
|
||||
<form class="form-horizontal" method="POST">
|
||||
{% csrf_token %}
|
||||
<h3>Global Settings</h3>
|
||||
{{grform.bar|formfield:"2,lbs"}}
|
||||
{{grform.plates|formfield:"3,lbs"}}
|
||||
<hr>
|
||||
<h3>Exercise Settings</h3>
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Exercise</th>
|
||||
<th>Start Weight</th>
|
||||
<th>Increment</th>
|
||||
<th>Working Weight</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="exercise-rules">
|
||||
{% for er in exercise_rules %}
|
||||
{% include "lifting/_er-row.html" %}
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<hr>
|
||||
<div class="form-group">
|
||||
<div class="col-md-2">
|
||||
<button id="add-exercise" type="button" class="btn">Add Exercise: </button>
|
||||
</div>
|
||||
<div class="col-md-5">
|
||||
<select id="new-exercise" class="form-control">
|
||||
{% for exercise in exercises %}
|
||||
<option value="{{exercise.id}}">{{exercise}}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">Save Settings</button>
|
||||
</form>
|
||||
{% endblock content %}
|
||||
|
||||
{% block script %}
|
||||
<script>
|
||||
$(function() {
|
||||
$('#add-exercise').click(function() {
|
||||
var val = $('#new-exercise').val();
|
||||
$.get('/_er-row/' + val + '/', function(data) {
|
||||
$('#exercise-rules').append(data);
|
||||
$('#new-exercise > option[value=' + val + ']').remove();
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
@ -1,7 +1,11 @@
|
||||
{% extends "base.html" %}
|
||||
{% load lifting %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-6">
|
||||
<section class="panel panel-default">
|
||||
@ -37,22 +41,21 @@
|
||||
<label class="col-sm-4 control-label">Lifting Units:</label>
|
||||
<div class="col-sm-8">
|
||||
<div class="radio">
|
||||
<input type="radio" name="lifting_unit" value="i">lbs</input>
|
||||
<input type="radio" name="lifting_units" value="i" {% if lifting_options.lifting_units == "i" %}checked{% endif %} >lb</input>
|
||||
</div>
|
||||
<div class="radio">
|
||||
<input type="radio" name="lifting_unit" value="m">kg</input>
|
||||
<input type="radio" name="lifting_units" value="m" {% if lifting_options.lifting_units == "m" %}checked{% endif %}>kg</input>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-4 control-label">Barbell:</label>
|
||||
<div class="col-sm-8">
|
||||
{% for bar in bars %}
|
||||
<div class="radio">
|
||||
<input type="radio" name="barbell" value="1">Men's Olympic (44lbs / 20kg) </input>
|
||||
</div>
|
||||
<div class="radio">
|
||||
<input type="radio" name="barbell" value="2">Women's Olympic (33lbs / 15kg) </input>
|
||||
<input type="radio" name="barbell" value="{{bar.id}}" {% if lifting_options.default_bar_id == bar.id %}checked{% endif %}>{{bar}}</input>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
@ -63,37 +66,28 @@
|
||||
<tr><td>Weight</td><td># of Pairs</td></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for weight, n in plates %}
|
||||
<tr>
|
||||
<td><input type="text" class="form-control" name="plate_weight" value="45"></input></td>
|
||||
<td><input type="text" class="form-control" name="plate_number" value="2"></input></td>
|
||||
<td><input type="text" class="form-control" name="plate_weight" value="{{weight|decimal}}"></input></td>
|
||||
<td><input type="text" class="form-control" name="plate_number" value="{{n}}"></input></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
<tr>
|
||||
<td><input type="text" class="form-control" name="plate_weight" value="25"></input></td>
|
||||
<td><input type="text" class="form-control" name="plate_number" value="1"></input></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><input type="text" class="form-control" name="plate_weight" value="10"></input></td>
|
||||
<td><input type="text" class="form-control" name="plate_number" value="1"></input></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><input type="text" class="form-control" name="plate_weight" value="5"></input></td>
|
||||
<td><input type="text" class="form-control" name="plate_number" value="2"></input></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><input type="text" class="form-control" name="plate_weight" value="2.5"></input></td>
|
||||
<td><input type="text" class="form-control" name="plate_number" value="1"></input></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><input type="text" class="form-control" name="plate_weight" value="1.25"></input></td>
|
||||
<td><input type="text" class="form-control" name="plate_number" value="1"></input></td>
|
||||
<td><input type="text" class="form-control" name="plate_weight" value=""></input></td>
|
||||
<td><input type="text" class="form-control" name="plate_number" value=""></input></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-12"><input type="submit" class="form-control" value="Save Changes"></input> </div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
{% endblock %}
|
||||
|
Loading…
Reference in New Issue
Block a user