Initial formatting with ruff

This commit is contained in:
Roland Geider
2024-01-27 10:30:02 +01:00
parent bb0d989d92
commit 6fdeb96d64
360 changed files with 2844 additions and 3559 deletions

View File

@@ -16,6 +16,9 @@ jobs:
- name: Checkout code
uses: actions/checkout@v4
- name: Get dependencies
run: pip install ruff isort
- name: Format the code
run: ruff format

View File

@@ -62,11 +62,23 @@ filter_dump(('nutrition.logitem',), 'nutrition_diary.json')
filter_dump(('exercises.muscle',), 'muscles.json')
filter_dump(('exercises.exercisecategory',), 'categories.json')
filter_dump(('exercises.exerciseimage',), 'exercise-images.json')
filter_dump(('exercises.exercisebase', 'exercises.variation',), 'exercise-base-data.json')
filter_dump(
('exercises.exercise', 'exercises.exercisecomment', 'exercises.alias'),
'translations.json')
filter_dump(('exercises.equipment', 'exercises.equipment',), 'equipment.json')
(
'exercises.exercisebase',
'exercises.variation',
),
'exercise-base-data.json',
)
filter_dump(
('exercises.exercise', 'exercises.exercisecomment', 'exercises.alias'), 'translations.json'
)
filter_dump(
(
'exercises.equipment',
'exercises.equipment',
),
'equipment.json',
)
#
# Gym

View File

@@ -12,6 +12,7 @@ from wger.tasks import (
setup_django_environment,
)
if __name__ == '__main__':
# If user passed the settings flag ignore the default wger settings
if not any('--settings' in s for s in sys.argv):

View File

@@ -21,8 +21,8 @@ import os
from celery import Celery
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings")
app = Celery("wger")
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'settings')
app = Celery('wger')
# read config from Django settings, the CELERY namespace would make celery
# config keys has `CELERY` prefix

View File

@@ -20,7 +20,7 @@ from django.apps import AppConfig
class ConfigConfig(AppConfig):
name = 'wger.config'
verbose_name = "Config"
verbose_name = 'Config'
def ready(self):
import wger.config.signals

View File

@@ -4,7 +4,6 @@ from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('gym', '0001_initial'),
('core', '0001_initial'),
@@ -18,23 +17,22 @@ class Migration(migrations.Migration):
'id',
models.AutoField(
verbose_name='ID', serialize=False, auto_created=True, primary_key=True
)
),
),
(
'default_gym',
models.ForeignKey(
blank=True,
to='gym.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.',
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,
verbose_name='Default gym',
on_delete=models.CASCADE
)
on_delete=models.CASCADE,
),
),
],
options={},
bases=(models.Model, ),
bases=(models.Model,),
),
migrations.CreateModel(
name='LanguageConfig',
@@ -43,15 +41,15 @@ class Migration(migrations.Migration):
'id',
models.AutoField(
verbose_name='ID', serialize=False, auto_created=True, primary_key=True
)
),
),
(
'item',
models.CharField(
max_length=2,
editable=False,
choices=[(b'1', 'Exercises'), (b'2', 'Ingredients')]
)
choices=[(b'1', 'Exercises'), (b'2', 'Ingredients')],
),
),
('show', models.BooleanField(default=1)),
(
@@ -60,8 +58,8 @@ class Migration(migrations.Migration):
related_name='language_source',
editable=False,
to='core.Language',
on_delete=models.CASCADE
)
on_delete=models.CASCADE,
),
),
(
'language_target',
@@ -69,13 +67,13 @@ class Migration(migrations.Migration):
related_name='language_target',
editable=False,
to='core.Language',
on_delete=models.CASCADE
)
on_delete=models.CASCADE,
),
),
],
options={
'ordering': ['item', 'language_target'],
},
bases=(models.Model, ),
bases=(models.Model,),
),
]

View File

@@ -5,7 +5,6 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('config', '0001_initial'),
]
@@ -18,7 +17,9 @@ class Migration(migrations.Migration):
choices=[
('1', 'Exercises'),
('2', 'Ingredients'),
], editable=False, max_length=2
],
editable=False,
max_length=2,
),
),
]

View File

@@ -4,11 +4,12 @@ from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('config', '0002_auto_20190618_1617'),
]
operations = [
migrations.DeleteModel(name='LanguageConfig', ),
migrations.DeleteModel(
name='LanguageConfig',
),
]

View File

@@ -52,7 +52,7 @@ class GymConfig(models.Model):
),
null=True,
blank=True,
on_delete=models.CASCADE
on_delete=models.CASCADE,
)
"""
Default gym for the wger installation
@@ -62,14 +62,13 @@ class GymConfig(models.Model):
"""
Return a more human-readable representation
"""
return "Default gym {0}".format(self.default_gym)
return 'Default gym {0}'.format(self.default_gym)
def save(self, *args, **kwargs):
"""
Perform additional tasks
"""
if self.default_gym:
# All users that have no gym set in the profile are edited
UserProfile.objects.filter(gym=None).update(gym=self.default_gym)

View File

@@ -29,7 +29,6 @@ class GymNameHeaderTestCase(WgerTestCase):
"""
def check_header(self, gym=None):
response = self.client.get(reverse('core:dashboard'))
self.assertEqual(response.context['custom_header'], gym)

View File

@@ -34,6 +34,6 @@ patterns_gym_config = [
urlpatterns = [
path(
'gym-config/',
include((patterns_gym_config, 'gym_config'), namespace="gym_config"),
include((patterns_gym_config, 'gym_config'), namespace='gym_config'),
),
]

View File

@@ -34,6 +34,7 @@ class GymConfigUpdateView(WgerFormMixin, UpdateView):
"""
Generic view to edit the gym config table
"""
model = GymConfig
fields = ['default_gym']
permission_required = 'config.change_gymconfig'

View File

@@ -1,2 +1,2 @@
LANGUAGE_ENDPOINT = "language"
LICENSE_ENDPOINT = "license"
LANGUAGE_ENDPOINT = 'language'
LICENSE_ENDPOINT = 'license'

View File

@@ -46,9 +46,9 @@ class UserprofileSerializer(serializers.ModelSerializer):
Workout session serializer
"""
email = serializers.EmailField(source="user.email", read_only=True)
username = serializers.EmailField(source="user.username", read_only=True)
date_joined = serializers.EmailField(source="user.date_joined", read_only=True)
email = serializers.EmailField(source='user.email', read_only=True)
username = serializers.EmailField(source='user.username', read_only=True)
date_joined = serializers.EmailField(source='user.date_joined', read_only=True)
class Meta:
model = UserProfile
@@ -86,7 +86,8 @@ class UserprofileSerializer(serializers.ModelSerializer):
class UserLoginSerializer(serializers.ModelSerializer):
""" Serializer to map to User model in relation to api user"""
"""Serializer to map to User model in relation to api user"""
email = serializers.CharField(required=False)
username = serializers.CharField(required=False)
password = serializers.CharField(required=True, min_length=8)
@@ -102,9 +103,9 @@ class UserLoginSerializer(serializers.ModelSerializer):
super().__init__(instance, data, **kwargs)
def validate(self, data):
email = data.get("email", None)
username = data.get("username", None)
password = data.get("password", None)
email = data.get('email', None)
username = data.get('username', None)
password = data.get('password', None)
if email is None and username is None:
raise serializers.ValidationError('Please provide an "email" or a "username"')
@@ -122,9 +123,10 @@ class UserLoginSerializer(serializers.ModelSerializer):
class UserRegistrationSerializer(serializers.ModelSerializer):
email = serializers.EmailField(
required=False, validators=[
required=False,
validators=[
UniqueValidator(queryset=User.objects.all()),
]
],
)
username = serializers.CharField(
required=True,

View File

@@ -88,6 +88,7 @@ class UserProfileViewSet(viewsets.ModelViewSet):
returns the data for the currently logged-in user's profile. To update
the profile, use a POST request with the new data, not a PATCH.
"""
serializer_class = UserprofileSerializer
permission_classes = (
IsAuthenticated,
@@ -99,7 +100,7 @@ class UserProfileViewSet(viewsets.ModelViewSet):
Only allow access to appropriate objects
"""
# REST API generation
if getattr(self, "swagger_fake_view", False):
if getattr(self, 'swagger_fake_view', False):
return UserProfile.objects.none()
return UserProfile.objects.filter(user=self.request.user)
@@ -159,10 +160,7 @@ class UserProfileViewSet(viewsets.ModelViewSet):
send_email(request.user)
return Response(
{
'status': 'sent',
'message': f'A verification email was sent to {request.user.email}'
}
{'status': 'sent', 'message': f'A verification email was sent to {request.user.email}'}
)
@@ -170,7 +168,8 @@ class ApplicationVersionView(viewsets.ViewSet):
"""
Returns the application's version
"""
permission_classes = (AllowAny, )
permission_classes = (AllowAny,)
@staticmethod
@extend_schema(
@@ -187,7 +186,8 @@ class PermissionView(viewsets.ViewSet):
"""
Checks whether the user has a django permission
"""
permission_classes = (AllowAny, )
permission_classes = (AllowAny,)
@staticmethod
@extend_schema(
@@ -200,12 +200,13 @@ class PermissionView(viewsets.ViewSet):
),
],
responses={
201:
inline_serializer(name='PermissionResponse', fields={
'result': BooleanField(),
}),
400:
OpenApiResponse(
201: inline_serializer(
name='PermissionResponse',
fields={
'result': BooleanField(),
},
),
400: OpenApiResponse(
description="Please pass a permission name in the 'permission' parameter"
),
},
@@ -230,7 +231,8 @@ class RequiredApplicationVersionView(viewsets.ViewSet):
Returns the minimum required version of flutter app to access this server
such as 1.4.2 or 3.0.0
"""
permission_classes = (AllowAny, )
permission_classes = (AllowAny,)
@staticmethod
@extend_schema(
@@ -250,7 +252,8 @@ class UserAPILoginView(viewsets.ViewSet):
Note that it is recommended to use token authorization instead.
"""
permission_classes = (AllowAny, )
permission_classes = (AllowAny,)
queryset = User.objects.all()
serializer_class = UserLoginSerializer
throttle_scope = 'login'
@@ -261,12 +264,11 @@ class UserAPILoginView(viewsets.ViewSet):
@extend_schema(
parameters=[],
responses={
status.HTTP_200_OK:
inline_serializer(
status.HTTP_200_OK: inline_serializer(
name='loginSerializer',
fields={'token': CharField()},
),
}
},
)
def post(self, request):
serializer = self.serializer_class(data=request.data, request=request)
@@ -292,8 +294,8 @@ class UserAPILoginView(viewsets.ViewSet):
data={'token': token.key},
status=status.HTTP_200_OK,
headers={
"Deprecation": "Sat, 01 Oct 2022 23:59:59 GMT",
}
'Deprecation': 'Sat, 01 Oct 2022 23:59:59 GMT',
},
)
@@ -301,7 +303,8 @@ class UserAPIRegistrationViewSet(viewsets.ViewSet):
"""
API endpoint
"""
permission_classes = (AllowRegisterUser, )
permission_classes = (AllowRegisterUser,)
serializer_class = UserRegistrationSerializer
def get_queryset(self):
@@ -313,12 +316,11 @@ class UserAPIRegistrationViewSet(viewsets.ViewSet):
@extend_schema(
parameters=[],
responses={
status.HTTP_200_OK:
inline_serializer(
status.HTTP_200_OK: inline_serializer(
name='loginSerializer',
fields={'token': CharField()},
),
}
},
)
def post(self, request):
data = request.data
@@ -333,11 +335,8 @@ class UserAPIRegistrationViewSet(viewsets.ViewSet):
send_email(user)
return Response(
{
'message': 'api user successfully registered',
'token': token.key
},
status=status.HTTP_201_CREATED
{'message': 'api user successfully registered', 'token': token.key},
status=status.HTTP_201_CREATED,
)
@@ -345,6 +344,7 @@ class LanguageViewSet(viewsets.ReadOnlyModelViewSet):
"""
API endpoint for the languages used in the application
"""
queryset = Language.objects.all()
serializer_class = LanguageSerializer
ordering_fields = '__all__'
@@ -361,16 +361,18 @@ class DaysOfWeekViewSet(viewsets.ReadOnlyModelViewSet):
This has historical reasons, and it's better and easier to just define a simple enum
"""
queryset = DaysOfWeek.objects.all()
serializer_class = DaysOfWeekSerializer
ordering_fields = '__all__'
filterset_fields = ('day_of_week', )
filterset_fields = ('day_of_week',)
class LicenseViewSet(viewsets.ReadOnlyModelViewSet):
"""
API endpoint for license objects
"""
queryset = License.objects.all()
serializer_class = LicenseSerializer
ordering_fields = '__all__'
@@ -385,17 +387,19 @@ class RepetitionUnitViewSet(viewsets.ReadOnlyModelViewSet):
"""
API endpoint for repetition units objects
"""
queryset = RepetitionUnit.objects.all()
serializer_class = RepetitionUnitSerializer
ordering_fields = '__all__'
filterset_fields = ('name', )
filterset_fields = ('name',)
class RoutineWeightUnitViewSet(viewsets.ReadOnlyModelViewSet):
"""
API endpoint for weight units objects
"""
queryset = WeightUnit.objects.all()
serializer_class = RoutineWeightUnitSerializer
ordering_fields = '__all__'
filterset_fields = ('name', )
filterset_fields = ('name',)

View File

@@ -18,7 +18,7 @@ from django.apps import AppConfig
class CoreConfig(AppConfig):
name = 'wger.core'
verbose_name = "Core"
verbose_name = 'Core'
def ready(self):
import wger.core.signals

View File

@@ -122,7 +122,7 @@ def create_demo_entries(user):
workout=workout,
reps=reps,
weight=18 - reps + random.randint(1, 4),
date=datetime.date.today() - datetime.timedelta(weeks=i)
date=datetime.date.today() - datetime.timedelta(weeks=i),
)
weight_log.append(log)
@@ -142,7 +142,7 @@ def create_demo_entries(user):
workout=workout,
reps=reps,
weight=30 - reps + random.randint(1, 4),
date=datetime.date.today() - datetime.timedelta(weeks=i)
date=datetime.date.today() - datetime.timedelta(weeks=i),
)
weight_log.append(log)
@@ -162,7 +162,7 @@ def create_demo_entries(user):
workout=workout,
reps=reps,
weight=110 - reps + random.randint(1, 10),
date=datetime.date.today() - datetime.timedelta(weeks=i)
date=datetime.date.today() - datetime.timedelta(weeks=i),
)
weight_log.append(log)

View File

