Skip to content

Commit

Permalink
feat(export-json): Export User workout in JSON format
Browse files Browse the repository at this point in the history
Currently, wger only supports a user downloading their
workout information in pdf format which is not interoperable.

This commit introduces exporting a user's workout information
in JSON format which promotes interoperability

[Delivers #157731622]
  • Loading branch information
King-Benx committed Jun 21, 2018
1 parent b4a29cc commit f69abf2
Show file tree
Hide file tree
Showing 23 changed files with 446 additions and 23 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,11 @@ target/
# IDE
.idea/

# External Libraries
# External Libraries
# wger/core/static/bower_components
node_modules
.DS_Store/
.vscode/
.idea/
package-lock.json
documents/*
2 changes: 1 addition & 1 deletion settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@

# Path to uploaded files
# Absolute filesystem path to the directory that will hold user-uploaded files.
MEDIA_ROOT = '/Users/tibzy/.local/share/wger/media'
MEDIA_ROOT = ''
MEDIA_URL = '/media/'

# Allow all hosts to access the application. Change if used in production.
Expand Down
2 changes: 1 addition & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ ignore = W503

# Flake8 configuration settings
[flake8]
exclude = .git,__pycache__,urls.py,tasks.py,settings.py,generator.py,*migrations*,*bower_components*,*docs*,__*__,
exclude = .git,__pycache__,urls.py,tasks.py,settings.py,generator.py,*migrations*,*bower_components*,*docs*,__*__,*env*,
max-line-length = 100
ignore = F401,W503
3 changes: 3 additions & 0 deletions wger/core/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -524,6 +524,9 @@ def __str__(self):
'''
return self.day_of_week

def __repr__(self):
return self.day_of_week


@python_2_unicode_compatible
class License(models.Model):
Expand Down
1 change: 1 addition & 0 deletions wger/core/templates/navigation.html
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
data-toggle="dropdown">{% trans "Workouts" %} <b class="caret"></b></a>
<ul class="dropdown-menu">
<li><a href="{% url 'manager:workout:overview' %}" rel="nofollow">{% trans "Workouts" %}</a></li>
<li><a href="{% url 'manager:workout:view_imports' %}" rel="nofollow">{% trans "Imported Workouts" %}</a></li>
<li><a href="{% url 'manager:workout:calendar' user.username %}" rel="nofollow">{% trans "Calendar" %}</a></li>
<li><a href="{% url 'manager:schedule:overview' %}" rel="nofollow">{% trans "Workouts schedules" %}</a></li>
</ul>
Expand Down
2 changes: 1 addition & 1 deletion wger/manager/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,5 @@
admin.site.register(manager_models.Day)
admin.site.register(manager_models.WorkoutLog)
admin.site.register(UserProfile)

admin.site.register(manager_models.ImportJsonDocument)
admin.site.register(manager_models.Setting)
12 changes: 11 additions & 1 deletion wger/manager/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@
Day,
Set,
Setting,
WorkoutLog
WorkoutLog,
ImportJsonDocument
)
from wger.utils.widgets import (
TranslatedSelectMultiple,
Expand Down Expand Up @@ -191,3 +192,12 @@ class Meta:
'time_end': widgets.HiddenInput(),
'user': widgets.HiddenInput(),
'notes': widgets.Textarea(attrs={'rows': 3})}


class DocumentForm(ModelForm):
'''
Form used for importing json docs
'''
class Meta:
model = ImportJsonDocument
fields = ('json_document', 'user')
27 changes: 27 additions & 0 deletions wger/manager/migrations/0010_importjsondocument.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.13 on 2018-06-16 10:04
from __future__ import unicode_literals

from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('manager', '0009_merge'),
]

operations = [
migrations.CreateModel(
name='ImportJsonDocument',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('json_document', models.FileField(upload_to='documents/')),
('uploaded_at', models.DateTimeField(auto_now_add=True)),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='User')),
],
),
]
20 changes: 20 additions & 0 deletions wger/manager/migrations/0011_workout_exported_from.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.13 on 2018-06-19 11:31
from __future__ import unicode_literals

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('manager', '0010_importjsondocument'),
]

operations = [
migrations.AddField(
model_name='workout',
name='exported_from',
field=models.CharField(blank=True, default='', max_length=100, null=True),
),
]
20 changes: 20 additions & 0 deletions wger/manager/migrations/0012_auto_20180619_1451.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.13 on 2018-06-19 11:51
from __future__ import unicode_literals

from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
('manager', '0011_workout_exported_from'),
]

operations = [
migrations.RenameField(
model_name='workout',
old_name='exported_from',
new_name='imported_from',
),
]
16 changes: 16 additions & 0 deletions wger/manager/migrations/0013_merge.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.13 on 2018-06-21 06:17
from __future__ import unicode_literals

from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
('manager', '0010_auto_20180620_1044'),
('manager', '0012_auto_20180619_1451'),
]

operations = [
]
20 changes: 20 additions & 0 deletions wger/manager/migrations/0014_auto_20180621_1341.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.13 on 2018-06-21 10:41
from __future__ import unicode_literals

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('manager', '0013_merge'),
]

operations = [
migrations.AlterField(
model_name='workouttype',
name='category',
field=models.CharField(choices=[('1', 'Default'), ('2', 'Dropset')], default='1', max_length=10),
),
]
26 changes: 22 additions & 4 deletions wger/manager/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ class Meta:
help_text=_("A short description or goal of the workout. For "
"example 'Focus on back' or 'Week 1 of program xy'."))
user = models.ForeignKey(User, verbose_name=_('User'))
imported_from = models.CharField(max_length=100, blank=True, null=True, default='')

def get_absolute_url(self):
'''
Expand Down Expand Up @@ -444,7 +445,6 @@ def get_canonical_representation(self):
has_setting_tmp = True
for exercise in set_obj.exercises.select_related():
setting_tmp = []


# Muscles for this set
for muscle in exercise.muscles.all():
Expand Down Expand Up @@ -529,7 +529,7 @@ def get_canonical_representation(self):
tmp_days_of_week = []
for day_of_week in self.day.select_related():
tmp_days_of_week.append(day_of_week)

return {'obj': self,
'days_of_week': {
'text': u', '.join([six.text_type(_(i.day_of_week))
Expand Down Expand Up @@ -609,10 +609,12 @@ class WorkoutType(models.Model):
)

category = models.CharField(max_length=10,
choices=TYPES, default=TYPE_DEFAULT)
choices=TYPES, default=TYPE_DEFAULT)

def __str__(self):
return self.category


@python_2_unicode_compatible
class Setting(models.Model):
'''
Expand All @@ -625,7 +627,7 @@ class Setting(models.Model):
repetition_unit = models.ForeignKey(RepetitionUnit,
verbose_name=_('Unit'),
default=1)

category = models.ForeignKey(WorkoutType, on_delete=models.CASCADE, null=True)
'''
The repetition unit of a set. This can be e.g. a repetition, a minute, etc.
Expand Down Expand Up @@ -906,3 +908,19 @@ def delete(self, *args, **kwargs):
'''
reset_workout_log(self.user_id, self.date.year, self.date.month)
super(WorkoutSession, self).delete(*args, **kwargs)


@python_2_unicode_compatible
class ImportJsonDocument(models.Model):
"""
Model for handling importing json files
"""
json_document = models.FileField(upload_to='documents/')
user = models.ForeignKey(User, verbose_name=_('User'))
uploaded_at = models.DateTimeField(auto_now_add=True)

def __str__(self):
'''
Return a more human-readable representation
'''
return self.json_document
46 changes: 46 additions & 0 deletions wger/manager/templates/workout/imports.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
{% extends "base.html" %}
{% load i18n %}
{% load staticfiles %}

{% block title %}{% trans "Your Imports &amp; Exports" %}{% endblock %}


{% block content %}
{% if imports %}
<div class="table-responsive">
<table class="table table-hovered table-bordered table-checked table-hovered">
<tr>
<th>{% trans "Import Date"%}</th>
<th>{% trans "Description" %}</th>
<th>{% trans "Sets per Day"%}</th>
<th>{% trans "Workout Days"%}</th>
<th>{% trans "Overview" %}</th>
{% comment %} <th>{% trans "Action" %}</th> {% endcomment %}
</tr>
{{table_content|safe}}
</table>
</div>
{% else %}
<h4>{% trans message %}</h4>
{% endif %}
{% endblock %}


{% block sidebar %}
<h4>{% trans "Your imports" %}</h4>
<p>{% blocktrans %}
This shows all your recent imported workouts
{% endblocktrans %}
</p>
<hr/>
<h4>{%trans "Import Workout "%}</h4>
<form action="{% url 'manager:workout:export_json' user.pk %}" method="post" enctype="multipart/form-data">
{% csrf_token %}
<br/>
<input type="file" name="json_document" class="form-control" required="required" accept=".json,.JSON"/>
<input type="hidden" name="user" id="user" value="{{user.pk}}" >
<br/>
<button class="btn btn-primary btn-block" type="submit">{% trans "UPLOAD JSON FILE" %}</button>
</form>
{% endblock %}

43 changes: 43 additions & 0 deletions wger/manager/templates/workout/view.html
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,43 @@ <h4 class="modal-title">{% trans "Download as PDF" %}</h4>
</div>
</div>
</div>
<div class="modal fade" id="import-export-popup">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title">{% trans "Import/Export Workouts as JSON" %}</h4>
</div>
<div class="modal-body">
<ul class="nav nav-tabs">
<li role="presentation" class="active"><a href="#export-json" data-toggle="tab">{% trans "Export Workouts" %}</a></li>
<li role="presentation"><a href="#import-json" data-toggle="tab">{% trans "Import Workouts" %}</a></li>
</ul>
<div class="tab-content">
<div id="export-json" class="tab-pane fade in active text-center">
<br/>
<a href="{% url 'manager:workout:export_json' workout.id %}" class="btn btn-success btn-block"><span class="{% fa_class 'file-code-o' %}"></span> {% trans "Export" %}</a>
<br/>
</div>
<div id="import-json" class="tab-pane fade active">
<form action="{% url 'manager:workout:export_json' workout.id %}" method="post" enctype="multipart/form-data">
{% csrf_token %}
<br/>
<input type="file" name="json_document" class="form-control" required="required" accept=".json,.JSON"/>
<input type="hidden" name="user" id="user" value="{{user.pk}}" >
<br/>
<button class="btn btn-primary btn-block" type="submit">{% trans "UPLOAD JSON FILE" %}</button>
</form>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">{% trans "Close" %}</button>
</div>
</div>
</div>
</div>

{% endblock %}


Expand All @@ -225,6 +262,12 @@ <h4 class="modal-title">{% trans "Download as PDF" %}</h4>
{% trans "Download as PDF" %}
</a>
</li>
<li>
<a data-toggle="modal" data-target="#import-export-popup">
<span class="{% fa_class 'file-code-o' %}"></span>
{% trans "Export/Import workout as JSON" %}
</a>
</li>
<li>
<a href="{% url 'manager:log:log' workout.id %}">
<span class="{% fa_class 'line-chart' %}"></span>
Expand Down
4 changes: 2 additions & 2 deletions wger/manager/tests/test_workout_canonical.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ def test_canonical_form(self):
'backsecondary': [],
'front': [1]},
'obj': Set.objects.get(pk=1),
'category': WorkoutType.objects.get(pk=1),}]}
'category': WorkoutType.objects.get(pk=1), }]}
self.assertEqual(workout.canonical_representation['day_list'][0], canonical_form)

canonical_form = {'days_of_week': {'day_list': [DaysOfWeek.objects.get(pk=4)],
Expand Down Expand Up @@ -112,7 +112,7 @@ def test_canonical_form(self):
'frontsecondary': [1], 'backsecondary': [1],
'front': []},
'obj': Set.objects.get(pk=2),
'category': WorkoutType.objects.get(pk=1),}]}
'category': WorkoutType.objects.get(pk=1), }]}
self.assertEqual(workout.canonical_representation['day_list'][1], canonical_form)

canonical_form = {'days_of_week': {'day_list': [DaysOfWeek.objects.get(pk=5)],
Expand Down
3 changes: 3 additions & 0 deletions wger/manager/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,9 @@
url(r'^(?P<pk>\d+)/ical$',
ical.export,
name='ical'),
url(r'^(?P<pk>\d+)/export_json/$', workout.export_json, name='export_json'),
url(r'^view_imports$', workout.view_imports, name='view_imports'),
# url(r'^import_workout/(?P<imported_from>[0-9A-Za-z_\-]+)/$', workout.import_workout, name='import_workout'),
url(r'^(?P<id>\d+)/pdf/log/(?P<images>[01]+)/(?P<comments>[01]+)/(?P<uidb64>[0-9A-Za-z_\-]+)/(?P<token>[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})$',
pdf.workout_log,
name='pdf-log'), #JS!
Expand Down
2 changes: 1 addition & 1 deletion wger/manager/views/day.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ def get_success_url(self):

def get_form(self, form_class=DayForm):
'''
Filter the days of the week that are alreeady used by other days
Filter the days of the week that are already used by other days
'''

# Get the form
Expand Down
Loading

0 comments on commit f69abf2

Please sign in to comment.