mass refactor
This commit is contained in:
parent
b61d681685
commit
c16b4caaa1
3
fitnotes/admin.py
Normal file
3
fitnotes/admin.py
Normal file
@ -0,0 +1,3 @@
|
||||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
40
fitnotes/importer.py
Normal file
40
fitnotes/importer.py
Normal file
@ -0,0 +1,40 @@
|
||||
import sqlite3
|
||||
from django.db import transaction
|
||||
from lifting.models import Lift, Set
|
||||
|
||||
|
||||
def _clean_name(name):
|
||||
return name.lower()
|
||||
|
||||
|
||||
def import_fitnotes_db(filename, user, fitnotes_to_lift):
|
||||
# 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'):
|
||||
|
||||
# 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]
|
||||
|
||||
Set.objects.create(lift_id=lift_id, date=date, weight_kg=weight_kg, reps=reps,
|
||||
source='fitnotes', user=user)
|
0
fitnotes/migrations/__init__.py
Normal file
0
fitnotes/migrations/__init__.py
Normal file
3
fitnotes/models.py
Normal file
3
fitnotes/models.py
Normal file
@ -0,0 +1,3 @@
|
||||
from django.db import models
|
||||
|
||||
# Create your models here.
|
62
fitnotes/tests.py
Normal file
62
fitnotes/tests.py
Normal file
@ -0,0 +1,62 @@
|
||||
from django.test import TestCase
|
||||
from django.contrib.auth.models import User
|
||||
from inventory.models import Lift
|
||||
from lifting.models import Set
|
||||
from .importer import import_fitnotes_db
|
||||
|
||||
|
||||
class TestFitnotesImport(TestCase):
|
||||
# fitnotes.db has:
|
||||
# April 1
|
||||
# bench press 10 @ 45
|
||||
# bench press 5 @ 95
|
||||
# bench press 3 @ 135
|
||||
# bench press 5 @ 155
|
||||
# April 3
|
||||
# squat 10 @ 45
|
||||
# squat 5 @ 95
|
||||
# squat 3 @ 135
|
||||
# squat 2 @ 185
|
||||
# squat 5 @ 225
|
||||
|
||||
def setUp(self):
|
||||
self.user = User.objects.create_user('default', 'default@example.com', 'default')
|
||||
self.bench = Lift.objects.create(name='bench press')
|
||||
self.squat = Lift.objects.create(name='squat')
|
||||
|
||||
self.good_mapping = {'flat barbell bench press': 'bench press',
|
||||
'barbell squat': 'squat'
|
||||
}
|
||||
self.bad_mapping = {'flat barbell bench press': 'bench press' }
|
||||
|
||||
def test_basic_import(self):
|
||||
# ensure that the data comes in
|
||||
import_fitnotes_db('fitnotes/testdata/example.fitnotes', self.user, self.good_mapping)
|
||||
assert Set.objects.filter(lift=self.bench).count() == 4
|
||||
assert Set.objects.filter(lift=self.squat).count() == 5
|
||||
|
||||
def test_double_import(self):
|
||||
# two identical dbs, should be idempotent
|
||||
import_fitnotes_db('fitnotes/testdata/example.fitnotes', self.user, self.good_mapping)
|
||||
import_fitnotes_db('fitnotes/testdata/example.fitnotes', self.user, self.good_mapping)
|
||||
assert Set.objects.filter(lift=self.bench).count() == 4
|
||||
assert Set.objects.filter(lift=self.squat).count() == 5
|
||||
|
||||
def test_import_with_other_data(self):
|
||||
Set.objects.create(lift=self.bench, weight_kg=100, reps=10, date='2014-01-01',
|
||||
user=self.user)
|
||||
import_fitnotes_db('fitnotes/testdata/example.fitnotes', self.user, self.good_mapping)
|
||||
assert Set.objects.filter(lift=self.bench).count() == 5
|
||||
|
||||
def test_import_with_bad_mapping(self):
|
||||
with self.assertRaises(ValueError):
|
||||
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):
|
||||
# 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)
|
||||
assert Set.objects.count() == 9
|
@ -3,5 +3,5 @@ from django.conf.urls import url
|
||||
from . import views
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^edit/$', views.edit_profile, name='edit-profile'),
|
||||
url(r'^fitnotes/$', views.fitnotes_upload),
|
||||
]
|
18
fitnotes/views.py
Normal file
18
fitnotes/views.py
Normal file
@ -0,0 +1,18 @@
|
||||
from django.shortcuts import render
|
||||
|
||||
@login_required
|
||||
def fitnotes_upload(request):
|
||||
if request.method == 'POST':
|
||||
form = FitnotesUploadForm(request.POST, request.FILES)
|
||||
if form.is_valid():
|
||||
_, fname = tempfile.mkstemp()
|
||||
with open(fname, 'wb') as tmp:
|
||||
for chunk in request.FILES['file'].chunks():
|
||||
tmp.write(chunk)
|
||||
try:
|
||||
importers.import_fitnotes_db(fname, request.user)
|
||||
finally:
|
||||
os.remove(fname)
|
||||
else:
|
||||
form = FitnotesUploadForm()
|
||||
return render(request, 'lifting/fitnotes.html', {'form': form})
|
0
inventory/__init__.py
Normal file
0
inventory/__init__.py
Normal file
6
inventory/admin.py
Normal file
6
inventory/admin.py
Normal file
@ -0,0 +1,6 @@
|
||||
from django.contrib import admin
|
||||
from .models import Lift
|
||||
|
||||
@admin.register(Lift)
|
||||
class LiftAdmin(admin.ModelAdmin):
|
||||
pass
|
28
inventory/migrations/0001_initial.py
Normal file
28
inventory/migrations/0001_initial.py
Normal file
@ -0,0 +1,28 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Bar',
|
||||
fields=[
|
||||
('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)),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Lift',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', auto_created=True, serialize=False, primary_key=True)),
|
||||
('name', models.CharField(max_length=200)),
|
||||
],
|
||||
),
|
||||
]
|
0
inventory/migrations/__init__.py
Normal file
0
inventory/migrations/__init__.py
Normal file
22
inventory/models.py
Normal file
22
inventory/models.py
Normal file
@ -0,0 +1,22 @@
|
||||
from django.db import models
|
||||
from common import to_lb
|
||||
|
||||
|
||||
class Bar(models.Model):
|
||||
name = models.CharField(max_length=100)
|
||||
weight_kg = 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)
|
||||
|
||||
|
||||
class Lift(models.Model):
|
||||
name = models.CharField(max_length=200)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
3
inventory/tests.py
Normal file
3
inventory/tests.py
Normal file
@ -0,0 +1,3 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
@ -0,0 +1 @@
|
||||
default_app_config = 'lifting.apps.LiftingConfig'
|
@ -1,15 +1,10 @@
|
||||
from django.contrib import admin
|
||||
from .models import Exercise, Set
|
||||
|
||||
@admin.register(Exercise)
|
||||
class ExerciseAdmin(admin.ModelAdmin):
|
||||
pass
|
||||
from .models import Set
|
||||
|
||||
|
||||
@admin.register(Set)
|
||||
class SetAdmin(admin.ModelAdmin):
|
||||
date_hierarchy = 'date'
|
||||
readonly_fields = ('user', 'exercise', 'date')
|
||||
list_filter = ('user__username', 'exercise')
|
||||
fields = ('user', 'exercise', 'date', 'weight_kg', 'reps', 'source')
|
||||
|
||||
readonly_fields = ('user', 'lift', 'date')
|
||||
list_filter = ('user__username', 'lift')
|
||||
fields = ('user', 'lift', 'date', 'weight_kg', 'reps', 'source')
|
||||
|
@ -5,14 +5,14 @@ from django.contrib.auth.models import User
|
||||
|
||||
|
||||
def create_profile(sender, created, instance, **kwargs):
|
||||
from .models import Profile
|
||||
from .models import LiftingOptions
|
||||
if created:
|
||||
Profile.objects.create(user=instance)
|
||||
LiftingOptions.objects.create(user=instance)
|
||||
|
||||
|
||||
class ProfileConfig(AppConfig):
|
||||
name = 'profiles'
|
||||
app_label = 'profiles'
|
||||
class LiftingConfig(AppConfig):
|
||||
name = 'lifting'
|
||||
app_label = 'lifting'
|
||||
|
||||
def ready(self):
|
||||
post_save.connect(create_profile, sender=User)
|
@ -1,40 +0,0 @@
|
||||
import sqlite3
|
||||
from django.db import transaction
|
||||
from lifting.models import Exercise, Set
|
||||
|
||||
|
||||
def _clean_name(name):
|
||||
return name.lower()
|
||||
|
||||
|
||||
def import_fitnotes_db(filename, user):
|
||||
# exercise names to db ids
|
||||
exercises = {}
|
||||
for e in Exercise.objects.all():
|
||||
for n in e.names:
|
||||
exercises[_clean_name(n)] = e.id
|
||||
|
||||
# build mapping FitNotes exercise id => our exercise id
|
||||
exercise_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)
|
||||
# map to an Exercise id or str
|
||||
exercise_id_mapping[fnid] = exercises[cleaned] if cleaned in exercises else 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'):
|
||||
|
||||
# create Exercise if it wasn't found and there's a workout using it
|
||||
if isinstance(exercise_id_mapping[fnid], str):
|
||||
exercise_id_mapping[fnid] = Exercise.objects.create(
|
||||
names=[exercise_id_mapping[fnid]]).id
|
||||
|
||||
exercise_id = exercise_id_mapping[fnid]
|
||||
|
||||
Set.objects.create(exercise_id=exercise_id, date=date, weight_kg=weight_kg, reps=reps,
|
||||
source='fitnotes', user=user)
|
@ -3,30 +3,36 @@ from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
import django.contrib.postgres.fields
|
||||
from django.conf import settings
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('inventory', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Exercise',
|
||||
name='LiftingOptions',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', auto_created=True, primary_key=True, serialize=False)),
|
||||
('names', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=200), size=None)),
|
||||
('id', models.AutoField(verbose_name='ID', auto_created=True, serialize=False, primary_key=True)),
|
||||
('lifting_units', models.CharField(default='i', choices=[('m', 'Metric (kg)'), ('i', 'Imperial (lb)')], max_length=1)),
|
||||
('plate_pairs', django.contrib.postgres.fields.ArrayField(base_field=models.DecimalField(max_digits=7, decimal_places=3), default=['45', '45', '25', '10', '5', '5', '2.5', '1.25'], size=None)),
|
||||
('user', models.OneToOneField(related_name='lifting_options', to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Set',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', auto_created=True, primary_key=True, serialize=False)),
|
||||
('id', models.AutoField(verbose_name='ID', auto_created=True, serialize=False, primary_key=True)),
|
||||
('date', models.DateField()),
|
||||
('weight_kg', models.DecimalField(decimal_places=3, max_digits=7)),
|
||||
('weight_kg', models.DecimalField(max_digits=7, decimal_places=3)),
|
||||
('reps', models.PositiveIntegerField()),
|
||||
('source', models.CharField(max_length=100)),
|
||||
('exercise', models.ForeignKey(to='lifting.Exercise', related_name='sets')),
|
||||
('lift', models.ForeignKey(related_name='sets', to='inventory.Lift')),
|
||||
('user', models.ForeignKey(related_name='sets', to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
),
|
||||
]
|
||||
|
@ -1,22 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
from django.conf import settings
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('lifting', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='set',
|
||||
name='user',
|
||||
field=models.ForeignKey(to=settings.AUTH_USER_MODEL, related_name='sets', default=None),
|
||||
preserve_default=False,
|
||||
),
|
||||
]
|
@ -1,30 +1,34 @@
|
||||
from django.db import models
|
||||
from django.contrib.auth.models import User
|
||||
from django.contrib.postgres.fields import ArrayField
|
||||
from inventory.models import Lift
|
||||
|
||||
SET_TYPES = (
|
||||
('warmup', 'Warmup'),
|
||||
('planned', 'Planned'),
|
||||
)
|
||||
|
||||
UNITS = (
|
||||
('m', 'Metric (kg)'),
|
||||
('i', 'Imperial (lb)'),
|
||||
)
|
||||
|
||||
class Exercise(models.Model):
|
||||
names = ArrayField(models.CharField(max_length=200))
|
||||
|
||||
def display_name(self):
|
||||
return self.names[0].title()
|
||||
class LiftingOptions(models.Model):
|
||||
user = models.OneToOneField(User, related_name='lifting_options')
|
||||
|
||||
def __str__(self):
|
||||
return ', '.join(self.names)
|
||||
lifting_units = models.CharField(max_length=1, choices=UNITS, default='i')
|
||||
plate_pairs = ArrayField(models.DecimalField(max_digits=7, decimal_places=3),
|
||||
default=['45','45','25','10','5','5','2.5','1.25'])
|
||||
|
||||
|
||||
class Set(models.Model):
|
||||
user = models.ForeignKey(User, related_name='sets')
|
||||
date = models.DateField()
|
||||
exercise = models.ForeignKey(Exercise, related_name='sets')
|
||||
lift = models.ForeignKey(Lift, related_name='sets')
|
||||
weight_kg = models.DecimalField(max_digits=7, decimal_places=3)
|
||||
reps = models.PositiveIntegerField()
|
||||
source = models.CharField(max_length=100)
|
||||
|
||||
def __str__(self):
|
||||
return '{} - {} @ {}kg - {}'.format(self.exercise, self.reps, self.weight_kg, self.date)
|
||||
return '{} - {} @ {}kg - {}'.format(self.lift, self.reps, self.weight_kg, self.date)
|
||||
|
@ -1,55 +1,10 @@
|
||||
from django.test import TestCase
|
||||
from django.contrib.auth.models import User
|
||||
from lifting.models import Exercise, Set
|
||||
from lifting.importers import import_fitnotes_db
|
||||
from lifting.models import LiftingOptions
|
||||
|
||||
|
||||
class TestFitnotesImport(TestCase):
|
||||
# fitnotes.db has:
|
||||
# April 1
|
||||
# bench press 10 @ 45
|
||||
# bench press 5 @ 95
|
||||
# bench press 3 @ 135
|
||||
# bench press 5 @ 155
|
||||
# April 3
|
||||
# squat 10 @ 45
|
||||
# squat 5 @ 95
|
||||
# squat 3 @ 135
|
||||
# squat 2 @ 185
|
||||
# squat 5 @ 225
|
||||
class TestLiftingOptions(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.user = User.objects.create_user('default', 'default@example.com', 'default')
|
||||
|
||||
def test_basic_import(self):
|
||||
# ensure that the data comes in
|
||||
import_fitnotes_db('lifting/testdata/example.fitnotes', self.user)
|
||||
|
||||
assert Exercise.objects.count() == 2
|
||||
bp = Exercise.objects.get(names__contains=["flat barbell bench press"])
|
||||
squat = Exercise.objects.get(names__contains=["barbell squat"])
|
||||
assert Set.objects.count() == 9
|
||||
|
||||
def test_double_import(self):
|
||||
# two identical dbs, should be idempotent
|
||||
import_fitnotes_db('lifting/testdata/example.fitnotes', self.user)
|
||||
import_fitnotes_db('lifting/testdata/example.fitnotes', self.user)
|
||||
assert Exercise.objects.count() == 2
|
||||
assert Set.objects.count() == 9
|
||||
|
||||
def test_import_with_other_data(self):
|
||||
Exercise.objects.create(names=['incline bench press'])
|
||||
e = Exercise.objects.create(names=['flat barbell bench press'])
|
||||
Set.objects.create(exercise=e, weight_kg=100, reps=10, date='2014-01-01', user=self.user)
|
||||
import_fitnotes_db('lifting/testdata/example.fitnotes', self.user)
|
||||
assert Exercise.objects.count() == 3
|
||||
assert Set.objects.count() == 10
|
||||
|
||||
def test_bad_import(self):
|
||||
# good db then bad db, should fail without screwing up existing data
|
||||
import_fitnotes_db('lifting/testdata/example.fitnotes', self.user)
|
||||
with self.assertRaises(Exception):
|
||||
# baddata.fitnotes has all exercise ids set to 9999
|
||||
import_fitnotes_db('lifting/testdata/baddata.fitnotes', self.user)
|
||||
assert Exercise.objects.count() == 2
|
||||
assert Set.objects.count() == 9
|
||||
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
|
||||
|
@ -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'^fitnotes/$', views.fitnotes_upload),
|
||||
url(r'^edit/$', views.edit_profile, name='edit-profile'),
|
||||
]
|
||||
|
@ -11,7 +11,8 @@ from django.views.generic import dates
|
||||
from django.db.models import Count, Max
|
||||
|
||||
from . import importers
|
||||
from .models import Set, Exercise
|
||||
from .models import Set
|
||||
from inventory.models import Lift
|
||||
|
||||
|
||||
@login_required
|
||||
@ -20,7 +21,7 @@ def month_lifts(request, year, month):
|
||||
|
||||
sets_by_day = defaultdict(set)
|
||||
for workset in Set.objects.filter(user=request.user, date__year=year, date__month=month):
|
||||
sets_by_day[workset.date.day].add(workset.exercise)
|
||||
sets_by_day[workset.date.day].add(workset.lift)
|
||||
date = datetime.date(year, month, 1)
|
||||
|
||||
# build calendar
|
||||
@ -68,7 +69,7 @@ def day_lifts(request, year, month, day):
|
||||
|
||||
@login_required
|
||||
def lift_list(request):
|
||||
lifts = Exercise.objects.filter(sets__user=request.user).annotate(
|
||||
lifts = Lift.objects.filter(sets__user=request.user).annotate(
|
||||
total=Count('sets'), max_kg=Max('sets__weight_kg'),
|
||||
last_date=Max('sets__date'),
|
||||
).order_by('-last_date')
|
||||
@ -77,8 +78,8 @@ def lift_list(request):
|
||||
|
||||
@login_required
|
||||
def by_lift(request, lift_id):
|
||||
lift = Exercise.objects.get(pk=lift_id)
|
||||
sets = Set.objects.filter(user=request.user, exercise=lift).order_by('-date')
|
||||
lift = Lift.objects.get(pk=lift_id)
|
||||
sets = Set.objects.filter(user=request.user, lift=lift).order_by('-date')
|
||||
return render(request, 'lifting/by_lift.html', {'lift': lift, 'sets': sets})
|
||||
|
||||
|
||||
@ -87,18 +88,7 @@ class FitnotesUploadForm(forms.Form):
|
||||
|
||||
|
||||
@login_required
|
||||
def fitnotes_upload(request):
|
||||
if request.method == 'POST':
|
||||
form = FitnotesUploadForm(request.POST, request.FILES)
|
||||
if form.is_valid():
|
||||
_, fname = tempfile.mkstemp()
|
||||
with open(fname, 'wb') as tmp:
|
||||
for chunk in request.FILES['file'].chunks():
|
||||
tmp.write(chunk)
|
||||
try:
|
||||
importers.import_fitnotes_db(fname, request.user)
|
||||
finally:
|
||||
os.remove(fname)
|
||||
else:
|
||||
form = FitnotesUploadForm()
|
||||
return render(request, 'lifting/fitnotes.html', {'form': form})
|
||||
def edit_profile(request):
|
||||
form = request.user.profile
|
||||
|
||||
return render(request, 'profiles/edit.html', {'form': form})
|
||||
|
@ -1 +0,0 @@
|
||||
default_app_config = 'profiles.apps.ProfileConfig'
|
@ -1,23 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
from django.conf import settings
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Profile',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True, serialize=False)),
|
||||
('lifting_units', models.CharField(max_length=1, choices=[('m', 'Metric (kg)'), ('i', 'Imperial (lbs)')])),
|
||||
('user', models.OneToOneField(related_name='config', to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
),
|
||||
]
|
@ -1,41 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
from django.conf import settings
|
||||
import django.contrib.postgres.fields
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('profiles', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Bar',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, serialize=False, verbose_name='ID', primary_key=True)),
|
||||
('name', models.CharField(max_length=100)),
|
||||
('weight_kg', models.DecimalField(max_digits=7, decimal_places=3)),
|
||||
('user', models.OneToOneField(related_name='bar', to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='profile',
|
||||
name='plate_pairs',
|
||||
field=django.contrib.postgres.fields.ArrayField(default=['45', '45', '25', '10', '5', '5', '2.5', '1.25'], base_field=models.DecimalField(max_digits=7, decimal_places=3), size=None),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='profile',
|
||||
name='lifting_units',
|
||||
field=models.CharField(default='i', choices=[('m', 'Metric (kg)'), ('i', 'Imperial (lb)')], max_length=1),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='profile',
|
||||
name='user',
|
||||
field=models.OneToOneField(related_name='profile', to=settings.AUTH_USER_MODEL),
|
||||
),
|
||||
]
|
@ -1,31 +0,0 @@
|
||||
from django.db import models
|
||||
from django.contrib.auth.models import User
|
||||
from django.contrib.postgres.fields import ArrayField
|
||||
from common import to_lb
|
||||
|
||||
UNITS = (
|
||||
('m', 'Metric (kg)'),
|
||||
('i', 'Imperial (lb)'),
|
||||
)
|
||||
|
||||
|
||||
class Profile(models.Model):
|
||||
user = models.OneToOneField(User, related_name='profile')
|
||||
|
||||
lifting_units = models.CharField(max_length=1, choices=UNITS, default='i')
|
||||
plate_pairs = ArrayField(models.DecimalField(max_digits=7, decimal_places=3),
|
||||
default=['45','45','25','10','5','5','2.5','1.25'])
|
||||
|
||||
|
||||
class Bar(models.Model):
|
||||
user = models.OneToOneField(User, related_name='bar')
|
||||
|
||||
name = models.CharField(max_length=100)
|
||||
weight_kg = 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)
|
@ -1,12 +0,0 @@
|
||||
from django.test import TestCase
|
||||
from django.contrib.auth.models import User
|
||||
from .models import Profile
|
||||
|
||||
|
||||
class TestProfile(TestCase):
|
||||
|
||||
def test_signal(self):
|
||||
|
||||
u = User.objects.create_user(username='test', email='test@example.com', password='test')
|
||||
|
||||
assert Profile.objects.filter(user=u).count() == 1
|
@ -1,10 +0,0 @@
|
||||
from django.shortcuts import render
|
||||
from django import forms
|
||||
from django.contrib.auth.decorators import login_required
|
||||
|
||||
|
||||
@login_required
|
||||
def edit_profile(request):
|
||||
form = request.user.profile
|
||||
|
||||
return render(request, 'profiles/edit.html', {'form': form})
|
@ -16,7 +16,7 @@
|
||||
<table class="table day-workouts">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Exercise</th>
|
||||
<th>Lift</th>
|
||||
<th>Weight</th>
|
||||
<th>Reps</th>
|
||||
<tr>
|
||||
@ -24,7 +24,7 @@
|
||||
<tbody>
|
||||
{% for set in sets %}
|
||||
<tr>
|
||||
<td><a href="{% url 'lift-detail' set.exercise.id %}">{{set.exercise}}</a></td>
|
||||
<td><a href="{% url 'lift-detail' set.lift.id %}">{{set.lift}}</a></td>
|
||||
<td>{% mass_unit set.weight_kg %}</td>
|
||||
<td>{{set.reps}}</td>
|
||||
</tr>
|
||||
|
@ -14,7 +14,7 @@
|
||||
<table class="table lifts-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Exercise</th>
|
||||
<th>Lift</th>
|
||||
<th>Total Sets</th>
|
||||
<th>Last Set</th>
|
||||
<th>Max Weight</th>
|
||||
|
@ -21,8 +21,9 @@ INSTALLED_APPS = (
|
||||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
'django_gravatar',
|
||||
'profiles',
|
||||
'inventory',
|
||||
'lifting',
|
||||
'fitnotes',
|
||||
)
|
||||
|
||||
MIDDLEWARE_CLASSES = (
|
||||
|
Loading…
Reference in New Issue
Block a user