@@ -72,7 +72,7 @@ class UserLoginForm(AuthenticationForm):
Row(
Column('username', css_class='col-6'),
Column('password', css_class='col-6'),
css_class='form-row'
css_class='form-row',
)
)
@@ -89,8 +89,8 @@ class UserLoginForm(AuthenticationForm):
return self.cleaned_data
def authenticate(self, request):
username = self.cleaned_data.get("username")
password = self.cleaned_data.get("password")
username = self.cleaned_data.get('username')
password = self.cleaned_data.get('password')
if username and password:
self.user_cache = authenticate(
@@ -108,20 +108,20 @@ class UserPreferencesForm(forms.ModelForm):
first_name = forms.CharField(label=_('First name'), required=False)
last_name = forms.CharField(label=_('Last name'), required=False)
email = EmailField(
label=_("Email"),
help_text=_("Used for password resets and, optionally, e-mail reminders."),
required=False
label=_('Email'),
help_text=_('Used for password resets and, optionally, e-mail reminders.'),
required=False,
)
birthdate = forms.DateField(
label=_("Date of Birth"),
label=_('Date of Birth'),
required=False,
widget=forms.DateInput(
attrs={
'type': 'date',
"max": str(date.today().replace(year=date.today().year - 10)),
"min": str(date.today().replace(year=date.today().year - 100))
'max': str(date.today().replace(year=date.today().year - 10)),
'min': str(date.today().replace(year=date.today().year - 100)),
},
)
),
)
class Meta:
@@ -145,42 +145,46 @@ class UserPreferencesForm(forms.ModelForm):
self.helper.form_class = 'wger-form'
self.helper.layout = Layout(
Fieldset(
_("Personal data"), 'email',
_('Personal data'),
'email',
Row(
Column('first_name', css_class='col-6'),
Column('last_name', css_class='col-6'),
css_class='form-row'
), 'birthdate', HTML("<hr>")
css_class='form-row',
),
'birthdate',
HTML('<hr>'),
),
Fieldset(
_("Workout reminders"),
_('Workout reminders'),
'workout_reminder_active',
'workout_reminder',
'workout_duration',
HTML("<hr>"),
HTML('<hr>'),
),
Fieldset(
_("Other settings"),
"ro_access",
"notification_language",
"weight_unit",
"show_comments",
"show_english_ingredients",
"num_days_weight_reminder",
), ButtonHolder(Submit('submit', _("Save"), css_class='btn-success btn-block'))
_('Other settings'),
'ro_access',
'notification_language',
'weight_unit',
'show_comments',
'show_english_ingredients',
'num_days_weight_reminder',
),
ButtonHolder(Submit('submit', _('Save'), css_class='btn-success btn-block')),
)
class UserEmailForm(forms.ModelForm):
email = EmailField(
label=_("Email"),
help_text=_("Used for password resets and, optionally, email reminders."),
required=False
label=_('Email'),
help_text=_('Used for password resets and, optionally, email reminders.'),
required=False,
)
class Meta:
model = User
fields = ('email', )
fields = ('email',)
def clean_email(self):
"""
@@ -192,7 +196,7 @@ class UserEmailForm(forms.ModelForm):
we want to check that nobody else has that e-mail address.
"""
email = self.cleaned_data["email"]
email = self.cleaned_data['email']
if not email:
return email
try:
@@ -203,7 +207,7 @@ class UserEmailForm(forms.ModelForm):
except User.DoesNotExist:
return email
raise ValidationError(_("This e-mail address is already in use."))
raise ValidationError(_('This e-mail address is already in use.'))
class UserPersonalInformationForm(UserEmailForm):
@@ -222,10 +226,11 @@ class PasswordConfirmationForm(Form):
This can be used to make sure the user really wants to perform a dangerous
action. The form must be initialised with a user object.
"""
password = CharField(
label=_("Password"),
label=_('Password'),
widget=PasswordInput,
help_text=_('Please enter your current password.')
help_text=_('Please enter your current password.'),
)
def __init__(self, user, data=None):
@@ -234,7 +239,7 @@ class PasswordConfirmationForm(Form):
self.helper = FormHelper()
self.helper.layout = Layout(
'password',
ButtonHolder(Submit('submit', _("Delete"), css_class='btn-danger btn-block'))
ButtonHolder(Submit('submit', _('Delete'), css_class='btn-danger btn-block')),
)
def clean_password(self):
@@ -244,7 +249,7 @@ class PasswordConfirmationForm(Form):
password = self.cleaned_data.get('password', None)
if not self.user.check_password(password):
raise ValidationError(_('Invalid password'))
return self.cleaned_data.get("password")
return self.cleaned_data.get('password')
class RegistrationForm(UserCreationForm, UserEmailForm):
@@ -263,13 +268,15 @@ class RegistrationForm(UserCreationForm, UserEmailForm):
self.helper = FormHelper()
self.helper.form_class = 'wger-form'
self.helper.layout = Layout(
'username', 'email',
'username',
'email',
Row(
Column('password1', css_class='col-md-6 col-12'),
Column('password2', css_class='col-md-6 col-12'),
css_class='form-row'
), 'captcha',
ButtonHolder(Submit('submitBtn', _("Register"), css_class='btn-success btn-block'))
css_class='form-row',
),
'captcha',
ButtonHolder(Submit('submitBtn', _('Register'), css_class='btn-success btn-block')),
)
@@ -283,16 +290,17 @@ class RegistrationFormNoCaptcha(UserCreationForm, UserEmailForm):
self.helper = FormHelper()
self.helper.form_class = 'wger-form'
self.helper.layout = Layout(
'username', 'email',
'username',
'email',
Row(
Column('password1', css_class='col-md-6 col-12'),
Column('password2', css_class='col-md-6 col-12'),
css_class='form-row'
css_class='form-row',
),
ButtonHolder(
Submit('submit', _("Register"), css_class='btn-success col-sm-6 col-12'),
css_class='text-center'
)
Submit('submit', _('Register'), css_class='btn-success col-sm-6 col-12'),
css_class='text-center',
),
)
@@ -300,12 +308,13 @@ class FeedbackRegisteredForm(forms.Form):
"""
Feedback form used for logged-in users
"""
contact = forms.CharField(
max_length=50,
min_length=10,
label=_('Contact'),
help_text=_('Some way of answering you (e-mail, etc.)'),
required=False
required=False,
)
comment = forms.CharField(
@@ -314,7 +323,7 @@ class FeedbackRegisteredForm(forms.Form):
widget=widgets.Textarea,
label=_('Comment'),
help_text=_('What do you want to say?'),
required=True
required=True,
)
@@ -322,6 +331,7 @@ class FeedbackAnonymousForm(FeedbackRegisteredForm):
"""
Feedback form used for anonymous users (has additionally a reCAPTCHA field)
"""
captcha = ReCaptchaField(
widget=ReCaptchaV3,
label='reCaptcha',

View File

@@ -9,7 +9,6 @@ Custom command permitting users to create user accounts
class Command(BaseCommand):
help = 'Permit user to create user accounts'
# Named (optional arguments)
@@ -32,15 +31,15 @@ class Command(BaseCommand):
if options['disable']:
user.userprofile.can_add_user = False
self.stdout.write(
self.style.
SUCCESS(f"{options['name']} is now DISABLED from adding users via the API")
self.style.SUCCESS(
f"{options['name']} is now DISABLED from adding users via the API"
)
)
else:
user.userprofile.can_add_user = True
self.stdout.write(
self.style.
SUCCESS(f"{options['name']} is now ALLOWED to add users via the API")
self.style.SUCCESS(f"{options['name']} is now ALLOWED to add users via the API")
)
user.userprofile.save()

View File

@@ -36,17 +36,18 @@ class Command(BaseCommand):
Clears caches (HTML, etc.)
"""
help = 'Clears the application cache. You *must* pass an option selecting ' \
'what exactly you want to clear. See available options.'
help = (
'Clears the application cache. You *must* pass an option selecting '
'what exactly you want to clear. See available options.'
)
def add_arguments(self, parser):
parser.add_argument(
'--clear-template',
action='store_true',
dest='clear_template',
default=False,
help='Clear only template caches'
help='Clear only template caches',
)
parser.add_argument(
@@ -54,7 +55,7 @@ class Command(BaseCommand):
action='store_true',
dest='clear_workout',
default=False,
help='Clear only the workout canonical view'
help='Clear only the workout canonical view',
)
parser.add_argument(
@@ -62,7 +63,7 @@ class Command(BaseCommand):
action='store_true',
dest='clear_all',
default=False,
help='Clear ALL cached entries'
help='Clear ALL cached entries',
)
def handle(self, **options):
@@ -71,7 +72,8 @@ class Command(BaseCommand):
"""
if (
not options['clear_template'] and not options['clear_workout']
not options['clear_template']
and not options['clear_workout']
and not options['clear_all']
):
raise CommandError('Please select what cache you need to delete, see help')
@@ -79,20 +81,20 @@ class Command(BaseCommand):
# Exercises, cached template fragments
if options['clear_template']:
if int(options['verbosity']) >= 2:
self.stdout.write("*** Clearing templates")
self.stdout.write('*** Clearing templates')
for user in User.objects.all():
if int(options['verbosity']) >= 2:
self.stdout.write(f"* Processing user {user.username}")
self.stdout.write(f'* Processing user {user.username}')
for entry in WorkoutLog.objects.filter(user=user).dates('date', 'year'):
if int(options['verbosity']) >= 3:
self.stdout.write(f" Year {entry.year}")
for month in WorkoutLog.objects.filter(user=user, date__year=entry.year
).dates('date', 'month'):
self.stdout.write(f' Year {entry.year}')
for month in WorkoutLog.objects.filter(user=user, date__year=entry.year).dates(
'date', 'month'
):
if int(options['verbosity']) >= 3:
self.stdout.write(f" Month {entry.month}")
self.stdout.write(f' Month {entry.month}')
reset_workout_log(user.id, entry.year, entry.month)
for day in WorkoutLog.objects.filter(
user=user,
@@ -100,7 +102,7 @@ class Command(BaseCommand):
date__month=month.month,
).dates('date', 'day'):
if int(options['verbosity']) >= 3:
self.stdout.write(f" Day {day.day}")
self.stdout.write(f' Day {day.day}')
reset_workout_log(user.id, entry.year, entry.month, day)
# Workout canonical form

View File

@@ -31,14 +31,13 @@ class Command(BaseCommand):
help = 'Deletes all temporary users older than 1 week'
def handle(self, **options):
profile_list = UserProfile.objects.filter(is_temporary=True)
counter = 0
for profile in profile_list:
delta = now() - profile.user.date_joined
if (delta >= datetime.timedelta(7)):
if delta >= datetime.timedelta(7):
counter += 1
profile.user.delete()
self.stdout.write(f"Deleted {counter} temporary users")
self.stdout.write(f'Deleted {counter} temporary users')

View File

@@ -44,14 +44,13 @@ class Command(BaseCommand):
help = 'Dummy generator for users'
def add_arguments(self, parser):
parser.add_argument(
'--nr-entries',
action='store',
default=20,
dest='number_users',
type=int,
help='The number of users to generate (default: 20)'
help='The number of users to generate (default: 20)',
)
parser.add_argument(
@@ -60,11 +59,10 @@ class Command(BaseCommand):
default='auto',
dest='add_to_gym',
type=str,
help='Gym to assign the users to. Allowed values: auto, none, <gym_id>. Default: auto'
help='Gym to assign the users to. Allowed values: auto, none, <gym_id>. Default: auto',
)
def handle(self, **options):
faker = Faker()
self.stdout.write(f"** Generating {options['number_users']} users")

View File

@@ -21,8 +21,8 @@ from wger.exercises.models import Exercise
class Command(BaseCommand):
help = (
"Helper command to extract translations for the exercises used in the flutter repo for "
"the screenshots for the play store"
'Helper command to extract translations for the exercises used in the flutter repo for '
'the screenshots for the play store'
)
uuids = {
@@ -31,7 +31,7 @@ class Command(BaseCommand):
'deadLift': 'ee8e8db4-2d82-49e1-ab7f-891e9a354934',
'crunches': 'b186f1f8-4957-44dc-bf30-d0b00064ce6f',
'curls': '1ae6a28d-10e7-4ecf-af4f-905f8193e2c6',
'raises': '63375f5b-2d81-471c-bea4-fc3d207e96cb'
'raises': '63375f5b-2d81-471c-bea4-fc3d207e96cb',
}
def handle(self, **options):
@@ -39,7 +39,7 @@ class Command(BaseCommand):
languages = []
for exercise_key in self.uuids.keys():
self.stdout.write(f"Extracting translations for {exercise_key}")
self.stdout.write(f'Extracting translations for {exercise_key}')
uuid = self.uuids[exercise_key]
translations = Exercise.objects.filter(exercise_base__uuid=uuid)
@@ -47,14 +47,14 @@ class Command(BaseCommand):
variables = []
for translation in translations:
variable_name = f"{exercise_key}{translation.language.short_name.upper()}"
variable_name = f'{exercise_key}{translation.language.short_name.upper()}'
variables.append(variable_name)
if translation.language not in languages:
languages.append(translation.language)
self.stdout.write(
f"- translation {translation.language.short_name}: {translation.name}"
f'- translation {translation.language.short_name}: {translation.name}'
)
out.append(
@@ -70,22 +70,23 @@ class Command(BaseCommand):
);
"""
)
self.stdout.write("")
self.stdout.write('')
out.append(f'final {exercise_key}Translations = [{",".join(variables)}];')
for language in languages:
out.insert(
0, f"""const tLanguage{language.id} = Language(
0,
f"""const tLanguage{language.id} = Language(
id: {language.id},
shortName: '{language.short_name}',
fullName: '{language.full_name}',
);"""
);""",
)
out.insert(0, "import 'package:wger/models/exercises/language.dart';")
out.insert(0, "import 'package:wger/models/exercises/translation.dart';")
out.insert(0, "// Autogenerated by extract-i18n-flutter-exercises.py do not edit!")
out.insert(0, '// Autogenerated by extract-i18n-flutter-exercises.py do not edit!')
#
# Write to output

View File

