diff --git a/extras/scripts/filter-fixtures.py b/extras/scripts/filter-fixtures.py index 6702d93ff..659a1a5d8 100644 --- a/extras/scripts/filter-fixtures.py +++ b/extras/scripts/filter-fixtures.py @@ -60,7 +60,12 @@ filter_dump(data, ('exercises.equipment', 'exercises.equipment',), 'equipment.js filter_dump(data, ('core.gym',), 'gyms.json') filter_dump(data, ('core.language',), 'languages.json') filter_dump(data, ('core.license',), 'licenses.json') + +# +# Configurations +# filter_dump(data, ('config.languageconfig',), 'language_config.json') +filter_dump(data, ('config.gymconfig',), 'gym-config.json') # # Other diff --git a/wger/config/fixtures/gym-config.json b/wger/config/fixtures/gym-config.json new file mode 100644 index 000000000..d24002e11 --- /dev/null +++ b/wger/config/fixtures/gym-config.json @@ -0,0 +1,9 @@ +[ + { + "pk": 1, + "model": "config.gymconfig", + "fields": { + "default_gym": null + } + } +] \ No newline at end of file diff --git a/wger/config/migrations/0003_auto__add_gymconfig__chg_field_languageconfig_language_target__chg_fie.py b/wger/config/migrations/0003_auto__add_gymconfig__chg_field_languageconfig_language_target__chg_fie.py new file mode 100644 index 000000000..8d2443ee1 --- /dev/null +++ b/wger/config/migrations/0003_auto__add_gymconfig__chg_field_languageconfig_language_target__chg_fie.py @@ -0,0 +1,69 @@ +# -*- coding: utf-8 -*- +from south.utils import datetime_utils as 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 'GymConfig' + db.create_table(u'config_gymconfig', ( + (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('default_gym', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['core.Gym'], null=True, blank=True)), + )) + db.send_create_signal(u'config', ['GymConfig']) + + + # Changing field 'LanguageConfig.language_target' + db.alter_column(u'config_languageconfig', 'language_target_id', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['core.Language'])) + + # Changing field 'LanguageConfig.language' + db.alter_column(u'config_languageconfig', 'language_id', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['core.Language'])) + + def backwards(self, orm): + # Deleting model 'GymConfig' + db.delete_table(u'config_gymconfig') + + + # Changing field 'LanguageConfig.language_target' + db.alter_column(u'config_languageconfig', 'language_target_id', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['exercises.Language'])) + + # Changing field 'LanguageConfig.language' + db.alter_column(u'config_languageconfig', 'language_id', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['exercises.Language'])) + + models = { + u'config.gymconfig': { + 'Meta': {'object_name': 'GymConfig'}, + 'default_gym': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['core.Gym']", 'null': 'True', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + }, + u'config.languageconfig': { + 'Meta': {'ordering': "['item', 'language_target']", 'object_name': 'LanguageConfig'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'item': ('django.db.models.fields.CharField', [], {'max_length': '2'}), + 'language': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'language_source'", 'to': u"orm['core.Language']"}), + 'language_target': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'language_target'", 'to': u"orm['core.Language']"}), + 'show': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + u'core.gym': { + 'Meta': {'object_name': 'Gym'}, + 'city': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True', 'blank': 'True'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'null': 'True', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '60'}), + 'owner': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'phone': ('django.db.models.fields.CharField', [], {'max_length': '20', 'null': 'True', 'blank': 'True'}), + 'street': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True', 'blank': 'True'}), + 'zip_code': ('django.db.models.fields.IntegerField', [], {'max_length': '5', 'null': 'True', 'blank': 'True'}) + }, + u'core.language': { + 'Meta': {'ordering': "['full_name']", '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'}) + } + } + + complete_apps = ['config'] \ No newline at end of file diff --git a/wger/config/models.py b/wger/config/models.py index 963084d77..d6139d8bb 100644 --- a/wger/config/models.py +++ b/wger/config/models.py @@ -22,6 +22,8 @@ from django.db.models.signals import post_save from django.utils.translation import ugettext_lazy as _ from django.core.cache import cache from wger.core.models import Language +from wger.core.models import Gym +from wger.core.models import UserProfile from wger.utils.cache import delete_template_fragment_cache from wger.utils.cache import cache_mapper @@ -119,3 +121,38 @@ def init_language_config(sender, instance, created, **kwargs): config.show = False config.save() post_save.connect(init_language_config, sender=Language) + + +class GymConfig(models.Model): + ''' + System wide configuration for gyms + + At the moment this only allows to set one gym as the default + ''' + + default_gym = models.ForeignKey(Gym, + verbose_name=_('Default gym'), + help_text=_('Select the default gym for this installation. ' + 'This will assign all new registered users to this ' + 'gym and update all existing users without a ' + 'gym.'), + null=True, + blank=True) + ''' + Default gym for the wger installation + ''' + + def __unicode__(self): + ''' + Return a more human-readable representation + ''' + return u"Default gym {0}".format(self.gym) + + def save(self, *args, **kwargs): + ''' + All users that have no gym set in the profile are edited + ''' + if self.default_gym: + UserProfile.objects.filter(gym=None).update(gym=self.default_gym) + + return super(GymConfig, self).save(*args, **kwargs) diff --git a/wger/config/tests/test_gym_config.py b/wger/config/tests/test_gym_config.py new file mode 100644 index 000000000..16f4657c4 --- /dev/null +++ b/wger/config/tests/test_gym_config.py @@ -0,0 +1,86 @@ +# -*- 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 +# along with Workout Manager. If not, see . + +from django.contrib.auth.models import User +from django.core.urlresolvers import reverse + +from wger.config.models import GymConfig +from wger.core.models import Gym, UserProfile +from wger.manager.tests.testcase import WorkoutManagerTestCase + + +class GymConfigTestCase(WorkoutManagerTestCase): + ''' + Test the system wide gym configuration + ''' + + def test_default_gym(self): + ''' + Test that newly registered users get a gym + ''' + + gym = Gym.objects.get(pk=2) + gym_config = GymConfig.objects.get(pk=1) + gym_config.default_gym = gym + gym_config.save() + + # Register + registration_data = {'username': 'myusername', + 'password1': 'secret', + 'password2': 'secret', + 'email': 'my.email@example.com', + 'recaptcha_response_field': 'PASSED', } + self.client.post(reverse('core:registration'), registration_data) + new_user = User.objects.get(pk=4) + + self.assertEqual(new_user.userprofile.gym, gym) + + def test_no_default_gym(self): + ''' + Test the user registration without a default gym + ''' + + gym = Gym.objects.get(pk=2) + gym_config = GymConfig.objects.get(pk=1) + gym_config.default_gym = None + gym_config.save() + + # Register + registration_data = {'username': 'myusername', + 'password1': 'secret', + 'password2': 'secret', + 'email': 'my.email@example.com', + 'recaptcha_response_field': 'PASSED', } + self.client.post(reverse('core:registration'), registration_data) + + new_user = User.objects.get(pk=4) + self.assertEqual(new_user.userprofile.gym_id, None) + + def test_update_userprofile(self): + ''' + Test setting the gym for users when setting a default gym + ''' + + UserProfile.objects.update(gym=None) + self.assertEqual(UserProfile.objects.exclude(gym=None).count(), 0) + + gym = Gym.objects.get(pk=2) + gym_config = GymConfig.objects.get(pk=1) + gym_config.default_gym = gym + gym_config.save() + + self.assertEqual(UserProfile.objects.filter(gym=gym).count(), 3) diff --git a/wger/config/urls.py b/wger/config/urls.py index bb9729e4f..7b892e975 100644 --- a/wger/config/urls.py +++ b/wger/config/urls.py @@ -19,6 +19,7 @@ from django.conf.urls import patterns, url from wger.config.views import languages from wger.config.views import language_config +from wger.config.views import gym_config urlpatterns = patterns('', @@ -40,8 +41,13 @@ urlpatterns = patterns('', languages.LanguageCreateView.as_view(), name='language-add'), - # Language configs - url(r'^language/config/(?P\d+)/edit', - language_config.LanguageConfigUpdateView.as_view(), - name='languageconfig-edit'), + # Language configs + url(r'^language-config/(?P\d+)/edit', + language_config.LanguageConfigUpdateView.as_view(), + name='languageconfig-edit'), + + # Gym config + url(r'^default-gym', + gym_config.GymConfigUpdateView.as_view(), + name='gymconfig-edit'), ) diff --git a/wger/config/views/gym_config.py b/wger/config/views/gym_config.py new file mode 100644 index 000000000..f11ab14a6 --- /dev/null +++ b/wger/config/views/gym_config.py @@ -0,0 +1,49 @@ +# -*- 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 as _ +from django.views.generic import UpdateView + +from wger.config.models import GymConfig +from wger.utils.generic_views import WgerFormMixin + + +logger = logging.getLogger('wger.custom') + + +class GymConfigUpdateView(WgerFormMixin, UpdateView): + ''' + Generic view to edit the gym config table + ''' + model = GymConfig + permission_required = 'config.change_gymconfig' + success_url = reverse_lazy('config:gymconfig-edit') + + def get_object(self): + ''' + Return the only gym config object + ''' + return GymConfig.objects.get(pk=1) + + def get_context_data(self, **kwargs): + context = super(GymConfigUpdateView, self).get_context_data(**kwargs) + context['form_action'] = reverse('config:gymconfig-edit') + context['title'] = _('Edit') + return context diff --git a/wger/core/templates/gym/list.html b/wger/core/templates/gym/list.html index 49f308438..4559c4aaf 100644 --- a/wger/core/templates/gym/list.html +++ b/wger/core/templates/gym/list.html @@ -36,7 +36,7 @@ {% block sidebar %} {% if perms.core.add_gym %} -

{% trans "Options" %}

+

{% trans "Options" %}

@@ -49,4 +49,19 @@

{% endif %} + +{% if perms.core.change_gymconfig %} +

{% trans "Default gym" %}

+

+ + {% trans 'Default gym configuration' %} + {% trans "Default gym configuration" %} + +

+{% endif %} {% endblock %} diff --git a/wger/core/views/user.py b/wger/core/views/user.py index 2cd90e3ea..569c53dba 100644 --- a/wger/core/views/user.py +++ b/wger/core/views/user.py @@ -42,6 +42,7 @@ from wger.core.forms import UserPreferencesForm, UserPersonalInformationForm from wger.core.forms import PasswordConfirmationForm from wger.core.forms import RegistrationForm from wger.core.forms import RegistrationFormNoCaptcha +from wger.config.models import GymConfig logger = logging.getLogger('wger.custom') @@ -168,9 +169,14 @@ def registration(request): password) user.save() - # Save the notification language + # Pre-set some values of the user's profile language = Language.objects.get(short_name=translation.get_language()) user.userprofile.notification_language = language + + gym_config = GymConfig.objects.get(pk=1) + if gym_config: + user.userprofile.gym = gym_config.default_gym + user.userprofile.save() user = authenticate(username=username, password=password) diff --git a/wger/manager/tests/testcase.py b/wger/manager/tests/testcase.py index 189b9675b..64ff167f1 100644 --- a/wger/manager/tests/testcase.py +++ b/wger/manager/tests/testcase.py @@ -51,6 +51,7 @@ class BaseTestCase(object): ''' fixtures = ('days_of_week', + 'gym-config', 'test-languages', 'test-licenses', 'test-gyms', diff --git a/wger/settings_global.py b/wger/settings_global.py index 83cb7f315..91d4b0201 100644 --- a/wger/settings_global.py +++ b/wger/settings_global.py @@ -348,3 +348,9 @@ REST_FRAMEWORK = { 'DEFAULT_FILTER_BACKENDS': ('rest_framework.filters.DjangoFilterBackend', 'rest_framework.filters.OrderingFilter',) } + + +# Configure south database migrations +SOUTH_MIGRATION_MODULES = { + 'easy_thumbnails': 'easy_thumbnails.south_migrations', +}