diff --git a/extras/scripts/filter-fixtures.py b/extras/scripts/filter-fixtures.py
index 641e6f1b5..3f5398004 100644
--- a/extras/scripts/filter-fixtures.py
+++ b/extras/scripts/filter-fixtures.py
@@ -52,6 +52,7 @@ filter_dump(data, ('exercises.muscle',), 'muscles.json')
filter_dump(data, ('exercises.exercisecategory',), 'categories.json')
filter_dump(data, ('exercises.exerciseimage',), 'exercise-images.json')
filter_dump(data, ('exercises.exercise', 'exercises.exercisecomment',), 'exercises.json')
+filter_dump(data, ('exercises.equipment', 'exercises.equipment',), 'equipment.json')
#
# Other
diff --git a/wger/config/management/commands/extract-categories-i18n.py b/wger/config/management/commands/extract-categories-i18n.py
index 8d0c86b89..2109bd601 100644
--- a/wger/config/management/commands/extract-categories-i18n.py
+++ b/wger/config/management/commands/extract-categories-i18n.py
@@ -15,20 +15,24 @@
# You should have received a copy of the GNU Affero General Public License
from django.core.management.base import BaseCommand
+
from wger.exercises.models import ExerciseCategory
+from wger.exercises.models import Equipment
class Command(BaseCommand):
'''
- Helper command to read out the exercise categories to manually include in
- the .po files
+ Helper command to read out the strings to manually include in the .po files
'''
- help = 'Read the exercise categories to include in .po file'
+ help = 'Read out all strings that have to be included manually in the .po file'
def handle(self, *args, **options):
- categories = ExerciseCategory.objects.all()
- for category in categories:
- self.stdout.write('msgid "{0}"\n'
- 'msgstr ""\n\n'.format(category))
+ for category in ExerciseCategory.objects.all():
+ self.stdout.write('msgid "{0}"\n'
+ 'msgstr ""\n\n'.format(category))
+
+ for equipment in Equipment.objects.all():
+ self.stdout.write('msgid "{0}"\n'
+ 'msgstr ""\n\n'.format(equipment))
diff --git a/wger/exercises/api/resources.py b/wger/exercises/api/resources.py
index 0e3c99030..22bba0bae 100644
--- a/wger/exercises/api/resources.py
+++ b/wger/exercises/api/resources.py
@@ -26,6 +26,7 @@ from wger.exercises.models import ExerciseComment
from wger.exercises.models import ExerciseImage
from wger.exercises.models import Muscle
from wger.exercises.models import Language
+from wger.exercises.models import Equipment
class ExerciseResource(ModelResource):
@@ -43,6 +44,12 @@ class ExerciseResource(ModelResource):
queryset = Exercise.objects.all()
+class EquipmentResource(ModelResource):
+
+ class Meta:
+ queryset = Equipment.objects.all()
+
+
class ExerciseCategoryResource(ModelResource):
class Meta:
diff --git a/wger/exercises/fixtures/test-equipment.json b/wger/exercises/fixtures/test-equipment.json
new file mode 100644
index 000000000..6b5a8f2b7
--- /dev/null
+++ b/wger/exercises/fixtures/test-equipment.json
@@ -0,0 +1,23 @@
+[
+ {
+ "pk": 1,
+ "model": "exercises.equipment",
+ "fields": {
+ "name": "Dumbbells"
+ }
+ },
+ {
+ "pk": 2,
+ "model": "exercises.equipment",
+ "fields": {
+ "name": "Kettlebell"
+ }
+ },
+ {
+ "pk": 3,
+ "model": "exercises.equipment",
+ "fields": {
+ "name": "Something else"
+ }
+ }
+]
diff --git a/wger/exercises/migrations/0009_auto__add_equipment.py b/wger/exercises/migrations/0009_auto__add_equipment.py
new file mode 100644
index 000000000..a841f26d1
--- /dev/null
+++ b/wger/exercises/migrations/0009_auto__add_equipment.py
@@ -0,0 +1,124 @@
+# -*- coding: utf-8 -*-
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+ # Adding model 'Equipment'
+ db.create_table(u'exercises_equipment', (
+ (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('name', self.gf('django.db.models.fields.CharField')(max_length=50)),
+ ))
+ db.send_create_signal(u'exercises', ['Equipment'])
+
+ # Adding M2M table for field equipment on 'Exercise'
+ m2m_table_name = db.shorten_name(u'exercises_exercise_equipment')
+ db.create_table(m2m_table_name, (
+ ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
+ ('exercise', models.ForeignKey(orm[u'exercises.exercise'], null=False)),
+ ('equipment', models.ForeignKey(orm[u'exercises.equipment'], null=False))
+ ))
+ db.create_unique(m2m_table_name, ['exercise_id', 'equipment_id'])
+
+
+ def backwards(self, orm):
+ # Deleting model 'Equipment'
+ db.delete_table(u'exercises_equipment')
+
+ # Removing M2M table for field equipment on 'Exercise'
+ db.delete_table(db.shorten_name(u'exercises_exercise_equipment'))
+
+
+ models = {
+ u'auth.group': {
+ 'Meta': {'object_name': 'Group'},
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+ 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+ },
+ u'auth.permission': {
+ 'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
+ 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+ },
+ u'auth.user': {
+ 'Meta': {'object_name': 'User'},
+ 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+ 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+ },
+ u'contenttypes.contenttype': {
+ 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+ 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ },
+ u'exercises.equipment': {
+ 'Meta': {'ordering': "['name']", 'object_name': 'Equipment'},
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+ },
+ u'exercises.exercise': {
+ 'Meta': {'ordering': "['name']", 'object_name': 'Exercise'},
+ 'category': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['exercises.ExerciseCategory']"}),
+ 'creation_date': ('django.db.models.fields.DateField', [], {'auto_now_add': 'True', 'null': 'True', 'blank': 'True'}),
+ 'description': ('django.db.models.fields.TextField', [], {'max_length': '2000'}),
+ 'equipment': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': u"orm['exercises.Equipment']", 'null': 'True', 'blank': 'True'}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'language': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['exercises.Language']"}),
+ 'muscles': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['exercises.Muscle']", 'symmetrical': 'False'}),
+ 'muscles_secondary': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'secondary_muscles'", 'null': 'True', 'symmetrical': 'False', 'to': u"orm['exercises.Muscle']"}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
+ 'status': ('django.db.models.fields.CharField', [], {'default': "'1'", 'max_length': '2'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'})
+ },
+ u'exercises.exercisecategory': {
+ 'Meta': {'ordering': "['name']", 'object_name': 'ExerciseCategory'},
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ },
+ u'exercises.exercisecomment': {
+ 'Meta': {'object_name': 'ExerciseComment'},
+ 'comment': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
+ 'exercise': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['exercises.Exercise']"}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
+ },
+ u'exercises.exerciseimage': {
+ 'Meta': {'ordering': "['-is_main', 'id']", 'object_name': 'ExerciseImage'},
+ 'exercise': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['exercises.Exercise']"}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'image': ('django.db.models.fields.files.ImageField', [], {'max_length': '100'}),
+ 'is_main': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
+ },
+ u'exercises.language': {
+ 'Meta': {'object_name': 'Language'},
+ 'full_name': ('django.db.models.fields.CharField', [], {'max_length': '30'}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'short_name': ('django.db.models.fields.CharField', [], {'max_length': '2'})
+ },
+ u'exercises.muscle': {
+ 'Meta': {'ordering': "['name']", 'object_name': 'Muscle'},
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_front': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+ }
+ }
+
+ complete_apps = ['exercises']
\ No newline at end of file
diff --git a/wger/exercises/models.py b/wger/exercises/models.py
index 5a7675081..58307be4b 100644
--- a/wger/exercises/models.py
+++ b/wger/exercises/models.py
@@ -81,9 +81,9 @@ class Muscle(models.Model):
Muscle an exercise works out
'''
- # Name, in latin, e.g. "Pectoralis major"
name = models.CharField(max_length=50,
- verbose_name=_('Name'))
+ verbose_name=_('Name'),
+ help_text=_('In latin, e.g. "Pectoralis major"'))
# Whether to use the front or the back image for background
is_front = models.BooleanField(default=1)
@@ -105,6 +105,33 @@ class Muscle(models.Model):
return False
+class Equipment(models.Model):
+ '''
+ Equipment used or needed by an exercise
+ '''
+
+ name = models.CharField(max_length=50,
+ verbose_name=_('Name'))
+
+ class Meta:
+ '''
+ Set default ordering
+ '''
+ ordering = ["name", ]
+
+ def __unicode__(self):
+ '''
+ Return a more human-readable representation
+ '''
+ return self.name
+
+ def get_owner_object(self):
+ '''
+ Equipment has no owner information
+ '''
+ return False
+
+
class ExerciseCategory(models.Model):
'''
Model for an exercise category
@@ -175,6 +202,12 @@ class Exercise(models.Model):
)
'''Secondary muscles trained by the exercise'''
+ equipment = models.ManyToManyField(Equipment,
+ verbose_name=_('Equipment'),
+ null=True,
+ blank=True)
+ '''Equipment needed by this exercise'''
+
# Non-editable fields
user = models.ForeignKey(User, verbose_name=_('User'), null=True, blank=True)
'''The user that submitted the exercise'''
diff --git a/wger/exercises/templates/equipment/list.html b/wger/exercises/templates/equipment/list.html
new file mode 100644
index 000000000..bd083077d
--- /dev/null
+++ b/wger/exercises/templates/equipment/list.html
@@ -0,0 +1,72 @@
+{% extends "base.html" %}
+{% load url from future %}
+{% load i18n %}
+{% load staticfiles %}
+{% load wger_extras %}
+
+{% block title %}{% trans "Equipment list" %}{% endblock %}
+
+
+{% block content %}
+
+
+
+
+ | {% trans "Actions" %} |
+ {% trans "Name" %} |
+
+
+
+{% for equipment in equipment_list %}
+
+
+
+
+
+
+
+
+ |
+ {{equipment.name}} |
+
+{% empty %}
+
+ | {% trans "No equipment found" %} |
+
+{% endfor %}
+
+
+
+
+{% pagination paginator page_obj %}
+{% endblock %}
+
+
+
+
+{% block sidebar %}
+{% if perms.exercises.add_equipment %}
+{% trans "Options" %}
+
+
+
+ {% trans "Add new equipment" %}
+
+
+{% endif %}
+{% endblock %}
diff --git a/wger/exercises/templates/mobile/view.html b/wger/exercises/templates/mobile/view.html
index b3b64d0dd..409e560c1 100644
--- a/wger/exercises/templates/mobile/view.html
+++ b/wger/exercises/templates/mobile/view.html
@@ -66,7 +66,11 @@ included in the database.{% endblocktrans %}
{% endif %} {# end exercise is pending review #}
-{% trans "Category" %}: {% trans exercise.category.name %}
+{% trans "Category" %}: {% trans exercise.category.name %}
+{% if exercise.equipment.all %}
+ {% trans "Equipment" %}: {{ exercise.equipment.all|join:", " }}
+{% endif %}
+
{{ exercise.description|safe }}
{# #}
diff --git a/wger/exercises/templates/view.html b/wger/exercises/templates/view.html
index 06429a22c..3a3cf17e1 100644
--- a/wger/exercises/templates/view.html
+++ b/wger/exercises/templates/view.html
@@ -76,8 +76,14 @@ included in the database.{% endblocktrans %}
{% endif %} {# end exercise is pending review #}
+
{% cache cache_timeout exercise-detail-header exercise.id language.id %}
-{% trans "Category" %}: {% trans exercise.category.name %}
+{% trans "Category" %}: {% trans exercise.category.name %}
+
+{% if exercise.equipment.all %}
+ {% trans "Equipment" %}: {{ exercise.equipment.all|join:", " }}
+{% endif %}
+
{{ exercise.description|safe }}
{% endcache %}
@@ -87,7 +93,7 @@ included in the database.{% endblocktrans %}
{# #}
{% with comments=exercise.exercisecomment_set.all %}
{% if comments %}
-{% trans "Comments for this exercise" %}
+{% trans "Comments for this exercise" %}
{% for comment in comments %}
@@ -137,7 +143,7 @@ included in the database.{% endblocktrans %}
{# #}
{% with images=exercise.exerciseimage_set.all %}
{% if images %}
- {% trans "Images" %}
+ {% trans "Images" %}
{% with main_image=images|first %}
diff --git a/wger/exercises/tests/test_equipment.py b/wger/exercises/tests/test_equipment.py
new file mode 100644
index 000000000..6fd1bb619
--- /dev/null
+++ b/wger/exercises/tests/test_equipment.py
@@ -0,0 +1,115 @@
+# This file is part of wger Workout Manager.
+#
+# wger Workout Manager is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# wger Workout Manager is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with Workout Manager. If not, see .
+
+from django.core.urlresolvers import reverse
+
+from wger.exercises.models import Equipment
+
+from wger.manager.tests.testcase import WorkoutManagerTestCase
+from wger.manager.tests.testcase import WorkoutManagerDeleteTestCase
+from wger.manager.tests.testcase import WorkoutManagerEditTestCase
+from wger.manager.tests.testcase import WorkoutManagerAddTestCase
+from wger.manager.tests.testcase import ApiBaseResourceTestCase
+
+from wger.utils.constants import PAGINATION_OBJECTS_PER_PAGE
+
+
+class AddEquipmentTestCase(WorkoutManagerAddTestCase):
+ '''
+ Tests adding a new equipment
+ '''
+
+ object_class = Equipment
+ url = 'equipment-add'
+ data = {'name': 'A new equipment'}
+ pk = 4
+
+
+class DeleteEquipmentTestCase(WorkoutManagerDeleteTestCase):
+ '''
+ Tests deleting an equipment
+ '''
+
+ object_class = Equipment
+ url = 'equipment-delete'
+ pk = 1
+
+
+class EditEquipmentTestCase(WorkoutManagerEditTestCase):
+ '''
+ Tests editing an equipment
+ '''
+
+ object_class = Equipment
+ url = 'equipment-edit'
+ pk = 1
+ data = {'name': 'A new name'}
+
+
+class EquipmentOverviewTestCase(WorkoutManagerTestCase):
+ '''
+ Tests the equipment overview page
+ '''
+
+ def test_overview(self):
+
+ # Add more equipments so we can test the pagination
+ self.user_login('admin')
+ data = {"name": "A new entry"}
+ for i in range(0, 50):
+ self.client.post(reverse('equipment-add'), data)
+
+ # Page exists and the pagination works
+ response = self.client.get(reverse('equipment-list'))
+ self.assertEqual(response.status_code, 200)
+ self.assertEqual(len(response.context['equipment_list']), PAGINATION_OBJECTS_PER_PAGE)
+
+ response = self.client.get(reverse('equipment-list'), {'page': 2})
+ self.assertEqual(response.status_code, 200)
+ self.assertEqual(len(response.context['equipment_list']), PAGINATION_OBJECTS_PER_PAGE)
+
+ response = self.client.get(reverse('equipment-list'), {'page': 3})
+ self.assertEqual(response.status_code, 200)
+ self.assertEqual(len(response.context['equipment_list']), 3)
+
+ # 'last' is a special case
+ response = self.client.get(reverse('equipment-list'), {'page': 'last'})
+ self.assertEqual(response.status_code, 200)
+ self.assertEqual(len(response.context['equipment_list']), 3)
+
+ # Page does not exist
+ response = self.client.get(reverse('equipment-list'), {'page': 100})
+ self.assertEqual(response.status_code, 404)
+
+ response = self.client.get(reverse('equipment-list'), {'page': 'foobar'})
+ self.assertEqual(response.status_code, 404)
+
+
+class EquipmentApiTestCase(ApiBaseResourceTestCase):
+ '''
+ Tests the equipment overview resource
+ '''
+ resource = 'equipment'
+ user = None
+ resource_updatable = False
+
+
+class EquipmentDetailApiTestCase(ApiBaseResourceTestCase):
+ '''
+ Tests accessing a specific equipment
+ '''
+ resource = 'equipment/1'
+ user = None
+ resource_updatable = False
diff --git a/wger/exercises/urls.py b/wger/exercises/urls.py
index 8aee1eb8f..c409b680f 100644
--- a/wger/exercises/urls.py
+++ b/wger/exercises/urls.py
@@ -6,6 +6,7 @@ from wger.exercises.views import comments
from wger.exercises.views import categories
from wger.exercises.views import muscles
from wger.exercises.views import images
+from wger.exercises.views import equipment
urlpatterns = patterns('wger.exercises.views',
@@ -87,4 +88,18 @@ urlpatterns = patterns('wger.exercises.views',
images.ExerciseImageDeleteView.as_view(),
name='exerciseimage-delete'),
+ # Equipment
+ url(r'^equipment/list$',
+ equipment.EquipmentListView.as_view(),
+ name='equipment-list'),
+ url(r'^equipment/add$',
+ equipment.EquipmentAddView.as_view(),
+ name='equipment-add'),
+ url(r'^equipment/(?P\d+)/edit$',
+ equipment.EquipmentEditView.as_view(),
+ name='equipment-edit'),
+ url(r'^equipment/(?P\d+)/delete$',
+ equipment.EquipmentDeleteView.as_view(),
+ name='equipment-delete'),
+
)
diff --git a/wger/exercises/views/equipment.py b/wger/exercises/views/equipment.py
new file mode 100644
index 000000000..8e398e00f
--- /dev/null
+++ b/wger/exercises/views/equipment.py
@@ -0,0 +1,115 @@
+# -*- coding: utf-8 -*-
+
+# This file is part of wger Workout Manager.
+#
+# wger Workout Manager is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# wger Workout Manager is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+import logging
+
+from django.core.urlresolvers import reverse
+from django.core.urlresolvers import reverse_lazy
+from django.utils.translation import ugettext_lazy
+from django.utils.translation import ugettext as _
+
+from django.views.generic import CreateView
+from django.views.generic import UpdateView
+from django.views.generic import DeleteView
+from django.views.generic import ListView
+
+from wger.exercises.models import Equipment
+
+from wger.utils.generic_views import WgerFormMixin
+from wger.utils.generic_views import WgerDeleteMixin
+from wger.utils.generic_views import WgerPermissionMixin
+from wger.utils.constants import PAGINATION_OBJECTS_PER_PAGE
+
+
+logger = logging.getLogger('wger.custom')
+
+'''
+Exercise equipment
+'''
+
+
+class EquipmentListView(WgerPermissionMixin, ListView):
+ '''
+ Generic view to list all equipments
+ '''
+
+ model = Equipment
+ template_name = 'equipment/list.html'
+ context_object_name = 'equipment_list'
+ paginate_by = PAGINATION_OBJECTS_PER_PAGE
+ permission_required = 'exercises.change_equipment'
+
+
+class EquipmentEditView(WgerFormMixin, UpdateView, WgerPermissionMixin):
+ '''
+ Generic view to update an existing equipment item
+ '''
+
+ model = Equipment
+ title = ugettext_lazy('Edit equipment')
+ permission_required = 'exercises.change_equipment'
+ success_url = reverse_lazy('equipment-list')
+
+ # Send some additional data to the template
+ def get_context_data(self, **kwargs):
+ context = super(EquipmentEditView, self).get_context_data(**kwargs)
+ context['form_action'] = reverse('equipment-edit',
+ kwargs={'pk': self.object.id})
+
+ return context
+
+
+class EquipmentAddView(WgerFormMixin, CreateView, WgerPermissionMixin):
+ '''
+ Generic view to add a new equipment item
+ '''
+
+ model = Equipment
+ title = ugettext_lazy('Add equipment')
+ permission_required = 'exercises.add_equipment'
+ success_url = reverse_lazy('equipment-list')
+
+ def get_context_data(self, **kwargs):
+ '''
+ Send some additional data to the template
+ '''
+ context = super(EquipmentAddView, self).get_context_data(**kwargs)
+ context['form_action'] = reverse('equipment-add')
+
+ return context
+
+
+class EquipmentDeleteView(WgerDeleteMixin, DeleteView, WgerPermissionMixin):
+ '''
+ Generic view to delete an existing exercise image
+ '''
+
+ model = Equipment
+ messages = ugettext_lazy('Equipment successfully deleted')
+ permission_required = 'exercises.delete_equipment'
+ success_url = reverse_lazy('equipment-list')
+
+ def get_context_data(self, **kwargs):
+ '''
+ Send some additional data to the template
+ '''
+ pk = self.kwargs['pk']
+ context = super(EquipmentDeleteView, self).get_context_data(**kwargs)
+
+ context['title'] = _('Delete equipment?')
+ context['form_action'] = reverse('equipment-delete',
+ kwargs={'pk': pk})
+
+ return context
diff --git a/wger/exercises/views/exercises.py b/wger/exercises/views/exercises.py
index 2461e22cb..5318fb242 100644
--- a/wger/exercises/views/exercises.py
+++ b/wger/exercises/views/exercises.py
@@ -55,6 +55,7 @@ from wger.utils.language import load_language
from wger.utils.language import load_item_languages
from wger.utils.cache import cache_mapper
from wger.utils.widgets import TranslatedSelect
+from wger.utils.widgets import TranslatedSelectMultiple
from wger.config.models import LanguageConfig
@@ -159,7 +160,8 @@ class ExercisesEditAddView(WgerFormMixin):
'category',
'muscles',
'muscles_secondary',
- 'description']
+ 'description',
+ 'equipment']
title = ugettext_lazy('Add exercise')
custom_js = 'init_tinymce();'
@@ -177,6 +179,7 @@ class ExercisesEditAddView(WgerFormMixin):
class Meta:
model = Exercise
+ widgets = {'equipment': TranslatedSelectMultiple()}
class Media:
js = ('js/tinymce/tiny_mce.js',)
diff --git a/wger/manager/fixtures/groups.json b/wger/manager/fixtures/groups.json
index ad8dcd0df..c94da13fd 100644
--- a/wger/manager/fixtures/groups.json
+++ b/wger/manager/fixtures/groups.json
@@ -20,6 +20,21 @@
"config",
"languageconfig"
],
+ [
+ "add_equipment",
+ "exercises",
+ "equipment"
+ ],
+ [
+ "change_equipment",
+ "exercises",
+ "equipment"
+ ],
+ [
+ "delete_equipment",
+ "exercises",
+ "equipment"
+ ],
[
"add_exercise",
"exercises",
@@ -158,4 +173,4 @@
]
}
}
-]
+]
\ No newline at end of file
diff --git a/wger/manager/fixtures/test-user-data.json b/wger/manager/fixtures/test-user-data.json
index 53ca4f712..b0b3f2938 100644
--- a/wger/manager/fixtures/test-user-data.json
+++ b/wger/manager/fixtures/test-user-data.json
@@ -20,6 +20,21 @@
"config",
"languageconfig"
],
+ [
+ "add_equipment",
+ "exercises",
+ "equipment"
+ ],
+ [
+ "change_equipment",
+ "exercises",
+ "equipment"
+ ],
+ [
+ "delete_equipment",
+ "exercises",
+ "equipment"
+ ],
[
"add_exercise",
"exercises",
@@ -65,6 +80,21 @@
"exercises",
"exercisecomment"
],
+ [
+ "add_exerciseimage",
+ "exercises",
+ "exerciseimage"
+ ],
+ [
+ "change_exerciseimage",
+ "exercises",
+ "exerciseimage"
+ ],
+ [
+ "delete_exerciseimage",
+ "exercises",
+ "exerciseimage"
+ ],
[
"add_language",
diff --git a/wger/manager/templates/mobile/navigation.html b/wger/manager/templates/mobile/navigation.html
index 041c2fcea..d4851a459 100644
--- a/wger/manager/templates/mobile/navigation.html
+++ b/wger/manager/templates/mobile/navigation.html
@@ -62,6 +62,7 @@
{% if perms.exercises %}
- {% trans "Administration" %}
- {% trans "Exercises pending review" %}
+ - {% trans "Equipment" %}
{% endif %}
diff --git a/wger/manager/templates/navigation.html b/wger/manager/templates/navigation.html
index c914c37a1..93aef1eb1 100644
--- a/wger/manager/templates/navigation.html
+++ b/wger/manager/templates/navigation.html
@@ -51,6 +51,7 @@
{% trans "Exercises pending review" %}
+ {% trans "Equipment" %}
{% endif %}
{% endcache %}
diff --git a/wger/manager/tests/testcase.py b/wger/manager/tests/testcase.py
index 80b047622..30914269c 100644
--- a/wger/manager/tests/testcase.py
+++ b/wger/manager/tests/testcase.py
@@ -58,6 +58,7 @@ class BaseTestCase(object):
'test-user-data',
'test-apikeys',
'test-weight-data',
+ 'test-equipment',
'test-exercises',
'test-exercise-images',
'test-weight-units',
diff --git a/wger/urls.py b/wger/urls.py
index f9ac508fb..12f60016b 100644
--- a/wger/urls.py
+++ b/wger/urls.py
@@ -25,6 +25,7 @@ v1_api.register(exercises_api.ExerciseImageResource())
v1_api.register(exercises_api.ExerciseResource())
v1_api.register(exercises_api.MuscleResource())
v1_api.register(exercises_api.LanguageResource())
+v1_api.register(exercises_api.EquipmentResource())
# Nutrition app
v1_api.register(nutrition_api.IngredientResource())
diff --git a/wger/utils/tests/__init__.py b/wger/utils/tests/__init__.py
index 361dfffc9..e69de29bb 100644
--- a/wger/utils/tests/__init__.py
+++ b/wger/utils/tests/__init__.py
@@ -1 +0,0 @@
-from wger.utils.tests.middleware import *