@@ -38,22 +38,25 @@ class Command(BaseCommand):
help = 'Write the translatable strings from the database to a file'
def handle(self, **options):
# Replace whitespace and other problematic characters with underscores
def cleanup_name(text: str) -> str:
return text.lower(). \
replace(' ', '_'). \
replace('-', '_'). \
replace('/', '_'). \
replace('(', '_'). \
replace(')', '_')
return (
text.lower()
.replace(' ', '_')
.replace('-', '_')
.replace('/', '_')
.replace('(', '_')
.replace(')', '_')
)
# Collect all translatable items
data = [i for i in ExerciseCategory.objects.all()] \
+ [i for i in Equipment.objects.all()] \
+ [i.name_en for i in Muscle.objects.all() if i.name_en] \
+ [i for i in RepetitionUnit.objects.all()] \
+ [i for i in WeightUnit.objects.all()]
data = (
[i for i in ExerciseCategory.objects.all()]
+ [i for i in Equipment.objects.all()]
+ [i.name_en for i in Muscle.objects.all() if i.name_en]
+ [i for i in RepetitionUnit.objects.all()]
+ [i for i in WeightUnit.objects.all()]
)
# Make entries unique and sort alphabetically
data = sorted(set([i.__str__() for i in data]))
@@ -70,7 +73,7 @@ class Command(BaseCommand):
#
# React - copy the file to src/i18n.tsx in the React repo
with open('wger/i18n.tsx', 'w') as f:
out = '''
out = """
// This code is autogenerated in the backend repo in extract-i18n.py do not edit!
// Translate dynamic strings that are returned from the server
@@ -82,13 +85,13 @@ class Command(BaseCommand):
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const DummyComponent = () => {
const [t] = useTranslation();'''
const [t] = useTranslation();"""
for i in data:
out += f't("server.{cleanup_name(i.__str__())}");\n'
out += '''
out += """
return (<p></p>);
};'''
};"""
f.write(out)
self.stdout.write(self.style.SUCCESS(f'Wrote content to wger/i18n.tsx'))
@@ -106,7 +109,7 @@ class Command(BaseCommand):
# Copy to lib/helpers/i18n.dart in the flutter repo
with open('wger/i18n.dart', 'w') as f:
out = '''
out = """
/// This code is autogenerated in the backend repo in extract-i18n.py do not edit!
/// Translate dynamic strings that are returned from the server
@@ -121,19 +124,19 @@ class Command(BaseCommand):
import 'package:logging/logging.dart';
String getTranslation(String value, BuildContext context) {
switch (value) {'''
switch (value) {"""
for i in data:
out += f'''
out += f"""
case '{i}':
return AppLocalizations.of(context).{cleanup_name(i.__str__())};
'''
"""
out += '''
out += """
default:
log('Could not translate the server string $value', level: Level.WARNING.value);
return value;
}
}'''
}"""
f.write(out)
self.stdout.write(self.style.SUCCESS('Wrote content to wger/i18n.dart'))

View File

@@ -11,13 +11,11 @@ List users registered via the API
class Command(BaseCommand):
help = 'List all users registered via REST, grouped by app-user'
def handle(self, *args, **options):
for app_profile in UserProfile.objects.filter(can_add_user=True):
self.stdout.write(self.style.SUCCESS(f"Users created by {app_profile.user.username}:"))
self.stdout.write(self.style.SUCCESS(f'Users created by {app_profile.user.username}:'))
for user_profile in UserProfile.objects.filter(added_by=app_profile.user):
self.stdout.write(f"- {user_profile.user.username}")
self.stdout.write(f'- {user_profile.user.username}')

View File

@@ -35,4 +35,4 @@ class Command(BaseCommand):
site.domain = domain
site.name = settings.SITE_URL
site.save()
self.stdout.write(self.style.SUCCESS(f"Set site URL to {domain}"))
self.stdout.write(self.style.SUCCESS(f'Set site URL to {domain}'))

View File

@@ -25,9 +25,11 @@ class Command(BaseCommand):
Updates the user cache table
"""
help = 'Update the user cache-table. This is only needed when the python' \
'code used to calculate any of the cached entries is changed and ' \
'the ones in the database need to be updated to reflect the new logic.'
help = (
'Update the user cache-table. This is only needed when the python'
'code used to calculate any of the cached entries is changed and '
'the ones in the database need to be updated to reflect the new logic.'
)
def handle(self, **options):
"""

View File

@@ -6,7 +6,6 @@ import django.core.validators
class Migration(migrations.Migration):
dependencies = [
('gym', '0001_initial'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
@@ -20,14 +19,14 @@ class Migration(migrations.Migration):
'id',
models.AutoField(
verbose_name='ID', serialize=False, auto_created=True, primary_key=True
)
),
),
('day_of_week', models.CharField(max_length=9, verbose_name='Day of the week')),
],
options={
'ordering': ['pk'],
},
bases=(models.Model, ),
bases=(models.Model,),
),
migrations.CreateModel(
name='Language',
@@ -36,7 +35,7 @@ class Migration(migrations.Migration):
'id',
models.AutoField(
verbose_name='ID', serialize=False, auto_created=True, primary_key=True
)
),
),
('short_name', models.CharField(max_length=2, verbose_name='Language short name')),
('full_name', models.CharField(max_length=30, verbose_name='Language full name')),
@@ -44,7 +43,7 @@ class Migration(migrations.Migration):
options={
'ordering': ['full_name'],
},
bases=(models.Model, ),
bases=(models.Model,),
),
migrations.CreateModel(
name='License',
@@ -53,12 +52,12 @@ class Migration(migrations.Migration):
'id',
models.AutoField(
verbose_name='ID', serialize=False, auto_created=True, primary_key=True
)
),
),
('full_name', models.CharField(max_length=60, verbose_name='Full name')),
(
'short_name',
models.CharField(max_length=15, verbose_name='Short name, e.g. CC-BY-SA 3')
models.CharField(max_length=15, verbose_name='Short name, e.g. CC-BY-SA 3'),
),
(
'url',
@@ -66,14 +65,14 @@ class Migration(migrations.Migration):
help_text='Link to license text or other information',
null=True,
verbose_name='Link',
blank=True
)
blank=True,
),
),
],
options={
'ordering': ['full_name'],
},
bases=(models.Model, ),
bases=(models.Model,),
),
migrations.CreateModel(
name='UserProfile',
@@ -82,7 +81,7 @@ class Migration(migrations.Migration):
'id',
models.AutoField(
verbose_name='ID', serialize=False, auto_created=True, primary_key=True
)
),
),
('is_temporary', models.BooleanField(default=False, editable=False)),
(
@@ -90,52 +89,48 @@ class Migration(migrations.Migration):
models.BooleanField(
default=True,
help_text='Check to show exercise comments on the workout view',
verbose_name='Show exercise comments'
)
verbose_name='Show exercise comments',
),
),
(
'show_english_ingredients',
models.BooleanField(
default=True,
help_text=
'Check to also show ingredients in English while creating\na nutritional plan. These ingredients are extracted from a list provided\nby the US Department of Agriculture. It is extremely complete, with around\n7000 entries, but can be somewhat overwhelming and make the search difficult.',
verbose_name='Also use ingredients in English'
)
help_text='Check to also show ingredients in English while creating\na nutritional plan. These ingredients are extracted from a list provided\nby the US Department of Agriculture. It is extremely complete, with around\n7000 entries, but can be somewhat overwhelming and make the search difficult.',
verbose_name='Also use ingredients in English',
),
),
(
'workout_reminder_active',
models.BooleanField(
default=False,
help_text=
'Check to activate automatic reminders for workouts. You need to provide a valid email for this to work.',
verbose_name='Activate workout reminders'
)
help_text='Check to activate automatic reminders for workouts. You need to provide a valid email for this to work.',
verbose_name='Activate workout reminders',
),
),
(
'workout_reminder',
models.IntegerField(
default=14,
help_text=
'The number of days you want to be reminded before a workout expires.',
help_text='The number of days you want to be reminded before a workout expires.',
verbose_name='Remind before expiration',
validators=[
django.core.validators.MinValueValidator(1),
django.core.validators.MaxValueValidator(30)
]
)
django.core.validators.MaxValueValidator(30),
],
),
),
(
'workout_duration',
models.IntegerField(
default=12,
help_text=
'Default duration in weeks of workouts not in a schedule. Used for email workout reminders.',
help_text='Default duration in weeks of workouts not in a schedule. Used for email workout reminders.',
verbose_name='Default duration of workouts',
validators=[
django.core.validators.MinValueValidator(1),
django.core.validators.MaxValueValidator(30)
]
)
django.core.validators.MaxValueValidator(30),
],
),
),
('last_workout_notification', models.DateField(null=True, editable=False)),
(
@@ -143,21 +138,20 @@ class Migration(migrations.Migration):
models.BooleanField(
default=True,
help_text='Check to activate timer pauses between exercises.',
verbose_name='Use pauses in workout timer'
)
verbose_name='Use pauses in workout timer',
),
),
(
'timer_pause',
models.IntegerField(
default=90,
help_text=
'Default duration in seconds of pauses used by the timer in the gym mode.',
help_text='Default duration in seconds of pauses used by the timer in the gym mode.',
verbose_name='Default duration of workout pauses',
validators=[
django.core.validators.MinValueValidator(10),
django.core.validators.MaxValueValidator(400)
]
)
django.core.validators.MaxValueValidator(400),
],
),
),
(
'age',
@@ -167,9 +161,9 @@ class Migration(migrations.Migration):
verbose_name='Age',
validators=[
django.core.validators.MinValueValidator(10),
django.core.validators.MaxValueValidator(100)
]
)
django.core.validators.MaxValueValidator(100),
],
),
),
(
'height',
@@ -179,9 +173,9 @@ class Migration(migrations.Migration):
verbose_name='Height (cm)',
validators=[
django.core.validators.MinValueValidator(140),
django.core.validators.MaxValueValidator(230)
]
)
django.core.validators.MaxValueValidator(230),
],
),
),
(
'gender',
@@ -189,8 +183,8 @@ class Migration(migrations.Migration):
default=b'1',
max_length=1,
null=True,
choices=[(b'1', 'Male'), (b'2', 'Female')]
)
choices=[(b'1', 'Male'), (b'2', 'Female')],
),
),
(
'sleep_hours',
@@ -201,9 +195,9 @@ class Migration(migrations.Migration):
verbose_name='Hours of sleep',
validators=[
django.core.validators.MinValueValidator(4),
django.core.validators.MaxValueValidator(10)
]
)
django.core.validators.MaxValueValidator(10),
],
),
),
(
'work_hours',
@@ -214,9 +208,9 @@ class Migration(migrations.Migration):
verbose_name='Work',
validators=[
django.core.validators.MinValueValidator(1),
django.core.validators.MaxValueValidator(15)
]
)
django.core.validators.MaxValueValidator(15),
],
),
),
(
'work_intensity',
@@ -226,8 +220,8 @@ class Migration(migrations.Migration):
max_length=1,
help_text='Approximately',
null=True,
verbose_name='Physical intensity'
)
verbose_name='Physical intensity',
),
),
(
'sport_hours',
@@ -238,9 +232,9 @@ class Migration(migrations.Migration):
verbose_name='Sport',
validators=[
django.core.validators.MinValueValidator(1),
django.core.validators.MaxValueValidator(30)
]
)
django.core.validators.MaxValueValidator(30),
],
),
),
(
'sport_intensity',
@@ -250,8 +244,8 @@ class Migration(migrations.Migration):
max_length=1,
help_text='Approximately',
null=True,
verbose_name='Physical intensity'
)
verbose_name='Physical intensity',
),
),
(
'freetime_hours',
@@ -262,9 +256,9 @@ class Migration(migrations.Migration):
verbose_name='Free time',
validators=[
django.core.validators.MinValueValidator(1),
django.core.validators.MaxValueValidator(15)
]
)
django.core.validators.MaxValueValidator(15),
],
),
),
(
'freetime_intensity',
@@ -274,8 +268,8 @@ class Migration(migrations.Migration):
max_length=1,
help_text='Approximately',
null=True,
verbose_name='Physical intensity'
)
verbose_name='Physical intensity',
),
),
(
'calories',
@@ -286,9 +280,9 @@ class Migration(migrations.Migration):
verbose_name='Total daily calories',
validators=[
django.core.validators.MinValueValidator(1500),
django.core.validators.MaxValueValidator(5000)
]
)
django.core.validators.MaxValueValidator(5000),
],
),
),
(
'weight_unit',
@@ -296,8 +290,8 @@ class Migration(migrations.Migration):
default=b'kg',
max_length=2,
verbose_name='Weight unit',
choices=[(b'kg', 'Metric (kilogram)'), (b'lb', 'Imperial (pound)')]
)
choices=[(b'kg', 'Metric (kilogram)'), (b'lb', 'Imperial (pound)')],
),
),
(
'gym',
@@ -306,8 +300,8 @@ class Migration(migrations.Migration):
editable=False,
to='gym.Gym',
null=True,
on_delete=models.CASCADE
)
on_delete=models.CASCADE,
),
),
(
'notification_language',
@@ -315,19 +309,18 @@ class Migration(migrations.Migration):
default=2,
verbose_name='Notification language',
to='core.Language',
help_text=
'Language to use when sending you email notifications, e.g. email reminders for workouts. This does not affect the language used on the website.',
on_delete=models.CASCADE
)
help_text='Language to use when sending you email notifications, e.g. email reminders for workouts. This does not affect the language used on the website.',
on_delete=models.CASCADE,
),
),
(
'user',
models.OneToOneField(
editable=False, to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE
)
),
),
],
options={},
bases=(models.Model, ),
bases=(models.Model,),
),
]

View File

@@ -4,7 +4,6 @@ from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('core', '0001_initial'),
]
@@ -14,10 +13,9 @@ class Migration(migrations.Migration):
model_name='userprofile',
name='ro_access',
field=models.BooleanField(
help_text=
'Allow anonymous users to access your workouts and logs in a read-only mode. You can then share the links to social media.',
help_text='Allow anonymous users to access your workouts and logs in a read-only mode. You can then share the links to social media.',
verbose_name='Allow read-only access',
default=False
default=False,
),
preserve_default=True,
),
@@ -30,7 +28,7 @@ class Migration(migrations.Migration):
max_length=1,
help_text='Approximately',
default='1',
verbose_name='Physical intensity'
verbose_name='Physical intensity',
),
preserve_default=True,
),
@@ -51,7 +49,7 @@ class Migration(migrations.Migration):
max_length=1,
help_text='Approximately',
default='2',
verbose_name='Physical intensity'
verbose_name='Physical intensity',
),
preserve_default=True,
),
@@ -62,7 +60,7 @@ class Migration(migrations.Migration):
choices=[('kg', 'Metric (kilogram)'), ('lb', 'Imperial (pound)')],
verbose_name='Weight unit',
max_length=2,
default='kg'
default='kg',
),
preserve_default=True,
),
@@ -75,7 +73,7 @@ class Migration(migrations.Migration):
max_length=1,
help_text='Approximately',
default='1',
verbose_name='Physical intensity'
verbose_name='Physical intensity',
),
preserve_default=True,
),

View File

@@ -5,7 +5,6 @@ import django.core.validators
class Migration(migrations.Migration):
dependencies = [
('core', '0002_auto_20141225_1512'),
]
@@ -18,7 +17,7 @@ class Migration(migrations.Migration):
verbose_name='Automatic reminders for weight entries',
max_length=30,
null=True,
help_text='Number of days after the last weight entry (enter 0 to deactivate)'
help_text='Number of days after the last weight entry (enter 0 to deactivate)',
),
preserve_default=True,
),
@@ -30,8 +29,8 @@ class Migration(migrations.Migration):
null=True,
validators=[
django.core.validators.MinValueValidator(10),
django.core.validators.MaxValueValidator(100)
]
django.core.validators.MaxValueValidator(100),
],
),
preserve_default=True,
),
@@ -43,8 +42,8 @@ class Migration(migrations.Migration):
null=True,
validators=[
django.core.validators.MinValueValidator(140),
django.core.validators.MaxValueValidator(230)
]
django.core.validators.MaxValueValidator(230),
],
),
preserve_default=True,
),
@@ -54,8 +53,7 @@ class Migration(migrations.Migration):
field=models.BooleanField(
verbose_name='Allow external access',
default=False,
help_text=
'Allow external users to access your workouts and logs in a read-only mode. You need to set this before you can share links e.g. to social media.'
help_text='Allow external users to access your workouts and logs in a read-only mode. You need to set this before you can share links e.g. to social media.',
),
preserve_default=True,
),

View File

@@ -4,7 +4,6 @@ from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('core', '0003_auto_20150217_1554'),
]
@@ -17,7 +16,7 @@ class Migration(migrations.Migration):
default=0,
verbose_name='Automatic reminders for weight entries',
max_length=30,
help_text='Number of days after the last weight entry (enter 0 to deactivate)'
help_text='Number of days after the last weight entry (enter 0 to deactivate)',
),
preserve_default=True,
),

View File

@@ -6,7 +6,6 @@ from django.conf import settings
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('core', '0004_auto_20150217_1914'),
@@ -20,14 +19,14 @@ class Migration(migrations.Migration):
'id',
models.AutoField(
auto_created=True, primary_key=True, serialize=False, verbose_name='ID'
)
),
),
('last_activity', models.DateField(null=True)),
(
'user',
models.OneToOneField(
editable=False, to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE
)
),
),
],
),
@@ -40,8 +39,8 @@ class Migration(migrations.Migration):
help_text='Number of days after the last weight entry (enter 0 to deactivate)',
validators=[
django.core.validators.MinValueValidator(0),
django.core.validators.MaxValueValidator(30)
]
django.core.validators.MaxValueValidator(30),
],
),
),
]

View File

@@ -7,13 +7,12 @@ def create_usercache(apps, schema_editor):
"""
Creates a usercache table for all users
"""
User = apps.get_model("auth", "User")
Usercache = apps.get_model("core", "Usercache")
WorkoutLog = apps.get_model("manager", "WorkoutLog")
WorkoutSession = apps.get_model("manager", "WorkoutSession")
User = apps.get_model('auth', 'User')
Usercache = apps.get_model('core', 'Usercache')
WorkoutLog = apps.get_model('manager', 'WorkoutLog')
WorkoutSession = apps.get_model('manager', 'WorkoutSession')
for user in User.objects.all():
#
# This is the logic of get_user_last_activity at the time this migration
# was created.
@@ -46,13 +45,12 @@ def delete_usercache(apps, schema_editor):
"""
Deletes the usercache table for all users
"""
Usercache = apps.get_model("core", "Usercache")
Usercache = apps.get_model('core', 'Usercache')
for cache in Usercache.objects.all():
cache.delete()
class Migration(migrations.Migration):
dependencies = [
('core', '0005_auto_20151025_2236'),
('auth', '0006_require_contenttypes_0002'),

View File

@@ -4,7 +4,6 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0006_auto_20151025_2237'),
]
@@ -17,7 +16,7 @@ class Migration(migrations.Migration):
'id',
models.AutoField(
verbose_name='ID', serialize=False, primary_key=True, auto_created=True
)
),
),
('name', models.CharField(verbose_name='Name', max_length=100)),
],

View File

@@ -4,7 +4,6 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0007_repetitionunit'),
]
@@ -17,7 +16,7 @@ class Migration(migrations.Migration):
'id',
models.AutoField(
verbose_name='ID', serialize=False, auto_created=True, primary_key=True
)
),
),
('name', models.CharField(verbose_name='Name', max_length=100)),
],

View File

@@ -28,7 +28,6 @@ def insert_data(apps, schema_editor):
class Migration(migrations.Migration):
dependencies = [
('core', '0008_weightunit'),
]

View File

@@ -6,7 +6,6 @@ import wger.core.models
class Migration(migrations.Migration):
dependencies = [
('core', '0009_auto_20160303_2340'),
]
@@ -18,17 +17,16 @@ class Migration(migrations.Migration):
field=models.DateField(
null=True,
validators=[wger.core.models.profile.birthdate_validator],
verbose_name='Date of Birth'
verbose_name='Date of Birth',
),
),
migrations.AlterField(
model_name='license',
name='full_name',
field=models.CharField(
help_text=
'If a license has been localized, e.g. the Creative Commons licenses for the different countries, add them as separate entries here.',
help_text='If a license has been localized, e.g. the Creative Commons licenses for the different countries, add them as separate entries here.',
max_length=60,
verbose_name='Full name'
verbose_name='Full name',
),
),
]

View File

@@ -5,7 +5,6 @@ import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('gym', '0008_auto_20190618_1617'),
('core', '0010_auto_20170403_0144'),
@@ -20,7 +19,7 @@ class Migration(migrations.Migration):
editable=False,
null=True,
on_delete=django.db.models.deletion.SET_NULL,
to='gym.gym'
to='gym.gym',
),
),
]

View File

@@ -6,7 +6,6 @@ import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('core', '0011_auto_20201201_0653'),
@@ -21,7 +20,7 @@ class Migration(migrations.Migration):
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name='added_by',
to=settings.AUTH_USER_MODEL
to=settings.AUTH_USER_MODEL,
),
),
migrations.AddField(

View File

@@ -4,7 +4,6 @@ from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('core', '0012_auto_20210210_1228'),
]

View File

@@ -4,7 +4,6 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0012_auto_20210210_1228'),
]

View File

@@ -4,7 +4,6 @@ from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('core', '0013_auto_20210726_1729'),
('core', '0013_userprofile_email_verified'),

View File

@@ -4,7 +4,6 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0014_merge_20210818_1735'),
]

View File

@@ -4,7 +4,6 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0015_alter_language_short_name'),
]
@@ -14,10 +13,7 @@ class Migration(migrations.Migration):
model_name='language',
name='short_name',
field=models.CharField(
help_text='ISO 639-1',
max_length=2,
unique=True,
verbose_name='Language short name'
help_text='ISO 639-1', max_length=2, unique=True, verbose_name='Language short name'
),
),
]

View File

@@ -41,4 +41,4 @@ class UserCache(models.Model):
"""
Return a more human-readable representation
"""
return f"Cache for user {self.user}"
return f'Cache for user {self.user}'

View File

@@ -32,8 +32,9 @@ class DaysOfWeek(models.Model):
"""
Order by day-ID, this is needed for some DBs
"""
ordering = [
"pk",
'pk',
]
def __str__(self):

View File

@@ -43,8 +43,9 @@ class Language(models.Model):
"""
Set Meta options
"""
ordering = [
"full_name",
'full_name',
]
#
@@ -54,7 +55,7 @@ class Language(models.Model):
"""
Return a more human-readable representation
"""
return f"{self.full_name} ({self.short_name})"
return f'{self.full_name} ({self.short_name})'
def get_absolute_url(self):
"""

View File

@@ -31,7 +31,7 @@ class License(models.Model):
'If a license has been localized, e.g. the Creative '
'Commons licenses for the different countries, add '
'them as separate entries here.'
)
),
)
"""Full name"""
@@ -45,7 +45,7 @@ class License(models.Model):
verbose_name=_('Link'),
help_text=_('Link to license text or other information'),
blank=True,
null=True
null=True,
)
"""URL to full license text or other information"""
@@ -53,8 +53,9 @@ class License(models.Model):
"""
Set Meta options
"""
ordering = [
"full_name",
'full_name',
]
#
@@ -64,7 +65,7 @@ class License(models.Model):
"""
Return a more human-readable representation
"""
return f"{self.full_name} ({self.short_name})"
return f'{self.full_name} ({self.short_name})'
#
# Own methods

View File

@@ -116,8 +116,7 @@ class UserProfile(models.Model):
show_comments = models.BooleanField(
verbose_name=_('Show exercise comments'),
help_text=_('Check to show exercise comments on the '
'workout view'),
help_text=_('Check to show exercise comments on the ' 'workout view'),
default=True,
)
"""
@@ -134,7 +133,7 @@ a nutritional plan. These ingredients are extracted from a list provided
by the US Department of Agriculture. It is extremely complete, with around
7000 entries, but can be somewhat overwhelming and make the search difficult."""
),
default=True
default=True,
)
workout_reminder_active = models.BooleanField(
@@ -145,15 +144,14 @@ by the US Department of Agriculture. It is extremely complete, with around
'to provide a valid email for this '
'to work.'
),
default=False
default=False,
)
workout_reminder = IntegerField(
verbose_name=_('Remind before expiration'),
help_text=_('The number of days you want to be reminded '
'before a workout expires.'),
help_text=_('The number of days you want to be reminded ' 'before a workout expires.'),
default=14,
validators=[MinValueValidator(1), MaxValueValidator(30)]
validators=[MinValueValidator(1), MaxValueValidator(30)],
)
workout_duration = IntegerField(
verbose_name=_('Default duration of workouts'),
@@ -163,7 +161,7 @@ by the US Department of Agriculture. It is extremely complete, with around
'reminders.'
),
default=12,
validators=[MinValueValidator(1), MaxValueValidator(30)]
validators=[MinValueValidator(1), MaxValueValidator(30)],
)
last_workout_notification = models.DateField(editable=False, blank=False, null=True)
"""
@@ -183,7 +181,7 @@ by the US Department of Agriculture. It is extremely complete, with around
'language used on the website.'
),
default=2,
on_delete=models.CASCADE
on_delete=models.CASCADE,
)
@property
@@ -216,7 +214,7 @@ by the US Department of Agriculture. It is extremely complete, with around
verbose_name=_('Age'),
blank=False,
null=True,
validators=[MinValueValidator(10), MaxValueValidator(100)]
validators=[MinValueValidator(10), MaxValueValidator(100)],
)
"""The user's age"""
@@ -232,7 +230,7 @@ by the US Department of Agriculture. It is extremely complete, with around
verbose_name=_('Height (cm)'),
blank=False,
validators=[MinValueValidator(140), MaxValueValidator(230)],
null=True
null=True,
)
"""The user's height"""
@@ -251,7 +249,7 @@ by the US Department of Agriculture. It is extremely complete, with around
default=7,
blank=False,
null=True,
validators=[MinValueValidator(4), MaxValueValidator(10)]
validators=[MinValueValidator(4), MaxValueValidator(10)],
)
"""The average hours of sleep per day"""
@@ -261,7 +259,7 @@ by the US Department of Agriculture. It is extremely complete, with around
default=8,
blank=False,
null=True,
validators=[MinValueValidator(1), MaxValueValidator(15)]
validators=[MinValueValidator(1), MaxValueValidator(15)],
)
"""The average hours at work per day"""
@@ -272,7 +270,7 @@ by the US Department of Agriculture. It is extremely complete, with around
choices=INTENSITY,
default=INTENSITY_LOW,
blank=False,
null=True
null=True,
)
"""Physical intensity of work"""
@@ -282,7 +280,7 @@ by the US Department of Agriculture. It is extremely complete, with around
default=3,
blank=False,
null=True,
validators=[MinValueValidator(1), MaxValueValidator(30)]
validators=[MinValueValidator(1), MaxValueValidator(30)],
)
"""The average hours performing sports per week"""
@@ -293,7 +291,7 @@ by the US Department of Agriculture. It is extremely complete, with around
choices=INTENSITY,
default=INTENSITY_MEDIUM,
blank=False,
null=True
null=True,
)
"""Physical intensity of sport activities"""
@@ -303,7 +301,7 @@ by the US Department of Agriculture. It is extremely complete, with around
default=8,
blank=False,
null=True,
validators=[MinValueValidator(1), MaxValueValidator(15)]
validators=[MinValueValidator(1), MaxValueValidator(15)],
)
"""The average hours of free time per day"""
@@ -314,7 +312,7 @@ by the US Department of Agriculture. It is extremely complete, with around
choices=INTENSITY,
default=INTENSITY_LOW,
blank=False,
null=True
null=True,
)
"""Physical intensity during free time"""
@@ -324,7 +322,7 @@ by the US Department of Agriculture. It is extremely complete, with around
default=2500,
blank=False,
null=True,
validators=[MinValueValidator(1500), MaxValueValidator(5000)]
validators=[MinValueValidator(1500), MaxValueValidator(5000)],
)
"""Basic caloric intake based on physical activity"""
@@ -346,18 +344,15 @@ by the US Department of Agriculture. It is extremely complete, with around
'logs in a read-only mode. You need to set this '
'before you can share links e.g. to social media.'
),
default=False
default=False,
)
"""Allow anonymous read-only access"""
num_days_weight_reminder = models.IntegerField(
verbose_name=_('Automatic reminders for weight '
'entries'),
help_text=_('Number of days after the last '
'weight entry (enter 0 to '
'deactivate)'),
verbose_name=_('Automatic reminders for weight ' 'entries'),
help_text=_('Number of days after the last ' 'weight entry (enter 0 to ' 'deactivate)'),
validators=[MinValueValidator(0), MaxValueValidator(30)],
default=0
default=0,
)
"""Number of Days for email weight reminder"""
@@ -415,17 +410,16 @@ by the US Department of Agriculture. It is extremely complete, with around
"""
Make sure the total amount of hours is 24
"""
if (
(self.sleep_hours and self.freetime_hours and self.work_hours)
and (self.sleep_hours + self.freetime_hours + self.work_hours) > 24
):
if (self.sleep_hours and self.freetime_hours and self.work_hours) and (
self.sleep_hours + self.freetime_hours + self.work_hours
) > 24:
raise ValidationError(_('The sum of all hours has to be 24'))
def __str__(self):
"""
Return a more human-readable representation
"""
return f"Profile for user {self.user}"
return f'Profile for user {self.user}'
@property
def use_metric(self):
@@ -521,11 +515,9 @@ by the US Department of Agriculture. It is extremely complete, with around
"""
Create a new weight entry as needed
"""
if (
not WeightEntry.objects.filter(user=self.user).exists() or (
datetime.date.today() - WeightEntry.objects.filter(user=self.user).latest().date
> datetime.timedelta(days=3)
)
if not WeightEntry.objects.filter(user=self.user).exists() or (
datetime.date.today() - WeightEntry.objects.filter(user=self.user).latest().date
> datetime.timedelta(days=3)
):
entry = WeightEntry()
entry.weight = weight

View File

@@ -28,8 +28,9 @@ class RepetitionUnit(models.Model):
"""
Set Meta options
"""
ordering = [
"name",
'name',
]
name = models.CharField(max_length=100, verbose_name=_('Name'))

View File

@@ -28,8 +28,9 @@ class WeightUnit(models.Model):
"""
Set Meta options
"""
ordering = [
"name",
'name',
]
name = models.CharField(max_length=100, verbose_name=_('Name'))

View File

@@ -61,8 +61,7 @@ def set_user_age(sender, instance, **kwargs):
today = datetime.date.today()
birthday = instance.birthdate
instance.age = (
today.year - birthday.year -
((today.month, today.day) < (birthday.month, birthday.day))
today.year - birthday.year - ((today.month, today.day) < (birthday.month, birthday.day))
)

View File

@@ -72,7 +72,6 @@ def pagination(paginator, page):
# we muck around here to remove the pages not inmediately 'around' the current
# one, otherwise we end up with a useless block with 300 pages.
if paginator.num_pages > PAGINATION_MAX_TOTAL_PAGES:
start_page = page.number - PAGINATION_PAGES_AROUND_CURRENT
for i in range(page.number - PAGINATION_PAGES_AROUND_CURRENT, page.number + 1):
if i > 0:
@@ -111,7 +110,7 @@ def render_muscles(muscles=None, muscles_sec=None):
"""
Renders the given muscles
"""
out = {"backgrounds": []}
out = {'backgrounds': []}
if not muscles and not muscles_sec:
return out
@@ -124,13 +123,15 @@ def render_muscles(muscles=None, muscles_sec=None):
out_secondary = muscles_sec if isinstance(muscles_sec, Iterable) else [muscles_sec]
if out_main:
front_back = "front" if out_main[0].is_front else "back"
front_back = 'front' if out_main[0].is_front else 'back'
else:
front_back = "front" if out_secondary[0].is_front else "back"
front_back = 'front' if out_secondary[0].is_front else 'back'
out['backgrounds'] = [i.image_url_main for i in out_main] \
+ [i.image_url_secondary for i in out_secondary] \
+ [static(f"images/muscles/muscular_system_{front_back}.svg")]
out['backgrounds'] = (
[i.image_url_main for i in out_main]
+ [i.image_url_secondary for i in out_secondary]
+ [static(f'images/muscles/muscular_system_{front_back}.svg')]
)
return out
@@ -203,12 +204,12 @@ def trans_weight_unit(unit, user=None):
if unit == 'kg':
return _('kg')
if unit == 'g':
return pgettext("weight unit, i.e. grams", "g")
return pgettext('weight unit, i.e. grams', 'g')
else:
if unit == 'kg':
return _('lb')
if unit == 'g':
return pgettext("weight unit, i.e. ounces", "oz")
return pgettext('weight unit, i.e. ounces', 'oz')
@register.filter

View File

@@ -231,7 +231,7 @@ class ApiPostTestCase:
response = self.client.post(self.url_detail, data=self.data)
self.assertIn(
response.status_code,
(status.HTTP_405_METHOD_NOT_ALLOWED, status.HTTP_403_FORBIDDEN)
(status.HTTP_405_METHOD_NOT_ALLOWED, status.HTTP_403_FORBIDDEN),
)
# Authorized user (owner)
@@ -239,7 +239,7 @@ class ApiPostTestCase:
response = self.client.post(self.url_detail, data=self.data)
self.assertIn(
response.status_code,
(status.HTTP_405_METHOD_NOT_ALLOWED, status.HTTP_403_FORBIDDEN)
(status.HTTP_405_METHOD_NOT_ALLOWED, status.HTTP_403_FORBIDDEN),
)
# Different logged in user
@@ -247,7 +247,7 @@ class ApiPostTestCase:
response = self.client.post(self.url_detail, data=self.data)
self.assertIn(
response.status_code,
(status.HTTP_405_METHOD_NOT_ALLOWED, status.HTTP_403_FORBIDDEN)
(status.HTTP_405_METHOD_NOT_ALLOWED, status.HTTP_403_FORBIDDEN),
)
def test_post(self):
@@ -279,7 +279,7 @@ class ApiPostTestCase:
response = self.client.post(self.url, data=self.data)
self.assertIn(
response.status_code,
(status.HTTP_405_METHOD_NOT_ALLOWED, status.HTTP_403_FORBIDDEN)
(status.HTTP_405_METHOD_NOT_ALLOWED, status.HTTP_403_FORBIDDEN),
)
# Logged in user
@@ -287,7 +287,7 @@ class ApiPostTestCase:
response = self.client.post(self.url, data=self.data)
self.assertIn(
response.status_code,
(status.HTTP_405_METHOD_NOT_ALLOWED, status.HTTP_403_FORBIDDEN)
(status.HTTP_405_METHOD_NOT_ALLOWED, status.HTTP_403_FORBIDDEN),
)
# Different logged in user
@@ -298,7 +298,7 @@ class ApiPostTestCase:
else:
self.assertIn(
response.status_code,
(status.HTTP_405_METHOD_NOT_ALLOWED, status.HTTP_403_FORBIDDEN)
(status.HTTP_405_METHOD_NOT_ALLOWED, status.HTTP_403_FORBIDDEN),
)
def test_post_special_endpoints(self):
@@ -362,7 +362,7 @@ class ApiPatchTestCase:
response = self.client.patch(self.url_detail, data=self.data)
self.assertIn(
response.status_code,
(status.HTTP_405_METHOD_NOT_ALLOWED, status.HTTP_403_FORBIDDEN)
(status.HTTP_405_METHOD_NOT_ALLOWED, status.HTTP_403_FORBIDDEN),
)
# Authorized user (owner)
@@ -370,7 +370,7 @@ class ApiPatchTestCase:
response = self.client.patch(self.url_detail, data=self.data)
self.assertIn(
response.status_code,
(status.HTTP_405_METHOD_NOT_ALLOWED, status.HTTP_403_FORBIDDEN)
(status.HTTP_405_METHOD_NOT_ALLOWED, status.HTTP_403_FORBIDDEN),
)
# Different logged in user
@@ -381,7 +381,7 @@ class ApiPatchTestCase:
else:
self.assertIn(
response.status_code,
(status.HTTP_405_METHOD_NOT_ALLOWED, status.HTTP_403_FORBIDDEN)
(status.HTTP_405_METHOD_NOT_ALLOWED, status.HTTP_403_FORBIDDEN),
)
def test_patch(self):
@@ -398,7 +398,7 @@ class ApiPatchTestCase:
response = self.client.patch(self.url, data=self.data)
self.assertIn(
response.status_code,
(status.HTTP_405_METHOD_NOT_ALLOWED, status.HTTP_403_FORBIDDEN)
(status.HTTP_405_METHOD_NOT_ALLOWED, status.HTTP_403_FORBIDDEN),
)
# Logged in user
@@ -488,7 +488,7 @@ class ApiPutTestCase:
response = self.client.put(self.url_detail, data=self.data)
self.assertIn(
response.status_code,
(status.HTTP_405_METHOD_NOT_ALLOWED, status.HTTP_403_FORBIDDEN)
(status.HTTP_405_METHOD_NOT_ALLOWED, status.HTTP_403_FORBIDDEN),
)
# Authorized user (owner)
@@ -496,7 +496,7 @@ class ApiPutTestCase:
response = self.client.put(self.url_detail, data=self.data)
self.assertIn(
response.status_code,
(status.HTTP_405_METHOD_NOT_ALLOWED, status.HTTP_403_FORBIDDEN)
(status.HTTP_405_METHOD_NOT_ALLOWED, status.HTTP_403_FORBIDDEN),
)
# Different logged in user
@@ -507,7 +507,7 @@ class ApiPutTestCase:
else:
self.assertIn(
response.status_code,
(status.HTTP_405_METHOD_NOT_ALLOWED, status.HTTP_403_FORBIDDEN)
(status.HTTP_405_METHOD_NOT_ALLOWED, status.HTTP_403_FORBIDDEN),
)
def test_put(self):
@@ -525,7 +525,7 @@ class ApiPutTestCase:
response = self.client.put(self.url, data=self.data)
self.assertIn(
response.status_code,
(status.HTTP_405_METHOD_NOT_ALLOWED, status.HTTP_403_FORBIDDEN)
(status.HTTP_405_METHOD_NOT_ALLOWED, status.HTTP_403_FORBIDDEN),
)
# Authorized user (owner)
@@ -601,7 +601,7 @@ class ApiDeleteTestCase:
response = self.client.delete(self.url_detail)
self.assertIn(
response.status_code,
(status.HTTP_405_METHOD_NOT_ALLOWED, status.HTTP_403_FORBIDDEN)
(status.HTTP_405_METHOD_NOT_ALLOWED, status.HTTP_403_FORBIDDEN),
)
# Logged in user
@@ -609,7 +609,7 @@ class ApiDeleteTestCase:
response = self.client.delete(self.url_detail)
self.assertIn(
response.status_code,
(status.HTTP_405_METHOD_NOT_ALLOWED, status.HTTP_403_FORBIDDEN)
(status.HTTP_405_METHOD_NOT_ALLOWED, status.HTTP_403_FORBIDDEN),
)
# Different logged in user
@@ -617,7 +617,7 @@ class ApiDeleteTestCase:
response = self.client.delete(self.url_detail)
self.assertIn(
response.status_code,
(status.HTTP_405_METHOD_NOT_ALLOWED, status.HTTP_403_FORBIDDEN)
(status.HTTP_405_METHOD_NOT_ALLOWED, status.HTTP_403_FORBIDDEN),
)
def test_delete(self):
@@ -643,7 +643,7 @@ class ApiDeleteTestCase:
response = self.client.delete(self.url)
self.assertIn(
response.status_code,
(status.HTTP_405_METHOD_NOT_ALLOWED, status.HTTP_403_FORBIDDEN)
(status.HTTP_405_METHOD_NOT_ALLOWED, status.HTTP_403_FORBIDDEN),
)
# Authorized user (owner)
@@ -651,7 +651,7 @@ class ApiDeleteTestCase:
response = self.client.delete(self.url)
self.assertIn(
response.status_code,
(status.HTTP_405_METHOD_NOT_ALLOWED, status.HTTP_403_FORBIDDEN)
(status.HTTP_405_METHOD_NOT_ALLOWED, status.HTTP_403_FORBIDDEN),
)
# Different logged in user
@@ -659,7 +659,7 @@ class ApiDeleteTestCase:
response = self.client.delete(self.url)
self.assertIn(
response.status_code,
(status.HTTP_405_METHOD_NOT_ALLOWED, status.HTTP_403_FORBIDDEN)
(status.HTTP_405_METHOD_NOT_ALLOWED, status.HTTP_403_FORBIDDEN),
)
def test_delete_special_endpoints(self):
@@ -700,6 +700,7 @@ class ApiBaseResourceTestCase(
All logic happens in the Api*TestCase classes
"""
pass

View File

@@ -185,15 +185,15 @@ class BaseTestCase:
shutil.copy(
'wger/exercises/tests/protestschwein.jpg',
self.media_root + '/exercise-images/1/protestschwein.jpg'
self.media_root + '/exercise-images/1/protestschwein.jpg',
)
shutil.copy(
'wger/exercises/tests/wildschwein.jpg',
self.media_root + '/exercise-images/1/wildschwein.jpg'
self.media_root + '/exercise-images/1/wildschwein.jpg',
)
shutil.copy(
'wger/exercises/tests/wildschwein.jpg',
self.media_root + '/exercise-images/2/wildschwein.jpg'
self.media_root + '/exercise-images/2/wildschwein.jpg',
)
@@ -253,7 +253,6 @@ class WgerTestCase(BaseTestCase, TestCase):
# Uploaded image or file, compare the filename
elif current_field_class in ('ImageFieldFile', 'FieldFile'):
# We can only compare the extensions, since the names can be changed
# Ideally we would check that the byte length is the same
self.assertEqual(pathlib.Path(field.name).suffix, pathlib.Path(value.name).suffix)
@@ -564,7 +563,6 @@ class WgerAccessTestCase(WgerTestCase):
anonymous_fail = True
def access(self, fail=True):
# Only perform the checks on derived classes
if self.__class__.__name__ == 'WgerAccessTestCase':
return

View File

@@ -32,7 +32,6 @@ class ChangePasswordTestCase(WgerTestCase):
"""
def change_password(self, fail=True):
# Fetch the change passwort page
response = self.client.get(reverse('core:user:change-password'))

View File

@@ -28,13 +28,14 @@ class DaysOfWeekRepresentationTestCase(WgerTestCase):
"""
Test that the representation of an object is correct
"""
self.assertEqual(f"{DaysOfWeek.objects.get(pk=1)}", 'Monday')
self.assertEqual(f'{DaysOfWeek.objects.get(pk=1)}', 'Monday')
class DaysOfWeekApiTestCase(api_base_test.ApiBaseResourceTestCase):
"""
Tests the days of week resource
"""
pk = 1
resource = DaysOfWeek
private_resource = False

View File

@@ -86,8 +86,9 @@ class DeleteUserByAdminTestCase(WgerTestCase):
self.assertEqual(User.objects.filter(username='test').count(), 1)
if fail:
self.assertIn(
response.status_code, (302, 403),
f'Unexpected status code for user {self.current_user}'
response.status_code,
(302, 403),
f'Unexpected status code for user {self.current_user}',
)
else:
self.assertEqual(

View File

@@ -55,10 +55,11 @@ class FeedbackTestCase(WgerTestCase):
# Correctly filled in reCaptcha
response = self.client.post(
reverse('core:feedback'), {
reverse('core:feedback'),
{
'comment': 'A very long and interesting comment',
'g-recaptcha-response': 'PASSED'
}
'g-recaptcha-response': 'PASSED',
},
)
self.assertEqual(response.status_code, 302)
@@ -82,7 +83,7 @@ class FeedbackTestCase(WgerTestCase):
self.user_login('test')
self.send_feedback()
@skip("Failing due to recaptcha issues")
@skip('Failing due to recaptcha issues')
def test_send_feedback_logged_out(self):
"""
Tests the feedback form as a logged out user

View File

@@ -22,7 +22,6 @@ from wger.gym.models import Gym
class UserGeneratorTestCase(WgerTestCase):
def test_generator(self):
# Arrange
User.objects.all().delete()

View File

@@ -60,11 +60,14 @@ class DashboardTestCase(WgerTestCase):
#
# 2. Add a weight entry
#
self.client.post(reverse('weight:add'), {
'weight': 100,
'date': '2012-01-01',
'user': 1,
})
self.client.post(
reverse('weight:add'),
{
'weight': 100,
'date': '2012-01-01',
'user': 1,
},
)
response = self.client.get(reverse('core:dashboard'))
self.assertEqual(response.status_code, 200)

View File

@@ -39,7 +39,7 @@ class LanguageRepresentationTestCase(WgerTestCase):
"""
Test that the representation of an object is correct
"""
self.assertEqual(f"{Language.objects.get(pk=1)}", 'Deutsch (de)')
self.assertEqual(f'{Language.objects.get(pk=1)}', 'Deutsch (de)')
class LanguageOverviewTest(WgerAccessTestCase):
@@ -95,6 +95,7 @@ class LanguageApiTestCase(api_base_test.ApiBaseResourceTestCase):
"""
Tests the language overview resource
"""
pk = 1
resource = Language
private_resource = False

View File

@@ -35,7 +35,7 @@ class LicenseRepresentationTestCase(WgerTestCase):
Test that the representation of an object is correct
"""
self.assertEqual(
f"{License.objects.get(pk=1)}",
f'{License.objects.get(pk=1)}',
'A cool and free license - Germany (ACAFL - DE)',
)
@@ -83,6 +83,7 @@ class LicenseApiTestCase(api_base_test.ApiBaseResourceTestCase):
"""
Tests the license resource
"""
pk = 1
resource = License
private_resource = False

View File

@@ -22,7 +22,6 @@ from wger.core.tests.base_testcase import BaseTestCase
class CheckPermissionApiTestCase(BaseTestCase, ApiBaseTestCase):
url = '/api/v2/check-permission/'
error_message = "Please pass a permission name in the 'permission' parameter"

View File

@@ -51,7 +51,8 @@ class PreferencesTestCase(WgerTestCase):
# Change some preferences
response = self.client.post(
reverse('core:user:preferences'), {
reverse('core:user:preferences'),
{
'show_comments': True,
'show_english_ingredients': True,
'email': 'my-new-email@example.com',
@@ -62,7 +63,7 @@ class PreferencesTestCase(WgerTestCase):
'num_days_weight_reminder': 10,
'weight_unit': 'kg',
'birthdate': '02/25/1987',
}
},
)
self.assertEqual(response.status_code, 302)
@@ -76,7 +77,8 @@ class PreferencesTestCase(WgerTestCase):
# Change some preferences
response = self.client.post(
reverse('core:user:preferences'), {
reverse('core:user:preferences'),
{
'show_comments': False,
'show_english_ingredients': True,
'email': '',
@@ -87,7 +89,7 @@ class PreferencesTestCase(WgerTestCase):
'num_days_weight_reminder': 10,
'weight_unit': 'lb',
'birthdate': '02/25/1987',
}
},
)
self.assertEqual(response.status_code, 302)
@@ -105,23 +107,25 @@ class PreferencesTestCase(WgerTestCase):
# Member2 has a contract
user = User.objects.get(username='member2')
self.assertEqual(
user.userprofile.address, {
user.userprofile.address,
{
'phone': '01234-567890',
'zip_code': '00000',
'street': 'Gassenstr. 14',
'city': 'The City',
}
},
)
# Test has no contracts
user = User.objects.get(username='test')
self.assertEqual(
user.userprofile.address, {
user.userprofile.address,
{
'phone': '',
'zip_code': '',
'street': '',
'city': '',
}
},
)
@@ -246,8 +250,8 @@ class PreferencesCalculationsTestCase(WgerTestCase):
bmi = user.userprofile.calculate_bmi()
self.assertEqual(
bmi,
user.userprofile.weight.quantize(TWOPLACES) /
decimal.Decimal(1.80 * 1.80).quantize(TWOPLACES)
user.userprofile.weight.quantize(TWOPLACES)
/ decimal.Decimal(1.80 * 1.80).quantize(TWOPLACES),
)
def test_basal_metabolic_rate(self):
@@ -263,7 +267,7 @@ class PreferencesCalculationsTestCase(WgerTestCase):
self.assertEqual(bmr, 1860)
# Female
user.userprofile.gender = "2"
user.userprofile.gender = '2'
bmr = user.userprofile.calculate_basal_metabolic_rate()
self.assertEqual(bmr, 1694)
@@ -281,29 +285,25 @@ class PreferencesCalculationsTestCase(WgerTestCase):
user = User.objects.get(pk=2)
self.assertEqual(
user.userprofile.calculate_activities(),
decimal.Decimal(1.57).quantize(TWOPLACES)
user.userprofile.calculate_activities(), decimal.Decimal(1.57).quantize(TWOPLACES)
)
# Gender has no influence
user.userprofile.gender = "2"
user.userprofile.gender = '2'
self.assertEqual(
user.userprofile.calculate_activities(),
decimal.Decimal(1.57).quantize(TWOPLACES)
user.userprofile.calculate_activities(), decimal.Decimal(1.57).quantize(TWOPLACES)
)
# Change some of the parameters
user.userprofile.work_intensity = '3'
self.assertEqual(
user.userprofile.calculate_activities(),
decimal.Decimal(1.80).quantize(TWOPLACES)
user.userprofile.calculate_activities(), decimal.Decimal(1.80).quantize(TWOPLACES)
)
user.userprofile.work_intensity = '2'
user.userprofile.sport_intensity = '2'
self.assertEqual(
user.userprofile.calculate_activities(),
decimal.Decimal(1.52).quantize(TWOPLACES)
user.userprofile.calculate_activities(), decimal.Decimal(1.52).quantize(TWOPLACES)
)

View File

@@ -134,23 +134,23 @@ class RegistrationTestCase(WgerTestCase):
self.user_logout()
# First password is missing
registration_data['password1'] = ""
registration_data['password1'] = ''
response = self.client.post(reverse('core:user:registration'), registration_data)
self.assertFalse(response.context['form'].is_valid())
self.user_logout()
# Second password is missing
registration_data['password2'] = ""
registration_data['password2'] = ''
response = self.client.post(reverse('core:user:registration'), registration_data)
self.assertFalse(response.context['form'].is_valid())
self.user_logout()
# Username is too long
long_user = (
"my_username_is_"
"wayyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy"
"_toooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo"
"_loooooooooooooooooooooooooooooooooooooooooooooooooooooooooong"
'my_username_is_'
'wayyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy'
'_toooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo'
'_loooooooooooooooooooooooooooooooooooooooooooooooooooooooooong'
)
registration_data['username'] = long_user
response = self.client.post(reverse('core:user:registration'), registration_data)
@@ -158,13 +158,13 @@ class RegistrationTestCase(WgerTestCase):
self.user_logout()
# Username contains invalid symbol
registration_data['username'] = "username!"
registration_data['username'] = 'username!'
response = self.client.post(reverse('core:user:registration'), registration_data)
self.assertFalse(response.context['form'].is_valid())
self.user_logout()
# Username is missing
registration_data['username'] = ""
registration_data['username'] = ''
response = self.client.post(reverse('core:user:registration'), registration_data)
self.assertFalse(response.context['form'].is_valid())
self.user_logout()

View File

@@ -34,7 +34,7 @@ class RepresentationTestCase(WgerTestCase):
"""
Test that the representation of an object is correct
"""
self.assertEqual(f"{RepetitionUnit.objects.get(pk=1)}", 'Repetitions')
self.assertEqual(f'{RepetitionUnit.objects.get(pk=1)}', 'Repetitions')
class OverviewTest(WgerAccessTestCase):
@@ -54,7 +54,7 @@ class AddTestCase(WgerAddTestCase):
object_class = RepetitionUnit
url = 'core:repetition-unit:add'
data = {'name': 'Furlongs'}
user_success = 'admin',
user_success = ('admin',)
user_fail = (
'general_manager1',
'general_manager2',
@@ -75,7 +75,7 @@ class DeleteTestCase(WgerDeleteTestCase):
pk = 1
object_class = RepetitionUnit
url = 'core:repetition-unit:delete'
user_success = 'admin',
user_success = ('admin',)
user_fail = (
'general_manager1',
'general_manager2',
@@ -97,7 +97,7 @@ class EditTestCase(WgerEditTestCase):
object_class = RepetitionUnit
url = 'core:repetition-unit:edit'
data = {'name': 'Furlongs'}
user_success = 'admin',
user_success = ('admin',)
user_fail = (
'general_manager1',
'general_manager2',
@@ -114,6 +114,7 @@ class ApiTestCase(api_base_test.ApiBaseResourceTestCase):
"""
Tests the unit resource
"""
pk = 1
resource = RepetitionUnit
private_resource = False

View File

@@ -26,7 +26,6 @@ class RobotsTxtTestCase(WgerTestCase):
"""
def test_robots(self):
response = self.client.get(reverse('robots'))
for lang in Language.objects.all():
self.assertTrue(f'wger.de/{lang.short_name}/sitemap.xml' in str(response.content))

View File

@@ -25,13 +25,11 @@ class SitemapTestCase(WgerTestCase):
"""
def test_sitemap_index(self):
response = self.client.get(reverse('sitemap'))
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.context['sitemaps']), 2)
def test_sitemap_exercises(self):
response = self.client.get(
reverse('django.contrib.sitemaps.views.sitemap', kwargs={'section': 'exercises'})
)
@@ -39,7 +37,6 @@ class SitemapTestCase(WgerTestCase):
self.assertEqual(len(response.context['urlset']), 11)
def test_sitemap_ingredients(self):
response = self.client.get(
reverse('django.contrib.sitemaps.views.sitemap', kwargs={'section': 'nutrition'})
)

View File

@@ -37,7 +37,6 @@ def _build_mock_user(gym_name, is_trainer=False):
@mock.patch('wger.core.views.user.django_login')
class TrainerLoginTestCase(WgerTestCase):
def test_trainer_is_allowed_to_login_to_non_trainer_in_same_gym(self, _):
request_user = _build_mock_user('same-gym', is_trainer=True)
request = _build_mock_request(request_user)
@@ -92,10 +91,7 @@ class UserApiLoginApiTestCase(BaseTestCase, ApiBaseTestCase):
def test_login_username_success(self):
response = self.client.post(
self.url,
{
'username': 'admin',
'password': 'adminadmin'
},
{'username': 'admin', 'password': 'adminadmin'},
)
result = response.data
@@ -109,16 +105,13 @@ class UserApiLoginApiTestCase(BaseTestCase, ApiBaseTestCase):
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertEqual(
result['non_field_errors'],
[ErrorDetail(string='Username or password unknown', code='invalid')]
[ErrorDetail(string='Username or password unknown', code='invalid')],
)
def test_login_email_success(self):
response = self.client.post(
self.url,
{
'email': 'admin@example.com',
'password': 'adminadmin'
},
{'email': 'admin@example.com', 'password': 'adminadmin'},
)
result = response.data
@@ -127,17 +120,14 @@ class UserApiLoginApiTestCase(BaseTestCase, ApiBaseTestCase):
def test_login_email_fail(self):
response = self.client.post(
self.url, {
'email': 'admin@example.com',
'password': 'adminadmin123'
}
self.url, {'email': 'admin@example.com', 'password': 'adminadmin123'}
)
result = response.data
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertEqual(
result['non_field_errors'],
[ErrorDetail(string='Username or password unknown', code='invalid')]
[ErrorDetail(string='Username or password unknown', code='invalid')],
)
def test_no_parameters(self):
@@ -147,5 +137,5 @@ class UserApiLoginApiTestCase(BaseTestCase, ApiBaseTestCase):
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertEqual(
result['non_field_errors'],
[ErrorDetail(string='Please provide an "email" or a "username"', code='invalid')]
[ErrorDetail(string='Please provide an "email" or a "username"', code='invalid')],
)

View File

@@ -19,7 +19,6 @@ from wger.core.tests.base_testcase import WgerTestCase
class CreateUserCommand(WgerTestCase):
def setUp(self):
super(CreateUserCommand, self).setUp()
self.out = StringIO()
@@ -68,20 +67,17 @@ class CreateUserCommand(WgerTestCase):
count_before = User.objects.count()
response = self.client.post(
reverse('api_register'), {
'username': 'restapi',
'email': 'abc@cde.fg',
'password': 'AekaiLe0ga'
},
Authorization=f'Token {token.key}'
reverse('api_register'),
{'username': 'restapi', 'email': 'abc@cde.fg', 'password': 'AekaiLe0ga'},
Authorization=f'Token {token.key}',
)
count_after = User.objects.count()
self.assertEqual(response.status_code, HTTP_201_CREATED)
new_user = User.objects.get(username='restapi')
token = Token.objects.get(user=new_user)
self.assertEqual(response.data["message"], "api user successfully registered")
self.assertEqual(response.data["token"], token.key)
self.assertEqual(response.data['message'], 'api user successfully registered')
self.assertEqual(response.data['token'], token.key)
self.assertEqual(count_after, count_before + 1)
def test_post_valid_api_user_creation_no_email(self):
@@ -93,19 +89,17 @@ class CreateUserCommand(WgerTestCase):
count_before = User.objects.count()
response = self.client.post(
reverse('api_register'), {
'username': 'restapi',
'password': 'AekaiLe0ga'
},
Authorization=f'Token {token.key}'
reverse('api_register'),
{'username': 'restapi', 'password': 'AekaiLe0ga'},
Authorization=f'Token {token.key}',
)
count_after = User.objects.count()
self.assertEqual(response.status_code, HTTP_201_CREATED)
new_user = User.objects.get(username='restapi')
token = Token.objects.get(user=new_user)
self.assertEqual(response.data["message"], "api user successfully registered")
self.assertEqual(response.data["token"], token.key)
self.assertEqual(response.data['message'], 'api user successfully registered')
self.assertEqual(response.data['token'], token.key)
self.assertEqual(count_after, count_before + 1)
def test_post_not_allowed_api_user_creation(self):
@@ -115,11 +109,8 @@ class CreateUserCommand(WgerTestCase):
count_before = User.objects.count()
response = self.client.post(
reverse('api_register'), {
'username': 'restapi',
'email': 'abc@cde.fg',
'password': 'AekaiLe0ga'
}
reverse('api_register'),
{'username': 'restapi', 'email': 'abc@cde.fg', 'password': 'AekaiLe0ga'},
)
count_after = User.objects.count()
@@ -149,12 +140,9 @@ class CreateUserCommand(WgerTestCase):
token = Token.objects.get(user=user)
response = self.client.post(
reverse('api_register'), {
'username': 'restapi',
'email': 'example.com',
'password': 'AekaiLe0ga'
},
Authorization=f'Token {token.key}'
reverse('api_register'),
{'username': 'restapi', 'email': 'example.com', 'password': 'AekaiLe0ga'},
Authorization=f'Token {token.key}',
)
self.assertEqual(response.status_code, HTTP_400_BAD_REQUEST)
@@ -167,12 +155,9 @@ class CreateUserCommand(WgerTestCase):
token = Token.objects.get(user=user)
response = self.client.post(
reverse('api_register'), {
'username': 'restapi',
'email': 'admin@example.com',
'password': 'AekaiLe0ga'
},
Authorization=f'Token {token.key}'
reverse('api_register'),
{'username': 'restapi', 'email': 'admin@example.com', 'password': 'AekaiLe0ga'},
Authorization=f'Token {token.key}',
)
self.assertEqual(response.status_code, HTTP_400_BAD_REQUEST)

View File

@@ -34,7 +34,7 @@ class RepresentationTestCase(WgerTestCase):
"""
Test that the representation of an object is correct
"""
self.assertEqual(f"{WeightUnit.objects.get(pk=1)}", 'kg')
self.assertEqual(f'{WeightUnit.objects.get(pk=1)}', 'kg')
class OverviewTest(WgerAccessTestCase):
@@ -54,7 +54,7 @@ class AddTestCase(WgerAddTestCase):
object_class = WeightUnit
url = 'core:weight-unit:add'
data = {'name': 'Furlongs'}
user_success = 'admin',
user_success = ('admin',)
user_fail = (
'general_manager1',
'general_manager2',
@@ -75,7 +75,7 @@ class DeleteTestCase(WgerDeleteTestCase):
pk = 1
object_class = WeightUnit
url = 'core:weight-unit:delete'
user_success = 'admin',
user_success = ('admin',)
user_fail = (
'general_manager1',
'general_manager2',
@@ -97,7 +97,7 @@ class EditTestCase(WgerEditTestCase):
object_class = WeightUnit
url = 'core:weight-unit:edit'
data = {'name': 'Furlongs'}
user_success = 'admin',
user_success = ('admin',)
user_fail = (
'general_manager1',
'general_manager2',
@@ -114,6 +114,7 @@ class ApiTestCase(api_base_test.ApiBaseResourceTestCase):
"""
Tests the unit resource
"""
pk = 1
resource = WeightUnit
private_resource = False

View File

@@ -69,7 +69,7 @@ patterns_user = [
path(
'login',
views.LoginView.as_view(template_name='user/login.html', authentication_form=UserLoginForm),
name='login'
name='login',
),
path('logout', user.logout, name='logout'),
path('delete', user.delete, name='delete'),
@@ -85,7 +85,6 @@ patterns_user = [
path('<int:pk>/edit', user.UserEditView.as_view(), name='edit'),
path('<int:pk>/overview', user.UserDetailView.as_view(), name='overview'),
path('list', user.UserListView.as_view(), name='list'),
# Password reset is implemented by Django, no need to cook our own soup here
# (besides the templates)
path(
@@ -191,17 +190,14 @@ patterns_weight_units = [
# Actual patterns
#
urlpatterns = [
# The landing page
path('', misc.index, name='index'),
# The dashboard
path('dashboard', misc.dashboard, name='dashboard'),
# Others
path(
'imprint',
TemplateView.as_view(template_name="misc/about.html"),
TemplateView.as_view(template_name='misc/about.html'),
name='imprint',
),
path(
@@ -209,12 +205,12 @@ urlpatterns = [
misc.FeedbackClass.as_view(),
name='feedback',
),
path('language/', include((patterns_language, 'language'), namespace="language")),
path('user/', include((patterns_user, 'user'), namespace="user")),
path('license/', include((patterns_license, 'license'), namespace="license")),
path('language/', include((patterns_language, 'language'), namespace='language')),
path('user/', include((patterns_user, 'user'), namespace='user')),
path('license/', include((patterns_license, 'license'), namespace='license')),
path(
'repetition-unit/',
include((patterns_repetition_units, 'repetition-unit'), namespace="repetition-unit")
include((patterns_repetition_units, 'repetition-unit'), namespace='repetition-unit'),
),
path('weight-unit/', include((patterns_weight_units, 'weight-unit'), namespace="weight-unit")),
path('weight-unit/', include((patterns_weight_units, 'weight-unit'), namespace='weight-unit')),
]

View File

@@ -50,6 +50,7 @@ class LanguageListView(LoginRequiredMixin, PermissionRequiredMixin, ListView):
"""
Show an overview of all languages
"""
model = Language
template_name = 'language/overview.html'
context_object_name = 'language_list'

View File

@@ -49,6 +49,7 @@ class LicenseListView(LoginRequiredMixin, PermissionRequiredMixin, ListView):
"""
Overview of all available licenses
"""
model = License
permission_required = 'core.add_license'
template_name = 'license/list.html'

View File

@@ -75,11 +75,8 @@ def demo_entries(request):
return HttpResponseRedirect(reverse('software:features'))
if (
(
(not request.user.is_authenticated or request.user.userprofile.is_temporary)
and not request.session['has_demo_data']
)
):
not request.user.is_authenticated or request.user.userprofile.is_temporary
) and not request.session['has_demo_data']:
# If we reach this from a page that has no user created by the
# middleware, do that now
if not request.user.is_authenticated:
@@ -96,7 +93,7 @@ def demo_entries(request):
'logs, (body) weight and nutrition plan entries so you can '
'better see what this site can do. Feel free to edit or '
'delete them!'
)
),
)
return HttpResponseRedirect(reverse('core:dashboard'))

View File

@@ -50,6 +50,7 @@ class ListView(LoginRequiredMixin, PermissionRequiredMixin, ListView):
"""
Overview of all available setting units
"""
model = RepetitionUnit
permission_required = 'core.add_repetitionunit'
template_name = 'repetition_unit/list.html'

View File

@@ -113,7 +113,7 @@ def login(request):
Small wrapper around the django login view
"""
next_url = "?next=" + request.GET.get('next') if request.GET.get('next') else ''
next_url = '?next=' + request.GET.get('next') if request.GET.get('next') else ''
form = UserLoginForm
form.helper.form_action = reverse('core:user:login') + next_url
@@ -135,12 +135,13 @@ def delete(request, user_pk=None):
# Forbidden if the user has not enough rights, doesn't belong to the
# gym or is an admin as well. General admins can delete all users.
if not request.user.has_perm('gym.manage_gyms') \
and (not request.user.has_perm('gym.manage_gym')
or request.user.userprofile.gym_id != user.userprofile.gym_id
or user.has_perm('gym.manage_gym')
or user.has_perm('gym.gym_trainer')
or user.has_perm('gym.manage_gyms')):
if not request.user.has_perm('gym.manage_gyms') and (
not request.user.has_perm('gym.manage_gym')
or request.user.userprofile.gym_id != user.userprofile.gym_id
or user.has_perm('gym.manage_gym')
or user.has_perm('gym.gym_trainer')
or user.has_perm('gym.manage_gyms')
):
return HttpResponseForbidden()
else:
user = request.user
@@ -150,11 +151,9 @@ def delete(request, user_pk=None):
if request.method == 'POST':
form = PasswordConfirmationForm(data=request.POST, user=request.user)
if form.is_valid():
user.delete()
messages.success(
request,
_('Account "{0}" was successfully deleted').format(user.username)
request, _('Account "{0}" was successfully deleted').format(user.username)
)
if not user_pk:
@@ -178,15 +177,15 @@ def trainer_login(request, user_pk):
orig_user_pk = request.user.pk
# No changing if identity is not set
if not request.user.has_perm('gym.gym_trainer') \
and not request.session.get('trainer.identity'):
if not request.user.has_perm('gym.gym_trainer') and not request.session.get('trainer.identity'):
return HttpResponseForbidden()
# Changing between trainers or managers is not allowed
if request.user.has_perm('gym.gym_trainer') \
and (user.has_perm('gym.gym_trainer')
or user.has_perm('gym.manage_gym')
or user.has_perm('gym.manage_gyms')):
if request.user.has_perm('gym.gym_trainer') and (
user.has_perm('gym.gym_trainer')
or user.has_perm('gym.manage_gym')
or user.has_perm('gym.manage_gyms')
):
return HttpResponseForbidden()
# Changing is only allowed between the same gym
@@ -201,7 +200,8 @@ def trainer_login(request, user_pk):
# Check if we're switching back to our original account
own = False
if (
user.has_perm('gym.gym_trainer') or user.has_perm('gym.manage_gym')
user.has_perm('gym.gym_trainer')
or user.has_perm('gym.manage_gym')
or user.has_perm('gym.manage_gyms')
):
own = True
@@ -209,7 +209,7 @@ def trainer_login(request, user_pk):
# Note: when logging without authenticating, it is necessary to set the
# authentication backend
if own:
del (request.session['trainer.identity'])
del request.session['trainer.identity']
django_login(request, user, 'django.contrib.auth.backends.ModelBackend')
if not own:
@@ -248,8 +248,9 @@ def registration(request):
template_data.update(csrf(request))
# Don't show captcha if the global parameter is false
FormClass = RegistrationForm if settings.WGER_SETTINGS['USE_RECAPTCHA'] \
else RegistrationFormNoCaptcha
FormClass = (
RegistrationForm if settings.WGER_SETTINGS['USE_RECAPTCHA'] else RegistrationFormNoCaptcha
)
# Redirect regular users, in case they reached the registration page
if request.user.is_authenticated and not request.user.userprofile.is_temporary:
@@ -313,7 +314,6 @@ def preferences(request):
# Process the preferences form
if request.method == 'POST':
form = UserPreferencesForm(data=request.POST, instance=request.user.userprofile)
form.user = request.user
@@ -325,7 +325,7 @@ def preferences(request):
data = {
'first_name': request.user.first_name,
'last_name': request.user.last_name,
'email': request.user.email
'email': request.user.email,
}
form = UserPreferencesForm(initial=data, instance=request.user.userprofile)
@@ -336,7 +336,6 @@ def preferences(request):
email_form = UserPersonalInformationForm(data=request.POST, instance=request.user)
if email_form.is_valid() and redirect:
# If the user changes the email, it is no longer verified
if user_email != email_form.instance.email:
logger.debug('resetting verified flag')
@@ -366,6 +365,7 @@ class UserDeactivateView(
"""
Deactivates a user
"""
permanent = False
model = User
permission_required = ('gym.manage_gym', 'gym.manage_gyms', 'gym.gym_trainer')
@@ -379,8 +379,9 @@ class UserDeactivateView(
if not request.user.is_authenticated:
return HttpResponseForbidden()
if (request.user.has_perm('gym.manage_gym') or request.user.has_perm('gym.gym_trainer')) \
and edit_user.userprofile.gym_id != request.user.userprofile.gym_id:
if (
request.user.has_perm('gym.manage_gym') or request.user.has_perm('gym.gym_trainer')
) and edit_user.userprofile.gym_id != request.user.userprofile.gym_id:
return HttpResponseForbidden()
return super(UserDeactivateView, self).dispatch(request, *args, **kwargs)
@@ -401,6 +402,7 @@ class UserActivateView(
"""
Activates a previously deactivated user
"""
permanent = False
model = User
permission_required = ('gym.manage_gym', 'gym.manage_gyms', 'gym.gym_trainer')
@@ -414,8 +416,9 @@ class UserActivateView(
if not request.user.is_authenticated:
return HttpResponseForbidden()
if (request.user.has_perm('gym.manage_gym') or request.user.has_perm('gym.gym_trainer')) \
and edit_user.userprofile.gym_id != request.user.userprofile.gym_id:
if (
request.user.has_perm('gym.manage_gym') or request.user.has_perm('gym.gym_trainer')
) and edit_user.userprofile.gym_id != request.user.userprofile.gym_id:
return HttpResponseForbidden()
return super(UserActivateView, self).dispatch(request, *args, **kwargs)
@@ -454,9 +457,11 @@ class UserEditView(
if not user.is_authenticated:
return HttpResponseForbidden()
if user.has_perm('gym.manage_gym') \
and not user.has_perm('gym.manage_gyms') \
and user.userprofile.gym != self.get_object().userprofile.gym:
if (
user.has_perm('gym.manage_gym')
and not user.has_perm('gym.manage_gyms')
and user.userprofile.gym != self.get_object().userprofile.gym
):
return HttpResponseForbidden()
return super(UserEditView, self).dispatch(request, *args, **kwargs)
@@ -502,6 +507,7 @@ class UserDetailView(LoginRequiredMixin, WgerMultiplePermissionRequiredMixin, De
"""
User overview for gyms
"""
model = User
permission_required = ('gym.manage_gym', 'gym.manage_gyms', 'gym.gym_trainer')
template_name = 'user/overview.html'
@@ -519,9 +525,11 @@ class UserDetailView(LoginRequiredMixin, WgerMultiplePermissionRequiredMixin, De
if not user.is_authenticated:
return HttpResponseForbidden()
if (user.has_perm('gym.manage_gym') or user.has_perm('gym.gym_trainer')) \
and not user.has_perm('gym.manage_gyms') \
and user.userprofile.gym != self.get_object().userprofile.gym:
if (
(user.has_perm('gym.manage_gym') or user.has_perm('gym.gym_trainer'))
and not user.has_perm('gym.manage_gyms')
and user.userprofile.gym != self.get_object().userprofile.gym
):
return HttpResponseForbidden()
return super(UserDetailView, self).dispatch(request, *args, **kwargs)
@@ -539,14 +547,16 @@ class UserDetailView(LoginRequiredMixin, WgerMultiplePermissionRequiredMixin, De
{
'workout': workout,
'logs': logs.dates('date', 'day').count(),
'last_log': logs.last()
'last_log': logs.last(),
}
)
context['workouts'] = out
context['weight_entries'] = WeightEntry.objects.filter(user=self.object) \
.order_by('-date')[:5]
context['nutrition_plans'] = NutritionPlan.objects.filter(user=self.object) \
.order_by('-creation_date')[:5]
context['weight_entries'] = WeightEntry.objects.filter(user=self.object).order_by('-date')[
:5
]
context['nutrition_plans'] = NutritionPlan.objects.filter(user=self.object).order_by(
'-creation_date'
)[:5]
context['session'] = WorkoutSession.objects.filter(user=self.object).order_by('-date')[:10]
context['admin_notes'] = AdminUserNote.objects.filter(member=self.object)[:5]
context['contracts'] = Contract.objects.filter(member=self.object)[:5]
@@ -563,8 +573,9 @@ class UserListView(LoginRequiredMixin, PermissionRequiredMixin, ListView):
"""
Overview of all users in the instance
"""
model = User
permission_required = ('gym.manage_gyms', )
permission_required = ('gym.manage_gyms',)
template_name = 'user/list.html'
def get_queryset(self):
@@ -592,7 +603,7 @@ class UserListView(LoginRequiredMixin, PermissionRequiredMixin, ListView):
_('Last activity'),
_('Gym'),
],
'users': context['object_list']['members']
'users': context['object_list']['members'],
}
return context
@@ -600,7 +611,7 @@ class UserListView(LoginRequiredMixin, PermissionRequiredMixin, ListView):
class WgerPasswordChangeView(PasswordChangeView):
template_name = 'form.html'
success_url = reverse_lazy('core:user:preferences')
title = gettext_lazy("Change password")
title = gettext_lazy('Change password')
def get_form(self, form_class=None):
form = super(WgerPasswordChangeView, self).get_form(form_class)
@@ -611,8 +622,9 @@ class WgerPasswordChangeView(PasswordChangeView):
Row(
Column('new_password1', css_class='col-6'),
Column('new_password2', css_class='col-6'),
css_class='form-row'
), ButtonHolder(Submit('submit', _("Save"), css_class='btn-success btn-block'))
css_class='form-row',
),
ButtonHolder(Submit('submit', _('Save'), css_class='btn-success btn-block')),
)
return form
@@ -627,7 +639,7 @@ class WgerPasswordResetView(PasswordResetView):
form = super(WgerPasswordResetView, self).get_form(form_class)
form.helper = FormHelper()
form.helper.form_class = 'wger-form'
form.helper.add_input(Submit('submit', _("Save"), css_class='btn-success btn-block'))
form.helper.add_input(Submit('submit', _('Save'), css_class='btn-success btn-block'))
return form
@@ -639,7 +651,7 @@ class WgerPasswordResetConfirmView(PasswordResetConfirmView):
form = super(WgerPasswordResetConfirmView, self).get_form(form_class)
form.helper = FormHelper()
form.helper.form_class = 'wger-form'
form.helper.add_input(Submit('submit', _("Save"), css_class='btn-success btn-block'))
form.helper.add_input(Submit('submit', _('Save'), css_class='btn-success btn-block'))
return form
@@ -648,8 +660,7 @@ def confirm_email(request):
if not request.user.userprofile.email_verified:
send_email(request.user)
messages.success(
request,
_('A verification email was sent to %(email)s') % {'email': request.user.email}
request, _('A verification email was sent to %(email)s') % {'email': request.user.email}
)
return HttpResponseRedirect(reverse('core:dashboard'))

View File

@@ -50,6 +50,7 @@ class ListView(LoginRequiredMixin, PermissionRequiredMixin, ListView):
"""
Overview of all available weight units
"""
model = WeightUnit
permission_required = 'core.add_weightunit'
template_name = 'weight_unit/list.html'

View File

@@ -1,7 +1,7 @@
EXERCISE_ENDPOINT = "exercisebaseinfo"
DELETION_LOG_ENDPOINT = "deletion-log"
CATEGORY_ENDPOINT = "exercisecategory"
MUSCLE_ENDPOINT = "muscle"
EQUIPMENT_ENDPOINT = "equipment"
IMAGE_ENDPOINT = "exerciseimage"
VIDEO_ENDPOINT = "video"
EXERCISE_ENDPOINT = 'exercisebaseinfo'
DELETION_LOG_ENDPOINT = 'deletion-log'
CATEGORY_ENDPOINT = 'exercisecategory'
MUSCLE_ENDPOINT = 'muscle'
EQUIPMENT_ENDPOINT = 'equipment'
IMAGE_ENDPOINT = 'exerciseimage'
VIDEO_ENDPOINT = 'video'

View File

@@ -35,7 +35,6 @@ class CanContributeExercises(BasePermission):
DELETE_METHODS = ['DELETE']
def has_permission(self, request, view):
# Everybody can read
if request.method in self.SAFE_METHODS:
return True
@@ -46,9 +45,8 @@ class CanContributeExercises(BasePermission):
# Creating or updating
if request.method in self.ADD_METHODS:
return (
request.user.userprofile.is_trustworthy
or request.user.has_perm('exercises.add_exercise')
return request.user.userprofile.is_trustworthy or request.user.has_perm(
'exercises.add_exercise'
)
# Only admins are allowed to delete entries

View File

@@ -88,6 +88,7 @@ class ExerciseImageSerializer(serializers.ModelSerializer):
"""
ExerciseImage serializer
"""
author_history = serializers.ListSerializer(child=serializers.CharField(), read_only=True)
exercise_base_uuid = serializers.ReadOnlyField(source='exercise_base.uuid')
@@ -115,6 +116,7 @@ class ExerciseVideoSerializer(serializers.ModelSerializer):
"""
ExerciseVideo serializer
"""
exercise_base_uuid = serializers.ReadOnlyField(source='exercise_base.uuid')
author_history = serializers.ListSerializer(child=serializers.CharField(), read_only=True)
@@ -147,6 +149,7 @@ class ExerciseVideoInfoSerializer(serializers.ModelSerializer):
"""
ExerciseVideo serializer for the info endpoint
"""
author_history = serializers.ListSerializer(child=serializers.CharField(), read_only=True)
class Meta:
@@ -245,6 +248,7 @@ class MuscleSerializer(serializers.ModelSerializer):
"""
Muscle serializer
"""
image_url_main = serializers.CharField()
image_url_secondary = serializers.CharField()
@@ -267,6 +271,7 @@ class ExerciseSerializer(serializers.ModelSerializer):
The fields from the new ExerciseBase are retrieved here as to retain
compatibility with the old model where all the fields where in Exercise.
"""
category = serializers.PrimaryKeyRelatedField(queryset=ExerciseCategory.objects.all())
muscles = serializers.PrimaryKeyRelatedField(many=True, queryset=Muscle.objects.all())
muscles_secondary = serializers.PrimaryKeyRelatedField(many=True, queryset=Muscle.objects.all())
@@ -277,21 +282,21 @@ class ExerciseSerializer(serializers.ModelSerializer):
class Meta:
model = Exercise
fields = (
"id",
"uuid",
"name",
"exercise_base",
"description",
"created",
"category",
"muscles",
"muscles_secondary",
"equipment",
"language",
"license",
"license_author",
"variations",
"author_history",
'id',
'uuid',
'name',
'exercise_base',
'description',
'created',
'category',
'muscles',
'muscles_secondary',
'equipment',
'language',
'license',
'license_author',
'variations',
'author_history',
)
@@ -299,6 +304,7 @@ class ExerciseTranslationBaseInfoSerializer(serializers.ModelSerializer):
"""
Exercise translation serializer for the base info endpoint
"""
id = serializers.IntegerField(required=False, read_only=True)
uuid = serializers.UUIDField(required=False, read_only=True)
exercise_base = serializers.PrimaryKeyRelatedField(
@@ -312,22 +318,22 @@ class ExerciseTranslationBaseInfoSerializer(serializers.ModelSerializer):
class Meta:
model = Exercise
fields = (
"id",
"uuid",
"name",
"exercise_base",
"description",
"created",
"language",
"aliases",
"notes",
"license",
"license_title",
"license_object_url",
"license_author",
"license_author_url",
"license_derivative_source_url",
"author_history",
'id',
'uuid',
'name',
'exercise_base',
'description',
'created',
'language',
'aliases',
'notes',
'license',
'license_title',
'license_object_url',
'license_author',
'license_author_url',
'license_derivative_source_url',
'author_history',
)
@@ -335,6 +341,7 @@ class ExerciseTranslationSerializer(serializers.ModelSerializer):
"""
Exercise translation serializer
"""
id = serializers.IntegerField(required=False, read_only=True)
uuid = serializers.UUIDField(required=False, read_only=True)
exercise_base = serializers.PrimaryKeyRelatedField(
@@ -345,13 +352,13 @@ class ExerciseTranslationSerializer(serializers.ModelSerializer):
class Meta:
model = Exercise
fields = (
"id",
"uuid",
"name",
"exercise_base",
"description",
"created",
"language",
'id',
'uuid',
'name',
'exercise_base',
'description',
'created',
'language',
'license_author',
)
@@ -402,25 +409,25 @@ class ExerciseInfoSerializer(serializers.ModelSerializer):
model = Exercise
depth = 1
fields = [
"id",
"name",
"aliases",
"uuid",
"exercise_base_id",
"description",
"created",
"category",
"muscles",
"muscles_secondary",
"equipment",
"language",
"license",
"license_author",
"images",
"videos",
"comments",
"variations",
"author_history",
'id',
'name',
'aliases',
'uuid',
'exercise_base_id',
'description',
'created',
'category',
'muscles',
'muscles_secondary',
'equipment',
'language',
'license',
'license_author',
'images',
'videos',
'comments',
'variations',
'author_history',
]
@@ -445,24 +452,24 @@ class ExerciseBaseInfoSerializer(serializers.ModelSerializer):
model = ExerciseBase
depth = 1
fields = [
"id",
"uuid",
"created",
"last_update",
"last_update_global",
"category",
"muscles",
"muscles_secondary",
"equipment",
"license",
"license_author",
"images",
"exercises",
"variations",
"images",
"videos",
"author_history",
"total_authors_history",
'id',
'uuid',
'created',
'last_update',
'last_update_global',
'category',
'muscles',
'muscles_secondary',
'equipment',
'license',
'license_author',
'images',
'exercises',
'variations',
'images',
'videos',
'author_history',
'total_authors_history',
]
def to_representation(self, instance):

View File

@@ -99,9 +99,10 @@ class ExerciseBaseViewSet(ModelViewSet):
For a read-only endpoint with all the information of an exercise, see /api/v2/exercisebaseinfo/
"""
queryset = ExerciseBase.translations.all()
serializer_class = ExerciseBaseSerializer
permission_classes = (CanContributeExercises, )
permission_classes = (CanContributeExercises,)
ordering_fields = '__all__'
filterset_fields = (
'category',
@@ -148,8 +149,9 @@ class ExerciseTranslationViewSet(ModelViewSet):
"""
API endpoint for editing or adding exercise translation objects.
"""
queryset = Exercise.objects.all()
permission_classes = (CanContributeExercises, )
permission_classes = (CanContributeExercises,)
serializer_class = ExerciseTranslationSerializer
ordering_fields = '__all__'
filterset_fields = (
@@ -171,7 +173,7 @@ class ExerciseTranslationViewSet(ModelViewSet):
tags=HTML_TAG_WHITELIST,
attributes=HTML_ATTRIBUTES_WHITELIST,
css_sanitizer=CSSSanitizer(allowed_css_properties=HTML_STYLES_WHITELIST),
strip=True
strip=True,
)
super().perform_create(serializer)
@@ -200,7 +202,7 @@ class ExerciseTranslationViewSet(ModelViewSet):
tags=HTML_TAG_WHITELIST,
attributes=HTML_ATTRIBUTES_WHITELIST,
css_sanitizer=CSSSanitizer(allowed_css_properties=HTML_STYLES_WHITELIST),
strip=True
strip=True,
)
super().perform_update(serializer)
@@ -217,8 +219,9 @@ class ExerciseViewSet(viewsets.ReadOnlyModelViewSet):
This is only kept for backwards compatibility and will be removed in the future
"""
queryset = Exercise.objects.all()
permission_classes = (CanContributeExercises, )
permission_classes = (CanContributeExercises,)
serializer_class = ExerciseSerializer
ordering_fields = '__all__'
filterset_fields = (
@@ -257,13 +260,13 @@ class ExerciseViewSet(viewsets.ReadOnlyModelViewSet):
try:
qs = qs.filter(exercise_base__category_id=int(category))
except ValueError:
logger.info(f"Got {category} as category ID")
logger.info(f'Got {category} as category ID')
if muscles:
try:
qs = qs.filter(exercise_base__muscles__in=[int(m) for m in muscles.split(',')])
except ValueError:
logger.info(f"Got {muscles} as muscle IDs")
logger.info(f'Got {muscles} as muscle IDs')
if muscles_secondary:
try:
@@ -276,13 +279,13 @@ class ExerciseViewSet(viewsets.ReadOnlyModelViewSet):
try:
qs = qs.filter(exercise_base__equipment__in=[int(e) for e in equipment.split(',')])
except ValueError:
logger.info(f"Got {equipment} as equipment IDs")
logger.info(f'Got {equipment} as equipment IDs')
if license:
try:
qs = qs.filter(exercise_base__license_id=int(license))
except ValueError:
logger.info(f"Got {license} as license ID")
logger.info(f'Got {license} as license ID')
return qs
@@ -306,14 +309,11 @@ class ExerciseViewSet(viewsets.ReadOnlyModelViewSet):
],
# yapf: disable
responses={
200:
inline_serializer(
200: inline_serializer(
name='ExerciseSearchResponse',
fields={
'value':
CharField(),
'data':
inline_serializer(
'value': CharField(),
'data': inline_serializer(
name='ExerciseSearchItemResponse',
fields={
'id': IntegerField(),
@@ -321,12 +321,12 @@ class ExerciseViewSet(viewsets.ReadOnlyModelViewSet):
'name': CharField(),
'category': CharField(),
'image': CharField(),
'image_thumbnail': CharField()
}
)
}
'image_thumbnail': CharField(),
},
),
},
)
}
},
# yapf: enable
)
@api_view(['GET'])
@@ -345,11 +345,12 @@ def search(request):
return Response(response)
languages = [load_language(l) for l in language_codes.split(',')]
translations = Exercise.objects \
.filter(Q(name__icontains=q) | Q(alias__alias__icontains=q)) \
.filter(language__in=languages) \
.order_by('exercise_base__category__name', 'name') \
translations = (
Exercise.objects.filter(Q(name__icontains=q) | Q(alias__alias__icontains=q))
.filter(language__in=languages)
.order_by('exercise_base__category__name', 'name')
.distinct()
)
for translation in translations:
image = None
@@ -372,8 +373,8 @@ def search(request):
'name': translation.name,
'category': _(translation.category.name),
'image': image,
'image_thumbnail': thumbnail
}
'image_thumbnail': thumbnail,
},
}
results.append(result_json)
response['suggestions'] = results
@@ -436,10 +437,11 @@ class EquipmentViewSet(viewsets.ReadOnlyModelViewSet):
"""
API endpoint for equipment objects
"""
queryset = Equipment.objects.all()
serializer_class = EquipmentSerializer
ordering_fields = '__all__'
filterset_fields = ('name', )
filterset_fields = ('name',)
@method_decorator(cache_page(settings.WGER_SETTINGS['EXERCISE_CACHE_TTL']))
def dispatch(self, request, *args, **kwargs):
@@ -454,20 +456,22 @@ class DeletionLogViewSet(viewsets.ReadOnlyModelViewSet):
as well when performing a sync (e.g. because many exercises where submitted at
once or an image was uploaded that hasn't a CC license)
"""
queryset = DeletionLog.objects.all()
serializer_class = DeletionLogSerializer
ordering_fields = '__all__'
filterset_fields = ('model_type', )
filterset_fields = ('model_type',)
class ExerciseCategoryViewSet(viewsets.ReadOnlyModelViewSet):
"""
API endpoint for exercise categories objects
"""
queryset = ExerciseCategory.objects.all()
serializer_class = ExerciseCategorySerializer
ordering_fields = '__all__'
filterset_fields = ('name', )
filterset_fields = ('name',)
@method_decorator(cache_page(settings.WGER_SETTINGS['EXERCISE_CACHE_TTL']))
def dispatch(self, request, *args, **kwargs):
@@ -481,7 +485,7 @@ class ExerciseImageViewSet(ModelViewSet):
queryset = ExerciseImage.objects.all()
serializer_class = ExerciseImageSerializer
permission_classes = (CanContributeExercises, )
permission_classes = (CanContributeExercises,)
ordering_fields = '__all__'
filterset_fields = (
'is_main',
@@ -509,7 +513,7 @@ class ExerciseImageViewSet(ModelViewSet):
t = get_thumbnailer(image.image)
thumbnails[alias] = {
'url': t.get_thumbnail(aliases.get(alias)).url,
'settings': aliases.get(alias)
'settings': aliases.get(alias),
}
thumbnails['original'] = image.image.url
return Response(thumbnails)
@@ -541,9 +545,10 @@ class ExerciseVideoViewSet(ModelViewSet):
"""
API endpoint for exercise video objects
"""
queryset = ExerciseVideo.objects.all()
serializer_class = ExerciseVideoSerializer
permission_classes = (CanContributeExercises, )
permission_classes = (CanContributeExercises,)
ordering_fields = '__all__'
filterset_fields = (
'is_main',
@@ -579,8 +584,9 @@ class ExerciseCommentViewSet(ModelViewSet):
"""
API endpoint for exercise comment objects
"""
serializer_class = ExerciseCommentSerializer
permission_classes = (CanContributeExercises, )
permission_classes = (CanContributeExercises,)
ordering_fields = '__all__'
filterset_fields = ('comment', 'exercise')
@@ -620,9 +626,10 @@ class ExerciseAliasViewSet(ModelViewSet):
"""
API endpoint for exercise aliases objects
"""
serializer_class = ExerciseAliasSerializer
queryset = Alias.objects.all()
permission_classes = (CanContributeExercises, )
permission_classes = (CanContributeExercises,)
ordering_fields = '__all__'
filterset_fields = ('alias', 'exercise')
@@ -653,15 +660,17 @@ class ExerciseVariationViewSet(ModelViewSet):
"""
API endpoint for exercise variation objects
"""
serializer_class = ExerciseVariationSerializer
queryset = Variation.objects.all()
permission_classes = (CanContributeExercises, )
permission_classes = (CanContributeExercises,)
class MuscleViewSet(viewsets.ReadOnlyModelViewSet):
"""
API endpoint for muscle objects
"""
queryset = Muscle.objects.all()
serializer_class = MuscleSerializer
ordering_fields = '__all__'

View File

@@ -20,12 +20,13 @@ from django.apps import AppConfig
class ExerciseConfig(AppConfig):
name = 'wger.exercises'
verbose_name = "Exercise"
verbose_name = 'Exercise'
def ready(self):
import wger.exercises.signals
from actstream import registry
registry.register(self.get_model('Alias'))
registry.register(self.get_model('Exercise'))
registry.register(self.get_model('ExerciseBase'))

View File

@@ -26,7 +26,6 @@ from wger.exercises.models import (
class ExerciseImageForm(forms.ModelForm):
class Meta:
model = ExerciseImage
fields = (
@@ -39,7 +38,6 @@ class ExerciseImageForm(forms.ModelForm):
class ExerciseVideoForm(forms.ModelForm):
class Meta:
model = ExerciseVideo
fields = (
@@ -50,7 +48,6 @@ class ExerciseVideoForm(forms.ModelForm):
class CommentForm(forms.ModelForm):
class Meta:
model = ExerciseComment
exclude = ('exercise', )
exclude = ('exercise',)

View File

@@ -40,7 +40,7 @@ class Command(BaseCommand):
'--exercise-base-id',
action='store',
dest='exercise_base_id',
help='The ID of the exercise base'
help='The ID of the exercise base',
)
parser.add_argument(
'--exercise-id', action='store', dest='exercise_id', help='The ID of the exercise'
@@ -77,7 +77,7 @@ class Command(BaseCommand):
exercise.license_author = author_name
exercise.save()
self.stdout.write(self.style.SUCCESS(f"Exercise and/or exercise base has been updated"))
self.stdout.write(self.style.SUCCESS(f'Exercise and/or exercise base has been updated'))
def print_error(self, error_message):
self.stdout.write(self.style.WARNING(f"{error_message}"))
self.stdout.write(self.style.WARNING(f'{error_message}'))

View File

@@ -38,7 +38,6 @@ class Command(BaseCommand):
"""
def handle(self, **options):
# Collect all exercise bases that are not used in any workout or log entry
out = f'{ExerciseBase.objects.all().count()} exercises currently in the database'
self.stdout.write(out)

View File

@@ -52,11 +52,10 @@ class Command(BaseCommand):
dest='remote_url',
default=settings.WGER_SETTINGS['WGER_INSTANCE'],
help=f'Remote URL to fetch the images from (default: WGER_SETTINGS'
f'["WGER_INSTANCE"] - {settings.WGER_SETTINGS["WGER_INSTANCE"]})'
f'["WGER_INSTANCE"] - {settings.WGER_SETTINGS["WGER_INSTANCE"]})',
)
def handle(self, **options):
if not settings.MEDIA_ROOT:
raise ImproperlyConfigured('Please set MEDIA_ROOT in your settings file')

View File

@@ -44,12 +44,10 @@ class Command(BaseCommand):
action='store',
dest='remote_url',
default='https://wger.de',
help='Remote URL to fetch the exercises from (default: '
'https://wger.de)'
help='Remote URL to fetch the exercises from (default: ' 'https://wger.de)',
)
def handle(self, **options):
if not settings.MEDIA_ROOT:
raise ImproperlyConfigured('Please set MEDIA_ROOT in your settings file')

View File

@@ -40,13 +40,12 @@ class Command(BaseCommand):
languages = Language.objects.all()
for base in ExerciseBase.objects.all():
data = {
'base': {
'uuid': base.uuid,
'category': base.category.name,
'equipment': ','.join([e.name for e in base.equipment.all()]),
'variations': base.variations.id if base.variations else ''
'variations': base.variations.id if base.variations else '',
}
}
@@ -57,7 +56,7 @@ class Command(BaseCommand):
'description': '',
'aliases': '',
'license': '',
'author': ''
'author': '',
}
exercise = Exercise.objects.filter(exercise_base=base, language=language).first()
@@ -73,7 +72,9 @@ class Command(BaseCommand):
out.append(data)
with open('exercise_cleanup.csv', 'w', newline='') as csvfile:
file_writer = csv.writer(csvfile, )
file_writer = csv.writer(
csvfile,
)
header = ['base:uuid', 'base:category', 'base:equipment', 'base:variations']
for language in languages:

View File

@@ -29,14 +29,17 @@ class Command(BaseCommand):
"""
Performs some sanity checks on the exercise database
"""
english: Language
help = "Performs some sanity checks on the exercise database. " \
"At the moment this script checks that each exercise:\n" \
"- has at least one translation\n" \
"- has a translation in English\n" \
"- has no duplicate translations\n\n" \
"Each problem can be fixed individually by using the --delete-* flags\n"
help = (
'Performs some sanity checks on the exercise database. '
'At the moment this script checks that each exercise:\n'
'- has at least one translation\n'
'- has a translation in English\n'
'- has no duplicate translations\n\n'
'Each problem can be fixed individually by using the --delete-* flags\n'
)
def create_parser(self, *args, **kwargs):
parser = super(Command, self).create_parser(*args, **kwargs)
@@ -44,14 +47,13 @@ class Command(BaseCommand):
return parser
def add_arguments(self, parser):
parser.add_argument(
'--delete-untranslated',
action='store_true',
dest='delete_untranslated',
default=False,
help="Delete exercises without translations (safe to use since these can't be "
"accessed over the UI)",
'accessed over the UI)',
)
parser.add_argument(
@@ -80,7 +82,6 @@ class Command(BaseCommand):
)
def handle(self, **options):
delete_untranslated = options['delete_untranslated'] or options['delete_all']
delete_duplicates = options['delete_duplicates'] or options['delete_all']
delete_no_english = options['delete_no_english'] or options['delete_all']
@@ -120,7 +121,8 @@ class Command(BaseCommand):
exercise_languages = base.exercises.values_list('language', flat=True)
duplicates = [
Language.objects.get(pk=item)
for item, count in collections.Counter(exercise_languages).items() if count > 1
for item, count in collections.Counter(exercise_languages).items()
if count > 1
]
if not duplicates:

View File

@@ -63,7 +63,7 @@ class Command(BaseCommand):
action='store_true',
dest='process_videos',
default=False,
help='Flag indicating whether to process and add videos to the exercises'
help='Flag indicating whether to process and add videos to the exercises',
)
parser.add_argument(
@@ -71,7 +71,7 @@ class Command(BaseCommand):
action='store_true',
dest='create_on_new',
default=True,
help="Controls whether we create new bases or exercises if they have the UUID 'NEW'"
help="Controls whether we create new bases or exercises if they have the UUID 'NEW'",
)
def handle(self, **options):
@@ -91,8 +91,9 @@ class Command(BaseCommand):
for language in [l.short_name for l in languages]:
for column in columns:
name = '{0}:{1}'.format(language, column)
assert (name in file_reader.fieldnames
), '{0} not in {1}'.format(name, file_reader.fieldnames)
assert name in file_reader.fieldnames, '{0} not in {1}'.format(
name, file_reader.fieldnames
)
default_license = License.objects.get(pk=CC_BY_SA_4_ID)
@@ -121,10 +122,14 @@ class Command(BaseCommand):
self.stdout.write(f' Skipping creating new exercise base...\n')
continue
base = ExerciseBase.objects.get_or_create(
uuid=base_uuid,
defaults={'category': ExerciseCategory.objects.get(name=base_category)}
)[0] if not new_base else ExerciseBase()
base = (
ExerciseBase.objects.get_or_create(
uuid=base_uuid,
defaults={'category': ExerciseCategory.objects.get(name=base_category)},
)[0]
if not new_base
else ExerciseBase()
)
# Update the base data
base.category = ExerciseCategory.objects.get(name=base_category)
@@ -200,12 +205,13 @@ class Command(BaseCommand):
if not new_translation:
continue
translation = Exercise.objects.get_or_create(
uuid=exercise_uuid, defaults={
'exercise_base': base,
'language': language
}
)[0] if not new_translation else Exercise()
translation = (
Exercise.objects.get_or_create(
uuid=exercise_uuid, defaults={'exercise_base': base, 'language': language}
)[0]
if not new_translation
else Exercise()
)
translation.exercise_base = base
translation.language = language
translation.name = exercise_name
@@ -218,8 +224,9 @@ class Command(BaseCommand):
exercise_license = License.objects.get(short_name=exercise_license)
except License.DoesNotExist:
self.stdout.write(
self.style.
WARNING(f' License does not exist: {exercise_license}!!!\n')
self.style.WARNING(
f' License does not exist: {exercise_license}!!!\n'
)
)
exercise_license = default_license
else:

View File

@@ -29,7 +29,6 @@ class Command(BaseCommand):
help = 'Read out the user submitted exercise'
def handle(self, **options):
exercises = Exercise.objects.all()
usernames = []
for exercise in exercises:

View File

@@ -37,6 +37,7 @@ class Command(BaseCommand):
"""
Synchronizes exercise data from a wger instance to the local database
"""
remote_url = settings.WGER_SETTINGS['WGER_INSTANCE']
help = """Synchronizes exercise data from a wger instance to the local database.
@@ -58,7 +59,7 @@ class Command(BaseCommand):
dest='remote_url',
default=settings.WGER_SETTINGS['WGER_INSTANCE'],
help=f'Remote URL to fetch the exercises from (default: WGER_SETTINGS'
f'["WGER_INSTANCE"] - {settings.WGER_SETTINGS["WGER_INSTANCE"]})'
f'["WGER_INSTANCE"] - {settings.WGER_SETTINGS["WGER_INSTANCE"]})',
)
parser.add_argument(
@@ -66,11 +67,10 @@ class Command(BaseCommand):
action='store_true',
dest='skip_delete',
default=False,
help='Skips deleting any entries'
help='Skips deleting any entries',
)
def handle(self, **options):
remote_url = options['remote_url']
try:

View File

@@ -31,7 +31,7 @@ class Command(BaseCommand):
'--exercise-base-id',
action='store',
dest='exercise_base_id',
help='The ID of the exercise base, otherwise all exercises will be updated'
help='The ID of the exercise base, otherwise all exercises will be updated',
)
parser.add_argument(
@@ -39,7 +39,7 @@ class Command(BaseCommand):
action='store_true',
dest='force',
default=False,
help='Force the update of the cache'
help='Force the update of the cache',
)
def handle(self, **options):
@@ -56,9 +56,9 @@ class Command(BaseCommand):
def handle_cache(self, exercise: ExerciseBase, force: bool):
if force:
self.stdout.write(f"Force updating cache for exercise base {exercise.uuid}")
self.stdout.write(f'Force updating cache for exercise base {exercise.uuid}')
else:
self.stdout.write(f"Warming cache for exercise base {exercise.uuid}")
self.stdout.write(f'Warming cache for exercise base {exercise.uuid}')
if force:
reset_exercise_api_cache(exercise.uuid)

View File

@@ -6,7 +6,6 @@ import wger.exercises.models
class Migration(migrations.Migration):
dependencies = [
('core', '0001_initial'),
]
@@ -19,14 +18,14 @@ class Migration(migrations.Migration):
'id',
models.AutoField(
verbose_name='ID', serialize=False, auto_created=True, primary_key=True
)
),
),
('name', models.CharField(max_length=50, verbose_name='Name')),
],
options={
'ordering': ['name'],
},
bases=(models.Model, ),
bases=(models.Model,),
),
migrations.CreateModel(
name='Exercise',
@@ -35,18 +34,17 @@ class Migration(migrations.Migration):
'id',
models.AutoField(
verbose_name='ID', serialize=False, auto_created=True, primary_key=True
)
),
),
(
'license_author',
models.CharField(
help_text=
'If you are not the author, enter the name or source here. This is needed for some licenses e.g. the CC-BY-SA.',
help_text='If you are not the author, enter the name or source here. This is needed for some licenses e.g. the CC-BY-SA.',
max_length=50,
null=True,
verbose_name='Author',
blank=True
)
blank=True,
),
),
(
'status',
@@ -54,27 +52,27 @@ class Migration(migrations.Migration):
default=b'1',
max_length=2,
editable=False,
choices=[(b'1', 'Pending'), (b'2', 'Accepted'), (b'3', 'Declined')]
)
choices=[(b'1', 'Pending'), (b'2', 'Accepted'), (b'3', 'Declined')],
),
),
(
'description',
models.TextField(
max_length=2000,
verbose_name='Description',
validators=[django.core.validators.MinLengthValidator(40)]
)
validators=[django.core.validators.MinLengthValidator(40)],
),
),
('name', models.CharField(max_length=200, verbose_name='Name')),
(
'creation_date',
models.DateField(auto_now_add=True, verbose_name='Date', null=True)
models.DateField(auto_now_add=True, verbose_name='Date', null=True),
),
],
options={
'ordering': ['name'],
},
bases=(models.Model, ),
bases=(models.Model,),
),
migrations.CreateModel(
name='ExerciseCategory',
@@ -83,7 +81,7 @@ class Migration(migrations.Migration):
'id',
models.AutoField(
verbose_name='ID', serialize=False, auto_created=True, primary_key=True
)
),
),
('name', models.CharField(max_length=100, verbose_name='Name')),
],
@@ -91,7 +89,7 @@ class Migration(migrations.Migration):
'ordering': ['name'],
'verbose_name_plural': 'Exercise Categories',
},
bases=(models.Model, ),
bases=(models.Model,),
),
migrations.CreateModel(
name='ExerciseComment',
@@ -100,15 +98,15 @@ class Migration(migrations.Migration):
'id',
models.AutoField(
verbose_name='ID', serialize=False, auto_created=True, primary_key=True
)
),
),
(
'comment',
models.CharField(
help_text='A comment about how to correctly do this exercise.',
max_length=200,
verbose_name='Comment'
)
verbose_name='Comment',
),
),
(
'exercise',
@@ -116,12 +114,12 @@ class Migration(migrations.Migration):
editable=False,
to='exercises.Exercise',
verbose_name='Exercise',
on_delete=models.CASCADE
)
on_delete=models.CASCADE,
),
),
],
options={},
bases=(models.Model, ),
bases=(models.Model,),
),
migrations.CreateModel(
name='ExerciseImage',
@@ -130,18 +128,17 @@ class Migration(migrations.Migration):
'id',
models.AutoField(
verbose_name='ID', serialize=False, auto_created=True, primary_key=True
)
),
),
(
'license_author',
models.CharField(
help_text=
'If you are not the author, enter the name or source here. This is needed for some licenses e.g. the CC-BY-SA.',
help_text='If you are not the author, enter the name or source here. This is needed for some licenses e.g. the CC-BY-SA.',
max_length=50,
null=True,
verbose_name='Author',
blank=True
)
blank=True,
),
),
(
'status',
@@ -149,31 +146,30 @@ class Migration(migrations.Migration):
default=b'1',
max_length=2,
editable=False,
choices=[(b'1', 'Pending'), (b'2', 'Accepted'), (b'3', 'Declined')]
)
choices=[(b'1', 'Pending'), (b'2', 'Accepted'), (b'3', 'Declined')],
),
),
(
'image',
models.ImageField(
help_text='Only PNG and JPEG formats are supported',
upload_to=wger.exercises.models.image.exercise_image_upload_dir,
verbose_name='Image'
)
verbose_name='Image',
),
),
(
'is_main',
models.BooleanField(
default=False,
help_text=
'Tick the box if you want to set this image as the main one for the exercise (will be shown e.g. in the search). The first image is automatically marked by the system.',
verbose_name='Is main picture'
)
help_text='Tick the box if you want to set this image as the main one for the exercise (will be shown e.g. in the search). The first image is automatically marked by the system.',
verbose_name='Is main picture',
),
),
(
'exercise',
models.ForeignKey(
verbose_name='Exercise', to='exercises.Exercise', on_delete=models.CASCADE
)
),
),
(
'license',
@@ -181,14 +177,14 @@ class Migration(migrations.Migration):
default=2,
verbose_name='License',
to='core.License',
on_delete=models.CASCADE
)
on_delete=models.CASCADE,
),
),
],
options={
'ordering': ['-is_main', 'id'],
},
bases=(models.Model, ),
bases=(models.Model,),
),
migrations.CreateModel(
name='Muscle',
@@ -197,22 +193,22 @@ class Migration(migrations.Migration):
'id',
models.AutoField(
verbose_name='ID', serialize=False, auto_created=True, primary_key=True
)
),
),
(
'name',
models.CharField(
help_text='In latin, e.g. "Pectoralis major"',
max_length=50,
verbose_name='Name'
)
verbose_name='Name',
),
),
('is_front', models.BooleanField(default=1)),
],
options={
'ordering': ['name'],
},
bases=(models.Model, ),
bases=(models.Model,),
),
migrations.AddField(
model_name='exercise',
@@ -262,7 +258,7 @@ class Migration(migrations.Migration):
null=True,
verbose_name='Secondary muscles',
to='exercises.Muscle',
blank=True
blank=True,
),
preserve_default=True,
),

View File

@@ -11,14 +11,13 @@ def generate_uuids(apps, schema_editor):
:param schema_editor:
:return:
"""
Excercise = apps.get_model("exercises", "Exercise")
Excercise = apps.get_model('exercises', 'Exercise')
for exercise in Excercise.objects.all():
exercise.uuid = uuid.uuid4()
exercise.save()
class Migration(migrations.Migration):
dependencies = [
('exercises', '0001_initial'),
]

View File

@@ -8,7 +8,7 @@ def copy_name(apps, schema_editor):
"""
Copies the exercise name to the original name field
"""
Excercise = apps.get_model("exercises", "Exercise")
Excercise = apps.get_model('exercises', 'Exercise')
for exercise in Excercise.objects.all():
exercise.name_original = exercise.name
exercise.save()
@@ -31,14 +31,13 @@ def capitalize_name(apps, schema_editor):
out.append(word)
return ' '.join(out)
Excercise = apps.get_model("exercises", "Exercise")
Excercise = apps.get_model('exercises', 'Exercise')
for exercise in Excercise.objects.all():
exercise.name = capitalize(exercise.name_original)
exercise.save()
class Migration(migrations.Migration):
dependencies = [
('exercises', '0002_auto_20150307_1841'),
]

View File

@@ -6,7 +6,6 @@ import uuid
class Migration(migrations.Migration):
dependencies = [
('exercises', '0003_auto_20160921_2000'),
]
@@ -33,7 +32,7 @@ class Migration(migrations.Migration):
blank=True,
related_name='secondary_muscles',
to='exercises.Muscle',
verbose_name='Secondary muscles'
verbose_name='Secondary muscles',
),
),
migrations.AlterField(
@@ -43,7 +42,7 @@ class Migration(migrations.Migration):
choices=[('1', 'Pending'), ('2', 'Accepted'), ('3', 'Declined')],
default='1',
editable=False,
max_length=2
max_length=2,
),
),
migrations.AlterField(
@@ -56,9 +55,8 @@ class Migration(migrations.Migration):
name='is_main',
field=models.BooleanField(
default=False,
help_text=
'Tick the box if you want to set this image as the main one for the exercise (will be shown e.g. in the search). The first image is automatically marked by the system.',
verbose_name='Main picture'
help_text='Tick the box if you want to set this image as the main one for the exercise (will be shown e.g. in the search). The first image is automatically marked by the system.',
verbose_name='Main picture',
),
),
migrations.AlterField(
@@ -68,7 +66,7 @@ class Migration(migrations.Migration):
choices=[('1', 'Pending'), ('2', 'Accepted'), ('3', 'Declined')],
default='1',
editable=False,
max_length=2
max_length=2,
),
),
]

View File

@@ -5,7 +5,6 @@ from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('exercises', '0004_auto_20170404_0114'),
]
@@ -13,16 +12,10 @@ class Migration(migrations.Migration):
operations = [
migrations.AlterModelOptions(
name='exercise',
options={
'base_manager_name': 'objects',
'ordering': ['name']
},
options={'base_manager_name': 'objects', 'ordering': ['name']},
),
migrations.AlterModelOptions(
name='exerciseimage',
options={
'base_manager_name': 'objects',
'ordering': ['-is_main', 'id']
},
options={'base_manager_name': 'objects', 'ordering': ['-is_main', 'id']},
),
]

Some files were not shown because too many files have changed in this diff Show More