diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 34bcb9a11d..5c051880f6 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -28,4 +28,4 @@ Please state your operating system, the RDMO version, and (if applicable) the br ### References / Verweise -* +* diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index c5d2e92343..e9f639996d 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -11,7 +11,7 @@ assignees: '' A clear and concise description of what the problem is, followed by the solution you'd like. -### Affected +### Affected Who is affected by the change (Users, Managers, Admins)? @@ -25,4 +25,4 @@ What sort of related functionality would you like to see in addition? ### References / Verweise -* +* diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml index eaf1ae0757..34440010df 100644 --- a/.github/workflows/pytest.yml +++ b/.github/workflows/pytest.yml @@ -36,7 +36,6 @@ jobs: key: lint-${{ hashFiles('.pre-commit-config.yaml') }} - name: Run ruff via pre-commit run: pre-commit run --all-files --color=always - continue-on-error: true # TODO: remove once files are reformatted test: runs-on: ubuntu-22.04 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 568b78cc29..6c10207c3d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -17,7 +17,9 @@ repos: exclude: error\.xml$ - id: check-yaml - id: end-of-file-fixer + exclude: \.html$|\.txt$ - id: trailing-whitespace + exclude: \.dot$ - id: debug-statements - repo: https://github.com/charliermarsh/ruff-pre-commit rev: v0.0.284 diff --git a/CHANGELOG.md b/CHANGELOG.md index c9e512e9f7..124e0d648a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -100,7 +100,7 @@ * Add account deletion for LDAP users * Fix attribute export * Fix condition resolution when going backwards -* Prevent overlay errors if custom list is used +* Prevent overlay errors if custom list is used * Various fixes ## RDMO 1.6.2 (Nov 03, 2021) diff --git a/LICENSE b/LICENSE index 6b0b1270ff..d645695673 100644 --- a/LICENSE +++ b/LICENSE @@ -200,4 +200,3 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. - diff --git a/conftest.py b/conftest.py index 0d621acf63..b62eb83355 100644 --- a/conftest.py +++ b/conftest.py @@ -3,6 +3,7 @@ from pathlib import Path import pytest + from django.conf import settings from django.core.management import call_command diff --git a/pyproject.toml b/pyproject.toml index 347e6e998a..f90c529851 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -137,6 +137,7 @@ select = [ ignore = [ "B006", # mutable-argument-default "B007", # unused-loop-control-variable + "B018", # useless-expression "RUF012", # mutable-class-default ] @@ -162,14 +163,20 @@ rest_framework = ["rest_framework"] "rdmo/**/models/__init__.py" = [ "F401", # unused-import ] +"rdmo/**/serializers/v1/__init__.py" = [ + "F401", # unused-import +] "rdmo/**/views/__init__.py" = [ "F401", # unused-import ] -# Ignore certain rules for tests, e.g. usage of assert is allowed "rdmo/**/tests/test_*.py" = [ "S101", # assert "S106", # hardcoded-password-func-arg ] +"testing/config/settings/__init__.py" = [ + "F401", # unused-import + "F403", # undefined-names +] [tool.pytest.ini_options] DJANGO_SETTINGS_MODULE = "config.settings" diff --git a/rdmo/accounts/adapter.py b/rdmo/accounts/adapter.py index a54cae687c..ccc7bf4d25 100644 --- a/rdmo/accounts/adapter.py +++ b/rdmo/accounts/adapter.py @@ -1,6 +1,7 @@ +from django.conf import settings + from allauth.account.adapter import DefaultAccountAdapter from allauth.socialaccount.adapter import DefaultSocialAccountAdapter -from django.conf import settings class AccountAdapter(DefaultAccountAdapter): diff --git a/rdmo/accounts/forms.py b/rdmo/accounts/forms.py index 63fd10ceea..e8e5f3df61 100644 --- a/rdmo/accounts/forms.py +++ b/rdmo/accounts/forms.py @@ -22,7 +22,7 @@ class Meta: fields = ('first_name', 'last_name', 'email') def __init__(self, *args, **kwargs): - super(ProfileForm, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) self.fields['first_name'].widget = forms.TextInput(attrs={'placeholder': _('First name')}) self.fields['last_name'].widget = forms.TextInput(attrs={'placeholder': _('Last name')}) @@ -50,7 +50,7 @@ def __init__(self, *args, **kwargs): self.fields[additional_field.key].initial = additional_field_value.value def save(self, *args, **kwargs): - super(ProfileForm, self).save(*args, **kwargs) + super().save(*args, **kwargs) self._save_additional_values() def _save_additional_values(self, user=None): @@ -72,7 +72,7 @@ class SignupForm(ProfileForm): use_required_attribute = False def __init__(self, *args, **kwargs): - super(SignupForm, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) # add a consent field, the label is added in the template if settings.ACCOUNT_TERMS_OF_USE: diff --git a/rdmo/accounts/models.py b/rdmo/accounts/models.py index 9d9c097a76..226b81f6af 100644 --- a/rdmo/accounts/models.py +++ b/rdmo/accounts/models.py @@ -4,6 +4,7 @@ from django.db.models.signals import post_save from django.dispatch import receiver from django.utils.translation import gettext_lazy as _ + from rdmo.core.models import TranslationMixin diff --git a/rdmo/accounts/serializers/v1.py b/rdmo/accounts/serializers/v1.py index ed9e627b6a..cd7018c4a9 100644 --- a/rdmo/accounts/serializers/v1.py +++ b/rdmo/accounts/serializers/v1.py @@ -2,6 +2,7 @@ from django.contrib.auth import get_user_model from django.contrib.auth.models import Group from django.contrib.sites.models import Site + from rest_framework import serializers from rdmo.projects.models import Membership diff --git a/rdmo/accounts/settings.py b/rdmo/accounts/settings.py index bf92651f87..f978d8fe46 100644 --- a/rdmo/accounts/settings.py +++ b/rdmo/accounts/settings.py @@ -5,7 +5,7 @@ user_view_permission = ( auth_app, auth_model, - 'view_{}'.format(auth_model) + f'view_{auth_model}' ) GROUPS = ( diff --git a/rdmo/accounts/templatetags/accounts_tags.py b/rdmo/accounts/templatetags/accounts_tags.py index 82d08eb6a8..6cadb0d25b 100644 --- a/rdmo/accounts/templatetags/accounts_tags.py +++ b/rdmo/accounts/templatetags/accounts_tags.py @@ -1,6 +1,6 @@ from django import template -from django.utils.translation import gettext_lazy as _ from django.utils.safestring import mark_safe +from django.utils.translation import gettext_lazy as _ from ..utils import get_full_name @@ -15,14 +15,11 @@ def full_name(user): @register.simple_tag() def user_data_as_dl(user): html = '
' - html += '
%(key)s
%(value)s
' % { - 'key': _('Name'), - 'value': get_full_name(user) - } + html += '
{key}
{value}
'.format( + key=_('Name'), + value=get_full_name(user), + ) for additional_value in user.additional_values.all(): - html += '
%(key)s
%(value)s
' % { - 'key': additional_value.field.text, - 'value': additional_value.value - } + html += f'
{additional_value.field.text}
{additional_value.value}
' html += '
' return mark_safe(html) diff --git a/rdmo/accounts/tests/test_admin.py b/rdmo/accounts/tests/test_admin.py index c5d51dc50b..4f1a25eb5d 100644 --- a/rdmo/accounts/tests/test_admin.py +++ b/rdmo/accounts/tests/test_admin.py @@ -2,7 +2,6 @@ from django.urls import reverse - roles = ('member', 'manager', 'editor', 'reviewer') diff --git a/rdmo/accounts/tests/test_utils.py b/rdmo/accounts/tests/test_utils.py index 706f23166b..08a7c51220 100644 --- a/rdmo/accounts/tests/test_utils.py +++ b/rdmo/accounts/tests/test_utils.py @@ -1,10 +1,10 @@ import pytest + from django.contrib.auth import get_user_model from django.contrib.auth.models import AnonymousUser from rdmo.accounts.models import Role -from rdmo.accounts.utils import get_full_name, is_site_manager, delete_user, get_user_from_db_or_none - +from rdmo.accounts.utils import delete_user, get_full_name, get_user_from_db_or_none, is_site_manager normal_users = ( ('user', 'user', 'user@example.com'), @@ -55,34 +55,33 @@ def test_is_site_manager_returns_false_when_role_doesnotexist_(db, client, usern @pytest.mark.parametrize('username,password,email', users) -def test_delete_user_returns_true(db, username, password, email): +def test_delete_user(db, username, password, email): user = get_user_model().objects.get(username=username, email=email) assert delete_user(user=user, email=email, password=password) is True @pytest.mark.parametrize('username,password,email', users) -def test_delete_user_returns_false_when_user_or_email_is_none(db, username, password, email): +def test_delete_user_when_user_or_email_is_none(db, username, password, email): user = get_user_model().objects.get(username=username, email=email) for test_user, test_email in ((user, None), (None, email), (None, None)): assert delete_user(user=test_user, email=test_email) is False @pytest.mark.parametrize('username,password,email', users) -def test_delete_user_returns_false_when_user_is_not_equal_to_db_user(db, username, password, email): +def test_delete_user_when_user_is_not_equal_to_db_user(db, username, password, email): user = get_user_model().objects.get(username=username, email=email) user.pk += 1 assert delete_user(user=user, email=email, password=None) is False - @pytest.mark.parametrize('username,password,email', users) -def test_delete_user_returns_false_when_user_with_usable_password_gives_none_for_password(db, username, password, email): +def test_delete_user_when_user_with_usable_password_gives_none_for_password(db, username, password, email): user = get_user_model().objects.get(username=username, email=email) assert delete_user(user=user, email=email, password=None) is False @pytest.mark.parametrize('username,password,email', users) -def test_delete_user_returns_false_when_delete_user_raises_an_exception(db, username, password, email): +def test_delete_user_when_delete_user_raises_an_exception(db, username, password, email): user = get_user_model().objects.get(username=username, email=email) def _delete(): raise ValueError user.delete = _delete @@ -90,7 +89,7 @@ def _delete(): raise ValueError @pytest.mark.parametrize('username,password,email', users) -def test_delete_user_returns_false_when_delete_is_called_for_user_without_usable_password_and_raises_an_exception(db, username, password, email): +def test_delete_user_without_usable_password_and_raises_an_exception(db, username, password, email): user = get_user_model().objects.get(username=username, email=email) user.set_unusable_password() def _delete(): raise ValueError diff --git a/rdmo/accounts/tests/test_views.py b/rdmo/accounts/tests/test_views.py index a0b9dc2d86..955a68c4b2 100644 --- a/rdmo/accounts/tests/test_views.py +++ b/rdmo/accounts/tests/test_views.py @@ -8,8 +8,7 @@ from django.urls import reverse from django.urls.exceptions import NoReverseMatch -from pytest_django.asserts import assertTemplateUsed, assertRedirects, \ - assertContains, assertNotContains, assertURLEqual +from pytest_django.asserts import assertContains, assertNotContains, assertRedirects, assertTemplateUsed, assertURLEqual from rdmo.accounts.tests.utils import reload_urls @@ -318,8 +317,8 @@ def test_password_reset_post_valid(db, client, settings, account): assert len(mail.outbox) == 1 # get the link from the mail - urls = re.findall(r'http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+', mail.outbox[0].body) # complicated regex - # urls = [i.strip() for i in mail.outbox[0].body.splitlines() if i.strip().startswith('http')] # simpler alternative + complicated_regex = r'http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+' + urls = re.findall(complicated_regex, mail.outbox[0].body) assert len(urls) == 1 # get the password_reset page @@ -672,7 +671,7 @@ def test_token_post_for_anonymous(db, client, settings, account_allow_user_token settings.ACCOUNT_ALLOW_USER_TOKEN = account_allow_user_token reload_urls('accounts') client.login(username='anonymous', password=None) - # breakpoint() + if settings.ACCOUNT_ALLOW_USER_TOKEN: url = reverse('account_token') response = client.post(url) @@ -692,7 +691,8 @@ def test_home_login_form(db, client, settings, login_form, username, password): client.login(username='anonymous', password=None) url = reverse('home') response = client.get(url) - response.status_code == 200 + + assert response.status_code == 200 if settings.LOGIN_FORM: assertContains(response, f'
') +# if settings.SHIBBOLETH and settings.SHIBBOLETH_LOGIN_URL: +# print(settings.SHIBBOLETH, settings.SHIBBOLETH_LOGIN_URL) +# # Anyonymous user is redirected to login +# assert response.status_code == 302 - # Redirected user logs in - client.login(username=username, password=password) - response = client.get(response) +# assertRedirects(response, reverse('account_login') + '?next=' + reverse('projects')) - if password: - # Logged in user lands on projects - response.status_code == 200 - else: - # Anonymous user is redirected to shibboleth login - response.status_code == 404 +# response = client.get(response.url) + +# # Shibboleth login is shown +# assert response.status_code == 200 +# assertContains(response, 'href="/account/shibboleth/login/">') + +# # Redirected user logs in +# client.login(username=username, password=password) +# response = client.get(response) + +# if password: +# # Logged in user lands on projects +# assert response.status_code == 200 +# else: +# # Anonymous user is redirected to shibboleth login +# assert response.status_code == 404 @pytest.mark.parametrize('shibboleth', boolean_toggle) diff --git a/rdmo/accounts/tests/test_viewsets.py b/rdmo/accounts/tests/test_viewsets.py index 10fd0369f2..1ce0b5058e 100644 --- a/rdmo/accounts/tests/test_viewsets.py +++ b/rdmo/accounts/tests/test_viewsets.py @@ -1,4 +1,5 @@ import pytest + from django.contrib.auth import get_user_model from django.urls import reverse diff --git a/rdmo/accounts/tests/utils.py b/rdmo/accounts/tests/utils.py index a5c328d263..b9f796e2d7 100644 --- a/rdmo/accounts/tests/utils.py +++ b/rdmo/accounts/tests/utils.py @@ -1,6 +1,6 @@ import sys - -from importlib import reload, import_module +from importlib import import_module, reload +from typing import Optional from django.urls import clear_url_caches @@ -21,7 +21,7 @@ def reload_urlconf(urlconf=None, settings=None): import_module(urlconf) -def reload_urls(app_name: str = None, settings=None) -> None: +def reload_urls(app_name: Optional[str] = None, settings=None) -> None: # reload the urlconf of the app if app_name is not None: reload_urlconf(urlconf=f'rdmo.{app_name}.urls', settings=settings) diff --git a/rdmo/accounts/urls/__init__.py b/rdmo/accounts/urls/__init__.py index 13a739c451..aaa1354629 100644 --- a/rdmo/accounts/urls/__init__.py +++ b/rdmo/accounts/urls/__init__.py @@ -2,8 +2,7 @@ from django.contrib.auth import views as auth_views from django.urls import include, re_path -from ..views import (profile_update, remove_user, shibboleth_login, - shibboleth_logout, terms_of_use, token) +from ..views import profile_update, remove_user, shibboleth_login, shibboleth_logout, terms_of_use, token urlpatterns = [ # edit own profile @@ -18,9 +17,12 @@ if settings.SHIBBOLETH: urlpatterns += [ - re_path('^shibboleth/login/', shibboleth_login, name='shibboleth_login'), - re_path('^shibboleth/logout/', shibboleth_logout, name='shibboleth_logout'), - re_path('^logout/', auth_views.LogoutView.as_view(next_page=settings.SHIBBOLETH_LOGOUT_URL), name='account_logout'), + re_path('^shibboleth/login/', + shibboleth_login, name='shibboleth_login'), + re_path('^shibboleth/logout/', + shibboleth_logout, name='shibboleth_logout'), + re_path('^logout/', + auth_views.LogoutView.as_view(next_page=settings.SHIBBOLETH_LOGOUT_URL), name='account_logout'), ] if settings.ACCOUNT or settings.SOCIALACCOUNT: @@ -30,8 +32,10 @@ ] else: urlpatterns += [ - re_path('^login/', auth_views.LoginView.as_view(template_name='account/login.html'), name='account_login'), - re_path('^logout/', auth_views.LogoutView.as_view(next_page=settings.LOGIN_REDIRECT_URL), name='account_logout'), + re_path('^login/', + auth_views.LoginView.as_view(template_name='account/login.html'), name='account_login'), + re_path('^logout/', + auth_views.LogoutView.as_view(next_page=settings.LOGIN_REDIRECT_URL), name='account_logout'), ] diff --git a/rdmo/accounts/urls/v1.py b/rdmo/accounts/urls/v1.py index 854cf8205d..deb5ae9050 100644 --- a/rdmo/accounts/urls/v1.py +++ b/rdmo/accounts/urls/v1.py @@ -1,4 +1,5 @@ from django.urls import include, path + from rest_framework import routers from ..viewsets import UserViewSet diff --git a/rdmo/accounts/utils.py b/rdmo/accounts/utils.py index 10cd76acfb..b200f64d18 100644 --- a/rdmo/accounts/utils.py +++ b/rdmo/accounts/utils.py @@ -14,7 +14,7 @@ def get_full_name(user): if user.first_name and user.last_name: - return '%s %s' % (user.first_name, user.last_name) + return f'{user.first_name} {user.last_name}' else: return user.username @@ -55,7 +55,8 @@ def delete_user(user=None, email=None, password=None): return False if user != database_user: - log.info('Deletion of user "%s" failed, the user from request (pk=%s) and database (pk=%s) differ.', username, user.pk, database_user.pk) + log.info('Deletion of user "%s" failed, the user from request (pk=%s) and database (pk=%s) differ.', + username, user.pk, database_user.pk) return False if user.has_usable_password() and password is not None: @@ -68,7 +69,8 @@ def delete_user(user=None, email=None, password=None): log.info('Deletion of user with usable password "%s" succeeded.', username) return True except Exception as e: - log.error('Deletion of user with usable password "%s" failed, an exception (%s) occured', (str(e), username)) + log.error('Deletion of user with usable password "%s" failed, an exception (%s) occured', + str(e), username) return False elif not user.has_usable_password() and password is None: try: @@ -76,7 +78,8 @@ def delete_user(user=None, email=None, password=None): log.info('Deletion of user without usable password "%s" succeeded.', username) return True except Exception as e: - log.error('Deletion of user without usable password "%s" failed, an exception (%s) occured', (str(e), username)) + log.error('Deletion of user without usable password "%s" failed, an exception (%s) occured', + str(e), username) return False else: log.info('Deletion of user "%s" failed, probably wrong value for password given', username) diff --git a/rdmo/accounts/views.py b/rdmo/accounts/views.py index eeebe58123..7ed194997a 100644 --- a/rdmo/accounts/views.py +++ b/rdmo/accounts/views.py @@ -7,6 +7,7 @@ from django.http import HttpResponseRedirect from django.shortcuts import render from django.urls import reverse + from rest_framework.authtoken.models import Token from rdmo.core.utils import get_next, get_referer_path_info diff --git a/rdmo/accounts/viewsets.py b/rdmo/accounts/viewsets.py index baa733cb85..b377dd08df 100644 --- a/rdmo/accounts/viewsets.py +++ b/rdmo/accounts/viewsets.py @@ -1,16 +1,16 @@ from django.contrib.auth import get_user_model -from django_filters.rest_framework import DjangoFilterBackend + from rest_framework.viewsets import ReadOnlyModelViewSet +from django_filters.rest_framework import DjangoFilterBackend + from rdmo.core.permissions import HasModelPermission, HasObjectPermission -from rdmo.management.rules import is_editor, is_reviewer -from .models import Role from .serializers.v1 import UserSerializer from .utils import is_site_manager -class UserViewSetMixin(object): +class UserViewSetMixin: def get_users_for_user(self, user): if user.is_authenticated: diff --git a/rdmo/conditions/imports.py b/rdmo/conditions/imports.py index 809dd6ea6a..f9f5f513f1 100644 --- a/rdmo/conditions/imports.py +++ b/rdmo/conditions/imports.py @@ -2,8 +2,7 @@ from django.contrib.sites.models import Site -from rdmo.core.imports import (check_permissions, set_common_fields, - set_foreign_field, validate_instance) +from rdmo.core.imports import check_permissions, set_common_fields, set_foreign_field, validate_instance from .models import Condition from .validators import ConditionLockedValidator, ConditionUniqueURIValidator diff --git a/rdmo/conditions/models.py b/rdmo/conditions/models.py index e1a3303294..3dcfd73da0 100644 --- a/rdmo/conditions/models.py +++ b/rdmo/conditions/models.py @@ -3,7 +3,7 @@ from django.db import models from django.utils.translation import gettext_lazy as _ -from rdmo.core.utils import copy_model, join_url +from rdmo.core.utils import join_url from rdmo.domain.models import Attribute @@ -61,7 +61,8 @@ class Condition(models.Model): help_text=_('The sites that can edit this condition (in a multi site setup).') ) source = models.ForeignKey( - Attribute, db_constraint=False, blank=True, null=True, on_delete=models.SET_NULL, related_name='conditions', + Attribute, blank=True, null=True, on_delete=models.SET_NULL, related_name='conditions', + db_constraint=False, verbose_name=_('Source'), help_text=_('The attribute of the value for this condition.') ) @@ -73,10 +74,12 @@ class Condition(models.Model): target_text = models.CharField( max_length=256, blank=True, verbose_name=_('Target (Text)'), - help_text=_('If using a regular value, the text value this condition is checking against (for boolean values use 1 and 0).') + help_text=_('If using a regular value, the text value this condition is checking against ' + '(for boolean values use 1 and 0).') ) target_option = models.ForeignKey( - 'options.Option', db_constraint=False, blank=True, null=True, on_delete=models.SET_NULL, related_name='conditions', + 'options.Option', blank=True, null=True, on_delete=models.SET_NULL, related_name='conditions', + db_constraint=False, verbose_name=_('Target (Option)'), help_text=_('If using a value pointing to an option, the option this condition is checking against.') ) diff --git a/rdmo/conditions/serializers/v1.py b/rdmo/conditions/serializers/v1.py index 6723584afb..6911b9285b 100644 --- a/rdmo/conditions/serializers/v1.py +++ b/rdmo/conditions/serializers/v1.py @@ -1,7 +1,6 @@ from rest_framework import serializers -from rdmo.core.serializers import (ElementModelSerializerMixin, - ReadOnlyObjectPermissionSerializerMixin) +from rdmo.core.serializers import ElementModelSerializerMixin, ReadOnlyObjectPermissionSerializerMixin from rdmo.domain.models import Attribute from rdmo.options.models import OptionSet from rdmo.questions.models import Page, Question, QuestionSet diff --git a/rdmo/conditions/tests/test_validator_locked.py b/rdmo/conditions/tests/test_validator_locked.py index 582e69746c..f35cfe8976 100644 --- a/rdmo/conditions/tests/test_validator_locked.py +++ b/rdmo/conditions/tests/test_validator_locked.py @@ -1,7 +1,8 @@ import pytest + from django.core.exceptions import ValidationError -from rest_framework.exceptions import \ - ValidationError as RestFameworkValidationError + +from rest_framework.exceptions import ValidationError as RestFameworkValidationError from ..models import Condition from ..serializers.v1 import ConditionSerializer diff --git a/rdmo/conditions/tests/test_validator_unique_uri.py b/rdmo/conditions/tests/test_validator_unique_uri.py index f05dae536e..a0b203f160 100644 --- a/rdmo/conditions/tests/test_validator_unique_uri.py +++ b/rdmo/conditions/tests/test_validator_unique_uri.py @@ -1,8 +1,9 @@ import pytest + from django.conf import settings from django.core.exceptions import ValidationError -from rest_framework.exceptions import \ - ValidationError as RestFameworkValidationError + +from rest_framework.exceptions import ValidationError as RestFameworkValidationError from ..models import Condition from ..serializers.v1 import ConditionSerializer diff --git a/rdmo/conditions/tests/test_viewset_condition.py b/rdmo/conditions/tests/test_viewset_condition.py index 94740e8a03..92d15cf6c4 100644 --- a/rdmo/conditions/tests/test_viewset_condition.py +++ b/rdmo/conditions/tests/test_viewset_condition.py @@ -1,6 +1,7 @@ import xml.etree.ElementTree as et import pytest + from django.urls import reverse from ..models import Condition @@ -97,7 +98,7 @@ def test_create(db, client, username, password): url = reverse(urlnames['list']) data = { 'uri_prefix': instance.uri_prefix, - 'uri_path': '%s_new_%s' % (instance.uri_path, username), + 'uri_path': f'{instance.uri_path}_new_{username}', 'comment': instance.comment, 'source': instance.source.pk, 'relation': instance.relation, @@ -119,7 +120,7 @@ def test_create_optionset(db, client, username, password): url = reverse(urlnames['list']) data = { 'uri_prefix': instance.uri_prefix, - 'uri_path': '%s_new_%s' % (instance.uri_path, username), + 'uri_path': f'{instance.uri_path}_new_{username}', 'comment': instance.comment, 'source': instance.source.pk, 'relation': instance.relation, @@ -146,7 +147,7 @@ def test_create_page(db, client, username, password): url = reverse(urlnames['list']) data = { 'uri_prefix': instance.uri_prefix, - 'uri_path': '%s_new_%s' % (instance.uri_path, username), + 'uri_path': f'{instance.uri_path}_new_{username}', 'comment': instance.comment, 'source': instance.source.pk, 'relation': instance.relation, @@ -173,7 +174,7 @@ def test_create_questionset(db, client, username, password): url = reverse(urlnames['list']) data = { 'uri_prefix': instance.uri_prefix, - 'uri_path': '%s_new_%s' % (instance.uri_path, username), + 'uri_path': f'{instance.uri_path}_new_{username}', 'comment': instance.comment, 'source': instance.source.pk, 'relation': instance.relation, @@ -200,7 +201,7 @@ def test_create_question(db, client, username, password): url = reverse(urlnames['list']) data = { 'uri_prefix': instance.uri_prefix, - 'uri_path': '%s_new_%s' % (instance.uri_path, username), + 'uri_path': f'{instance.uri_path}_new_{username}', 'comment': instance.comment, 'source': instance.source.pk, 'relation': instance.relation, @@ -227,7 +228,7 @@ def test_create_task(db, client, username, password): url = reverse(urlnames['list']) data = { 'uri_prefix': instance.uri_prefix, - 'uri_path': '%s_new_%s' % (instance.uri_path, username), + 'uri_path': f'{instance.uri_path}_new_{username}', 'comment': instance.comment, 'source': instance.source.pk, 'relation': instance.relation, diff --git a/rdmo/conditions/tests/test_viewset_condition_multisite.py b/rdmo/conditions/tests/test_viewset_condition_multisite.py index cbb8f59806..0455e1c22a 100644 --- a/rdmo/conditions/tests/test_viewset_condition_multisite.py +++ b/rdmo/conditions/tests/test_viewset_condition_multisite.py @@ -1,17 +1,14 @@ import xml.etree.ElementTree as et import pytest -from django.urls import reverse - -from ..models import Condition -from .test_viewset_condition import export_formats +from django.urls import reverse +from ...core.tests import get_obj_perms_status_code from ...core.tests import multisite_status_map as status_map from ...core.tests import multisite_users as users -from ...core.tests import get_obj_perms_status_code - -from .test_viewset_condition import urlnames +from ..models import Condition +from .test_viewset_condition import export_formats, urlnames @pytest.mark.parametrize('username,password', users) @@ -68,7 +65,7 @@ def test_create(db, client, username, password): url = reverse(urlnames['list']) data = { 'uri_prefix': instance.uri_prefix, - 'uri_path': '%s_new_%s' % (instance.uri_path, username), + 'uri_path': f'{instance.uri_path}_new_{username}', 'comment': instance.comment, 'source': instance.source.pk, 'relation': instance.relation, @@ -90,7 +87,7 @@ def test_create_optionset(db, client, username, password): url = reverse(urlnames['list']) data = { 'uri_prefix': instance.uri_prefix, - 'uri_path': '%s_new_%s' % (instance.uri_path, username), + 'uri_path': f'{instance.uri_path}_new_{username}', 'comment': instance.comment, 'source': instance.source.pk, 'relation': instance.relation, @@ -117,7 +114,7 @@ def test_create_page(db, client, username, password): url = reverse(urlnames['list']) data = { 'uri_prefix': instance.uri_prefix, - 'uri_path': '%s_new_%s' % (instance.uri_path, username), + 'uri_path': f'{instance.uri_path}_new_{username}', 'comment': instance.comment, 'source': instance.source.pk, 'relation': instance.relation, @@ -144,7 +141,7 @@ def test_create_questionset(db, client, username, password): url = reverse(urlnames['list']) data = { 'uri_prefix': instance.uri_prefix, - 'uri_path': '%s_new_%s' % (instance.uri_path, username), + 'uri_path': f'{instance.uri_path}_new_{username}', 'comment': instance.comment, 'source': instance.source.pk, 'relation': instance.relation, @@ -171,7 +168,7 @@ def test_create_question(db, client, username, password): url = reverse(urlnames['list']) data = { 'uri_prefix': instance.uri_prefix, - 'uri_path': '%s_new_%s' % (instance.uri_path, username), + 'uri_path': f'{instance.uri_path}_new_{username}', 'comment': instance.comment, 'source': instance.source.pk, 'relation': instance.relation, @@ -198,7 +195,7 @@ def test_create_task(db, client, username, password): url = reverse(urlnames['list']) data = { 'uri_prefix': instance.uri_prefix, - 'uri_path': '%s_new_%s' % (instance.uri_path, username), + 'uri_path': f'{instance.uri_path}_new_{username}', 'comment': instance.comment, 'source': instance.source.pk, 'relation': instance.relation, diff --git a/rdmo/conditions/tests/test_viewset_relation.py b/rdmo/conditions/tests/test_viewset_relation.py index 7ff7baefcf..d569367481 100644 --- a/rdmo/conditions/tests/test_viewset_relation.py +++ b/rdmo/conditions/tests/test_viewset_relation.py @@ -1,4 +1,5 @@ import pytest + from django.urls import reverse users = ( diff --git a/rdmo/conditions/viewsets.py b/rdmo/conditions/viewsets.py index fd561ea209..3816b99921 100644 --- a/rdmo/conditions/viewsets.py +++ b/rdmo/conditions/viewsets.py @@ -1,10 +1,11 @@ -from django_filters.rest_framework import DjangoFilterBackend from rest_framework.decorators import action from rest_framework.filters import SearchFilter from rest_framework.permissions import IsAuthenticated from rest_framework.response import Response from rest_framework.viewsets import ModelViewSet +from django_filters.rest_framework import DjangoFilterBackend + from rdmo.core.exports import XMLResponse from rdmo.core.permissions import HasModelPermission, HasObjectPermission from rdmo.core.utils import is_truthy, render_to_format @@ -60,9 +61,11 @@ def detail_export(self, request, pk=None, export_format='xml'): xml = ConditionRenderer().render([serializer.data], context=self.get_export_renderer_context(request)) return XMLResponse(xml, name=self.get_object().uri_path) else: - return render_to_format(self.request, export_format, self.get_object().uri_path, 'conditions/export/conditions.html', { - 'conditions': [self.get_object()] - }) + return render_to_format( + self.request, export_format, self.get_object().uri_path, 'conditions/export/conditions.html', { + 'conditions': [self.get_object()] + } + ) def get_export_renderer_context(self, request): full = is_truthy(request.GET.get('full')) diff --git a/rdmo/core/exports.py b/rdmo/core/exports.py index f3e61a9712..89a4b28ade 100644 --- a/rdmo/core/exports.py +++ b/rdmo/core/exports.py @@ -1,7 +1,6 @@ -from django.conf import settings - from xml.dom.minidom import parseString +from django.conf import settings from django.http import HttpResponse diff --git a/rdmo/core/imports.py b/rdmo/core/imports.py index 4cebb92fc7..587afd54e1 100644 --- a/rdmo/core/imports.py +++ b/rdmo/core/imports.py @@ -6,6 +6,7 @@ from random import randint from django.core.exceptions import ObjectDoesNotExist, ValidationError + from rest_framework.utils import model_meta from rdmo.core.utils import get_languages @@ -48,11 +49,11 @@ def set_common_fields(instance, element): def set_lang_field(instance, field_name, element): for lang_code, lang_string, lang_field in get_languages(): - field = element.get('%s_%s' % (field_name, lang_code)) + field = element.get(f'{field_name}_{lang_code}') if field: - setattr(instance, '%s_%s' % (field_name, lang_field), field) + setattr(instance, f'{field_name}_{lang_field}', field) else: - setattr(instance, '%s_%s' % (field_name, lang_field), '') + setattr(instance, f'{field_name}_{lang_field}', '') def set_foreign_field(instance, field_name, element) -> None: @@ -139,7 +140,7 @@ def set_m2m_through_instances(instance, field_name, element, source_name, target try: # look for the item in items through_instance = next(filter(lambda item: getattr(item, target_name).uri == target_instance.uri, - through_instances)) + through_instances)) # update order if the item if it changed if through_instance.order != order: @@ -224,21 +225,21 @@ def validate_instance(instance, element, *validators): validator(instance if instance.id else None)(vars(instance)) except ValidationError as e: try: - exception_message = '; '.join(['{}: {}'.format(key, ', '.join(messages)) for key, messages in e.message_dict.items()]) + exception_message = '; '.join(['{}: {}'.format(key, ', '.join(messages)) + for key, messages in e.message_dict.items()]) except AttributeError: exception_message = ''.join(e.messages) except ObjectDoesNotExist as e: exception_message = e finally: - if exception_message is None: - return - message = '{instance_model} {instance_uri} cannot be imported ({exception}).'.format( - instance_model=instance._meta.object_name, - instance_uri=element.get('uri'), - exception=exception_message - ) - logger.info(message) - element['errors'].append(message) + if exception_message is not None: + message = '{instance_model} {instance_uri} cannot be imported ({exception}).'.format( + instance_model=instance._meta.object_name, + instance_uri=element.get('uri'), + exception=exception_message + ) + logger.info(message) + element['errors'].append(message) def check_permissions(instance, element, user): diff --git a/rdmo/core/mail.py b/rdmo/core/mail.py index 64c85642fb..d4a210ca10 100644 --- a/rdmo/core/mail.py +++ b/rdmo/core/mail.py @@ -5,7 +5,7 @@ def send_mail(subject, message, from_email=None, to=[], cc=[], bcc=[], reply_to=None, attachments=[]): site = Site.objects.get_current() - subject = '[{}] '.format(site.name) + subject + subject = f'[{site.name}] ' + subject if from_email is None: from_email = settings.DEFAULT_FROM_EMAIL diff --git a/rdmo/core/management/commands/create_admin_user.py b/rdmo/core/management/commands/create_admin_user.py index 487fabf00d..cf110c3399 100644 --- a/rdmo/core/management/commands/create_admin_user.py +++ b/rdmo/core/management/commands/create_admin_user.py @@ -1,5 +1,5 @@ -from django.core.management.base import BaseCommand from django.contrib.auth import get_user_model +from django.core.management.base import BaseCommand class Command(BaseCommand): diff --git a/rdmo/core/management/commands/delete_users.py b/rdmo/core/management/commands/delete_users.py index 493b970ac0..8deda1f4f2 100644 --- a/rdmo/core/management/commands/delete_users.py +++ b/rdmo/core/management/commands/delete_users.py @@ -23,7 +23,7 @@ def add_arguments(self, parser): def make_user_id_list(self, filename): user_ids = [] try: - filecontent = open(filename, 'r') + filecontent = open(filename) except Exception as e: print('Error reading id list file. ' + str(e)) sys.exit(1) diff --git a/rdmo/core/management/commands/download_vendor_files.py b/rdmo/core/management/commands/download_vendor_files.py index fed476e728..2c3e8935f8 100644 --- a/rdmo/core/management/commands/download_vendor_files.py +++ b/rdmo/core/management/commands/download_vendor_files.py @@ -3,10 +3,11 @@ import os import shutil -import requests from django.conf import settings from django.core.management.base import BaseCommand +import requests + class Command(BaseCommand): @@ -23,7 +24,7 @@ def handle(self, *args, **options): if file_type in vendor_conf: for file in vendor_conf[file_type]: # get the directory and the file_name - path_tokens = ['vendor', key] + os.path.normpath(file['path']).split(os.path.sep) + path_tokens = ['vendor', key, *os.path.normpath(file['path']).split(os.path.sep)] directory = os.path.join(*path_tokens[:-1]) file_name = os.path.join(*path_tokens) @@ -36,7 +37,7 @@ def handle(self, *args, **options): # get the full url of the file url = requests.compat.urljoin(vendor_conf['url'], file['path']) - print('%s -> %s' % (url, file_name)) + print(f'{url} -> {file_name}') # fetch the file from the cdn response = requests.get(url) diff --git a/rdmo/core/management/commands/find_inactive_users.py b/rdmo/core/management/commands/find_inactive_users.py index 504d650c8f..e282dd06e2 100644 --- a/rdmo/core/management/commands/find_inactive_users.py +++ b/rdmo/core/management/commands/find_inactive_users.py @@ -1,13 +1,12 @@ -import sys import csv - +import sys from datetime import datetime -import pytz - from django.contrib.auth.models import User from django.core.management.base import BaseCommand +import pytz + class Command(BaseCommand): diff --git a/rdmo/core/management/commands/find_spam_users.py b/rdmo/core/management/commands/find_spam_users.py index 883b00f2b4..236a43964b 100644 --- a/rdmo/core/management/commands/find_spam_users.py +++ b/rdmo/core/management/commands/find_spam_users.py @@ -39,7 +39,7 @@ def save_csv(self, data, filename): print('List written to ' + filename) def print_file(self, filename): - f = open(filename, 'r') + f = open(filename) content = f.read() print(content) f.close() diff --git a/rdmo/core/management/commands/find_users.py b/rdmo/core/management/commands/find_users.py index 2625c5bacc..c679036246 100644 --- a/rdmo/core/management/commands/find_users.py +++ b/rdmo/core/management/commands/find_users.py @@ -48,7 +48,7 @@ def save_csv(self, data, filename): print('List written to ' + filename) def print_file(self, filename): - f = open(filename, 'r') + f = open(filename) content = f.read() print(content) f.close() diff --git a/rdmo/core/management/commands/make_theme.py b/rdmo/core/management/commands/make_theme.py index f71afbd2d2..9dab60d8ec 100644 --- a/rdmo/core/management/commands/make_theme.py +++ b/rdmo/core/management/commands/make_theme.py @@ -1,5 +1,5 @@ -from shutil import copyfile from pathlib import Path +from shutil import copyfile from django.apps import apps from django.conf import settings @@ -19,15 +19,15 @@ def copy(self, path): target_path = self.theme_path / Path(*path.parts[1:]) if target_path.exists(): - print('Skip {} -> {}. Target file exists.'.format(source_path, target_path)) + print(f'Skip {source_path} -> {target_path}. Target file exists.') else: - print('Copy {} -> {}.'.format(source_path, target_path)) + print(f'Copy {source_path} -> {target_path}.') target_path.parent.mkdir(parents=True, exist_ok=True) copyfile(source_path, target_path) def enable_theme(self): - settings_line = 'INSTALLED_APPS = [\'{}\'] + INSTALLED_APPS'.format(self.theme_name) + settings_line = f'INSTALLED_APPS = [\'{self.theme_name}\'] + INSTALLED_APPS' replaced = False local_settings = self.local_path.read_text().splitlines() @@ -48,8 +48,10 @@ def enable_theme(self): self.local_path.write_text('\n'.join(local_settings)) def add_arguments(self, parser): - parser.add_argument('--name', action='store', default='rdmo_theme', help='Module name for the theme.') - parser.add_argument('--file', action='store', help='Copy specific file/template, e.g. core/static/css/variables.scss.') + parser.add_argument('--name', action='store', default='rdmo_theme', + help='Module name for the theme.') + parser.add_argument('--file', action='store', + help='Copy specific file/template, e.g. core/static/css/variables.scss.') def handle(self, *args, **options): self.setup(options) @@ -64,9 +66,9 @@ def handle(self, *args, **options): self.copy(Path('core') / 'static' / 'core' / 'css' / 'variables.scss') for language, language_string in settings.LANGUAGES: - self.copy(Path('core') / 'templates' / 'core' / 'home_text_{}.html'.format(language)) - self.copy(Path('core') / 'templates' / 'core' / 'about_text_{}.html'.format(language)) - self.copy(Path('core') / 'templates' / 'core' / 'footer_text_{}.html'.format(language)) + self.copy(Path('core') / 'templates' / 'core' / f'home_text_{language}.html') + self.copy(Path('core') / 'templates' / 'core' / f'about_text_{language}.html') + self.copy(Path('core') / 'templates' / 'core' / f'footer_text_{language}.html') print('Enable theme by adding the necessary config line.') self.enable_theme() diff --git a/rdmo/core/management/commands/set_uri_prefix.py b/rdmo/core/management/commands/set_uri_prefix.py index 46c5e03a05..8acffb0a1e 100644 --- a/rdmo/core/management/commands/set_uri_prefix.py +++ b/rdmo/core/management/commands/set_uri_prefix.py @@ -1,9 +1,9 @@ from django.core.management.base import BaseCommand from rdmo.conditions.models import Condition -from rdmo.options.models import OptionSet, Option from rdmo.domain.models import Attribute -from rdmo.questions.models import Catalog, Section, Subsection, QuestionSet, Question +from rdmo.options.models import Option, OptionSet +from rdmo.questions.models import Catalog, Question, QuestionSet, Section, Subsection from rdmo.tasks.models import Task from rdmo.views.models import View diff --git a/rdmo/core/managers.py b/rdmo/core/managers.py index 083c91eb06..e1f68ebf70 100644 --- a/rdmo/core/managers.py +++ b/rdmo/core/managers.py @@ -4,20 +4,20 @@ from .constants import PERMISSIONS -class CurrentSiteQuerySetMixin(object): +class CurrentSiteQuerySetMixin: def filter_current_site(self): return self.filter(models.Q(sites=None) | models.Q(sites=settings.SITE_ID)) -class GroupsQuerySetMixin(object): +class GroupsQuerySetMixin: def filter_group(self, user): groups = user.groups.all() return self.filter(models.Q(groups=None) | models.Q(groups__in=groups)) -class AvailabilityQuerySetMixin(object): +class AvailabilityQuerySetMixin: def filter_availability(self, user): model = str(self.model._meta) @@ -29,19 +29,19 @@ def filter_availability(self, user): return self.filter(available=True) -class CurrentSiteManagerMixin(object): +class CurrentSiteManagerMixin: def filter_current_site(self): return self.get_queryset().filter_current_site() -class GroupsManagerMixin(object): +class GroupsManagerMixin: def filter_group(self, user): return self.get_queryset().filter_group(user) -class AvailabilityManagerMixin(object): +class AvailabilityManagerMixin: def filter_availability(self, user): return self.get_queryset().filter_availability(user) diff --git a/rdmo/core/models.py b/rdmo/core/models.py index 9ac75d4cc4..85f7ece3af 100644 --- a/rdmo/core/models.py +++ b/rdmo/core/models.py @@ -25,10 +25,10 @@ def save(self, *args, **kwargs): self.updated = now() - super(Model, self).save(*args, **kwargs) + super().save(*args, **kwargs) -class TranslationMixin(object): +class TranslationMixin: def trans(self, field): current_language = get_language() @@ -36,12 +36,12 @@ def trans(self, field): languages = get_languages() for lang_code, lang_string, lang_field in languages: if lang_code == current_language: - r = getattr(self, '%s_%s' % (field, lang_field)) or None + r = getattr(self, f'{field}_{lang_field}') or None if r is not None: return r elif settings.REPLACE_MISSING_TRANSLATION: for i in range(1, 6): - r = getattr(self, '%s_%s' % (field, 'lang' + str(i))) or None + r = getattr(self, '{}_{}'.format(field, 'lang' + str(i))) or None if r is not None: return r return '' diff --git a/rdmo/core/permissions.py b/rdmo/core/permissions.py index d86bcb1b44..6cdc1b5662 100644 --- a/rdmo/core/permissions.py +++ b/rdmo/core/permissions.py @@ -1,7 +1,6 @@ import logging -from rest_framework.permissions import (DjangoModelPermissions, - DjangoObjectPermissions) +from rest_framework.permissions import DjangoModelPermissions, DjangoObjectPermissions logger = logging.getLogger(__name__) diff --git a/rdmo/core/plugins.py b/rdmo/core/plugins.py index 4b1762d4bb..ce61e2f11a 100644 --- a/rdmo/core/plugins.py +++ b/rdmo/core/plugins.py @@ -3,7 +3,7 @@ from .utils import import_class -class Plugin(object): +class Plugin: def __init__(self, key, label, class_name): self.key = key @@ -21,7 +21,9 @@ def get_plugins(plugin_settings): def get_plugin(plugin_settings, plugin_key): try: key, label, class_name = next( - (key, label, class_name) for key, label, class_name in getattr(settings, plugin_settings) if key == plugin_key + (key, label, class_name) + for key, label, class_name in getattr(settings, plugin_settings) + if key == plugin_key ) return import_class(class_name)(key, label, class_name) except StopIteration: diff --git a/rdmo/core/renderers.py b/rdmo/core/renderers.py index b0503beeb9..987cff907f 100644 --- a/rdmo/core/renderers.py +++ b/rdmo/core/renderers.py @@ -3,6 +3,7 @@ from django.utils.encoding import smart_str from django.utils.timezone import get_current_timezone, now from django.utils.xmlutils import SimplerXMLGenerator + from rest_framework.renderers import BaseRenderer from rdmo import __version__ @@ -32,7 +33,7 @@ def render(self, data, context={}): def render_text_element(self, xml, tag, attrs, text): # remove None values from attrs - attrs = dict((key, value) for key, value in attrs.items() if value) + attrs = {key: value for key, value in attrs.items() if value} xml.startElement(tag, attrs) if text is not None: diff --git a/rdmo/core/serializers.py b/rdmo/core/serializers.py index c1f5e6a28a..1c06cc8a50 100644 --- a/rdmo/core/serializers.py +++ b/rdmo/core/serializers.py @@ -3,8 +3,8 @@ from django.contrib.auth.models import Group from django.contrib.sites.models import Site from django.db.models import Max + from rest_framework import serializers -from rest_framework.reverse import reverse from rest_framework.utils import model_meta from rdmo.core.utils import get_language_warning, get_languages, markdown2html @@ -35,7 +35,7 @@ class MarkdownSerializerMixin(serializers.Serializer): markdown_fields = () def to_representation(self, instance): - response = super(MarkdownSerializerMixin, self).to_representation(instance) + response = super().to_representation(instance) for markdown_field in self.markdown_fields: if markdown_field in response and response[markdown_field]: @@ -44,10 +44,10 @@ def to_representation(self, instance): return response -class TranslationSerializerMixin(object): +class TranslationSerializerMixin: def __init__(self, *args, **kwargs): - super(TranslationSerializerMixin, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) meta = getattr(self, 'Meta', None) if meta is None: @@ -55,17 +55,17 @@ def __init__(self, *args, **kwargs): for lang_code, lang_string, lang_field in get_languages(): for field in meta.trans_fields: - field_name = '%s_%s' % (field, lang_field) + field_name = f'{field}_{lang_field}' model_field = meta.model._meta.get_field(field_name) - self.fields['%s_%s' % (field, lang_code)] = serializers.CharField( + self.fields[f'{field}_{lang_code}'] = serializers.CharField( source=field_name, required=not model_field.blank, allow_null=model_field.null, allow_blank=model_field.blank) -class ThroughModelSerializerMixin(object): +class ThroughModelSerializerMixin: def create(self, validated_data): parent_fields = self.get_parent_fields(validated_data) @@ -113,8 +113,7 @@ def set_through_fields(self, instance, through_fields): for data in validated_data: try: # look for the item in items - item = next(filter(lambda item: getattr(item, target_name) == - data.get(target_name), items)) + item = next(filter(lambda item: getattr(item, target_name) == data.get(target_name), items)) # update order if the item if it changed if item.order != data.get('order'): item.order = data.get('order') @@ -188,7 +187,7 @@ def get_model(self, obj): class ElementWarningSerializerMixin(serializers.ModelSerializer): def get_warning(self, obj): - return any([get_language_warning(obj, field_name) for field_name in self.Meta.warning_fields]) + return any(get_language_warning(obj, field_name) for field_name in self.Meta.warning_fields) class ReadOnlyObjectPermissionSerializerMixin: @@ -213,7 +212,7 @@ class ReadOnlyObjectPermissionSerializerMixin: def construct_object_permission(model, action_name: str) -> str: model_app_label = model._meta.app_label model_name = model._meta.model_name - perm = '%s.%s_%s_object' % (model_app_label, action_name, model_name) + perm = f'{model_app_label}.{action_name}_{model_name}_object' return perm def get_read_only(self, obj) -> bool: diff --git a/rdmo/core/swagger.py b/rdmo/core/swagger.py index a9b43c71fc..32d5e3d747 100644 --- a/rdmo/core/swagger.py +++ b/rdmo/core/swagger.py @@ -3,6 +3,7 @@ from rest_framework.response import Response from rest_framework.schemas import SchemaGenerator from rest_framework.views import APIView + from rest_framework_swagger import renderers from .urls.v1 import urlpatterns diff --git a/rdmo/core/templatetags/core_tags.py b/rdmo/core/templatetags/core_tags.py index bafb865f02..52a094b9ee 100644 --- a/rdmo/core/templatetags/core_tags.py +++ b/rdmo/core/templatetags/core_tags.py @@ -22,9 +22,9 @@ def i18n_switcher(): for language, language_string in settings.LANGUAGES: url = reverse('i18n_switcher', args=[language]) if language == translation.get_language(): - string += "
  • %s
  • " % (url, language_string) + string += f"
  • {language_string}
  • " else: - string += "
  • %s
  • " % (url, language_string) + string += f"
  • {language_string}
  • " return mark_safe(string) @@ -60,34 +60,34 @@ def vendor(vendor_key): if 'js' in vendor_config: for file in vendor_config['js']: if settings.VENDOR_CDN: - tag = '' % { - 'url': vendor_config['url'].rstrip('/'), - 'path': file['path'], - 'sri': file['sri'] if 'sri' in file else '' - } + tag = ''.format( + url=vendor_config['url'].rstrip('/'), + path=file['path'], + sri=file['sri'] if 'sri' in file else '', + ) else: - tag = '' % { - 'static_url': settings.STATIC_URL.rstrip('/'), - 'vendor_key': vendor_key, - 'path': file['path'] - } + tag = ''.format( + static_url=settings.STATIC_URL.rstrip('/'), + vendor_key=vendor_key, + path=file['path'], + ) tags.append(tag) if 'css' in vendor_config: for file in vendor_config['css']: if settings.VENDOR_CDN: - tag = '' % { - 'url': vendor_config['url'].rstrip('/'), - 'path': file['path'], - 'sri': file['sri'] if 'sri' in file else '' - } + tag = ''.format( + url=vendor_config['url'].rstrip('/'), + path=file['path'], + sri=file['sri'] if 'sri' in file else '', + ) else: - tag = '' % { - 'static_url': settings.STATIC_URL.rstrip('/'), - 'vendor_key': vendor_key, - 'path': file['path'] - } + tag = ''.format( + static_url=settings.STATIC_URL.rstrip('/'), + vendor_key=vendor_key, + path=file['path'], + ) tags.append(tag) diff --git a/rdmo/core/tests/__init__.py b/rdmo/core/tests/__init__.py index a71a08afcb..943cba0608 100644 --- a/rdmo/core/tests/__init__.py +++ b/rdmo/core/tests/__init__.py @@ -125,8 +125,8 @@ def get_obj_perms_status_code(instance, username, method): try: if not instance.editors.exists(): return multisite_status_map[method][username] - except AttributeError as exc: - raise AttributeError(f'instance {instance} should have an editors attribute') from exc + except AttributeError as e: + raise AttributeError(f'instance {instance} should have an editors attribute') from e elif isinstance(instance, str): pass @@ -139,8 +139,8 @@ def get_obj_perms_status_code(instance, username, method): try: method_instance_obj_perms_map = status_map_object_permissions[method][instance_obj_perms_key] - except KeyError as exc: - raise KeyError('instance (%s) should be defined in status_map_object_permissions' % instance_obj_perms_key) from exc + except KeyError as e: + raise KeyError(f'instance ({instance_obj_perms_key}) should be defined in status_map_object_permissions') from e try: return method_instance_obj_perms_map[username] except KeyError: diff --git a/rdmo/core/tests/test_models.py b/rdmo/core/tests/test_models.py index 8ace9596ba..11576a6837 100644 --- a/rdmo/core/tests/test_models.py +++ b/rdmo/core/tests/test_models.py @@ -2,7 +2,6 @@ from rdmo.core.models import TranslationMixin - boolean_toggle = (True, False) test_languages = ('en', 'de') diff --git a/rdmo/core/tests/test_project_status.py b/rdmo/core/tests/test_project_status.py index 923e47e6ba..a05afcab75 100644 --- a/rdmo/core/tests/test_project_status.py +++ b/rdmo/core/tests/test_project_status.py @@ -1,4 +1,3 @@ -import pytest from django.core.management import call_command diff --git a/rdmo/core/tests/test_tags.py b/rdmo/core/tests/test_tags.py index b354d46f05..0aa74a73fa 100644 --- a/rdmo/core/tests/test_tags.py +++ b/rdmo/core/tests/test_tags.py @@ -19,6 +19,6 @@ def test_i18n_switcher(rf): rendered_template = Template(template).render(context) for language in settings.LANGUAGES: if language == settings.LANGUAGES[0]: - assert '%s' % language in rendered_template + assert '{}'.format(*language) in rendered_template else: - assert'%s' % language in rendered_template + assert'{}'.format(*language) in rendered_template diff --git a/rdmo/core/tests/test_validators.py b/rdmo/core/tests/test_validators.py index 005a7963f5..8a5e9637f2 100644 --- a/rdmo/core/tests/test_validators.py +++ b/rdmo/core/tests/test_validators.py @@ -1,5 +1,7 @@ import pytest + from django.core.exceptions import ValidationError + from rest_framework import serializers from ..validators import InstanceValidator diff --git a/rdmo/core/tests/test_views.py b/rdmo/core/tests/test_views.py index f15157ef87..77869e9526 100644 --- a/rdmo/core/tests/test_views.py +++ b/rdmo/core/tests/test_views.py @@ -1,4 +1,5 @@ import pytest + from django.urls import reverse users = ( diff --git a/rdmo/core/tests/test_viewset_settings.py b/rdmo/core/tests/test_viewset_settings.py index 6b015d869a..f962cda1a7 100644 --- a/rdmo/core/tests/test_viewset_settings.py +++ b/rdmo/core/tests/test_viewset_settings.py @@ -1,4 +1,5 @@ import pytest + from django.urls import reverse users = ( diff --git a/rdmo/core/tests/test_xml.py b/rdmo/core/tests/test_xml.py index 640454a8a9..9f7c2c4519 100644 --- a/rdmo/core/tests/test_xml.py +++ b/rdmo/core/tests/test_xml.py @@ -1,86 +1,31 @@ -# import os -# import xml.etree.ElementTree as ET +from pathlib import Path -# from ..imports import read_xml_file -# from ..xml import (flat_xml_to_dictlist, get_ns_map, get_ns_tag, -# get_text_or_attribute, get_uri, node_type_from_dictlist, -# sort_dictlist_by_key) +from ..xml import flat_xml_to_elements, get_ns_map, get_ns_tag, get_uri, read_xml_file -# # tests -# def test_get_ns_map(): -# xmls = read_all_test_xmls() -# for key in xmls: -# assert str(get_ns_map(xmls[key])) == "{'dc': 'http://purl.org/dc/elements/1.1/'}" +def test_get_ns_map(settings): + xml_path = Path(settings.BASE_DIR) / 'xml/elements/attributes.xml' + root = read_xml_file(xml_path) + assert get_ns_map(root) == {'dc': 'http://purl.org/dc/elements/1.1/'} -# def test_get_ns_tag(): -# root = read_test_xml('domain') -# nsmap = get_ns_map(root) -# assert get_ns_tag('dc:uri', nsmap) == "{http://purl.org/dc/elements/1.1/}uri" +def test_get_ns_tag(settings): + xml_path = Path(settings.BASE_DIR) / 'xml/elements/attributes.xml' + root = read_xml_file(xml_path) + nsmap = get_ns_map(root) + assert get_ns_tag('dc:uri', nsmap) == "{http://purl.org/dc/elements/1.1/}uri" -# def test_get_text_or_attribute(): -# root = read_test_xml('domain') -# el = root.find('attribute') -# assert get_text_or_attribute(el, 'key') == 'set' -# assert get_text_or_attribute(el, 'does_not_exist') is None +def test_get_uri(settings): + xml_path = Path(settings.BASE_DIR) / 'xml/elements/attributes.xml' + root = read_xml_file(xml_path) + el = root.find('attribute') + nsmap = get_ns_map(root) + assert get_uri(el, nsmap) == 'http://example.com/terms/domain/blocks' -# def test_get_uri(): -# root = read_test_xml('domain') -# el = root.find('attribute') -# nsmap = get_ns_map(root) -# assert get_uri(el, nsmap) == 'http://example.com/terms/domain/set' - - -# def test_etree_to_dict(): -# root = read_test_xml('domain') -# dictlist = flat_xml_to_dictlist(root) -# assert dictlist[0]['uri'] == 'http://example.com/terms/domain/conditions' -# assert dictlist[1]['parent'] == 'http://example.com/terms/domain/conditions' - -# root = read_test_xml('conditions') -# dictlist = flat_xml_to_dictlist(root) -# assert dictlist[0]['uri'] == 'http://example.com/terms/conditions/options_empty' - -# root = read_test_xml('options') -# dictlist = flat_xml_to_dictlist(root) -# assert dictlist[0]['uri'] == 'http://example.com/terms/options/one_two_three' - -# root = read_test_xml('tasks') -# dictlist = flat_xml_to_dictlist(root) -# assert dictlist[0]['uri'] == 'http://example.com/terms/tasks/options_contains_one' - -# root = read_test_xml('questions') -# dictlist = flat_xml_to_dictlist(root) -# assert dictlist[0]['uri'] == 'http://example.com/terms/questions/catalog' - -# root = read_test_xml('views') -# dictlist = flat_xml_to_dictlist(root) -# assert dictlist[0]['uri'] == 'http://example.com/terms/views/view_a' - - -# def test_node_type_from_dictlist(): -# root = read_test_xml('options') -# dictlist = flat_xml_to_dictlist(root) -# optionsets = node_type_from_dictlist(dictlist, 'optionset') -# assert optionsets[0]['node_type'] == 'optionset' - - -# # test utils -# def read_all_test_xmls(): -# xml = {} -# xml['domain'] = read_test_xml('domain') -# xml['conditions'] = read_test_xml('conditions') -# xml['options'] = read_test_xml('options') -# xml['project'] = read_test_xml('project') -# xml['questions'] = read_test_xml('questions') -# xml['tasks'] = read_test_xml('tasks') -# xml['views'] = read_test_xml('views') -# return xml - - -# def read_test_xml(filename): -# xml_folder = os.getcwd().split('rdmo')[0] + 'rdmo/testing/xml/' -# return read_xml_file(xml_folder + filename + '.xml') +def test_flat_xml_to_elements(settings): + xml_path = Path(settings.BASE_DIR) / 'xml/elements/attributes.xml' + root = read_xml_file(xml_path) + elements = flat_xml_to_elements(root) + assert elements['http://example.com/terms/domain/blocks']['uri'] == 'http://example.com/terms/domain/blocks' diff --git a/rdmo/core/urls/v1.py b/rdmo/core/urls/v1.py index 0b3a4e7671..442d612de5 100644 --- a/rdmo/core/urls/v1.py +++ b/rdmo/core/urls/v1.py @@ -1,4 +1,5 @@ from django.urls import include, path + from rest_framework import routers from ..viewsets import GroupViewSet, SettingsViewSet, SitesViewSet diff --git a/rdmo/core/utils.py b/rdmo/core/utils.py index b3dd0bebcf..22d9d42b31 100644 --- a/rdmo/core/utils.py +++ b/rdmo/core/utils.py @@ -7,14 +7,15 @@ from tempfile import mkstemp from urllib.parse import urlparse -import pypandoc -from defusedcsv import csv from django.apps import apps from django.conf import settings from django.http import Http404, HttpResponse, HttpResponseBadRequest from django.template.loader import get_template from django.utils.encoding import force_str from django.utils.translation import gettext_lazy as _ + +import pypandoc +from defusedcsv import csv from markdown import markdown log = logging.getLogger(__name__) @@ -149,7 +150,7 @@ def get_language_fields(field_name): def get_language_warning(obj, field): for lang_code, lang_string, lang_field in get_languages(): - if not getattr(obj, '%s_%s' % (field, lang_field)): + if not getattr(obj, f'{field}_{lang_field}'): return True return False @@ -158,7 +159,7 @@ def set_export_reference_document(format, context): # try to get the view uri from the context try: view = context['view'] - view_uri = getattr(view, 'uri') + view_uri = view.uri except (AttributeError, KeyError, TypeError): view_uri = None @@ -215,11 +216,11 @@ def render_to_format(request, export_format, title, template_src, context): if export_format == 'html': # create the response object response = HttpResponse(html) - response['Content-Disposition'] = 'filename="%s.%s"' % (title, export_format) + response['Content-Disposition'] = f'filename="{title}.{export_format}"' else: pandoc_args = settings.EXPORT_PANDOC_ARGS.get(export_format, []) - content_disposition = 'attachment; filename="%s.%s"' % (title, export_format) + content_disposition = f'attachment; filename="{title}.{export_format}"' if export_format == 'pdf': # check pandoc version (the pdf arg changed to version 2) @@ -229,23 +230,23 @@ def render_to_format(request, export_format, title, template_src, context): ) for arg in pandoc_args] # display pdf in browser - content_disposition = 'filename="%s.%s"' % (title, export_format) + content_disposition = f'filename="{title}.{export_format}"' # use reference document for certain file formats refdoc = set_export_reference_document(export_format, context) if refdoc is not None and export_format in ['docx', 'odt']: # check pandoc version (the args changed to version 2) if get_pandoc_main_version() == 1: - pandoc_args.append('--reference-{}={}'.format(export_format, refdoc)) + pandoc_args.append(f'--reference-{export_format}={refdoc}') else: - pandoc_args.append('--reference-doc={}'.format(refdoc)) + pandoc_args.append(f'--reference-doc={refdoc}') # add the possible resource-path if pandoc_version_at_least("2") is True: - pandoc_args.append('--resource-path={}'.format(settings.STATIC_ROOT)) + pandoc_args.append(f'--resource-path={settings.STATIC_ROOT}') if 'resource_path' in context: resource_path = Path(settings.MEDIA_ROOT).joinpath(context['resource_path']) - pandoc_args.append('--resource-path={}'.format(resource_path)) + pandoc_args.append(f'--resource-path={resource_path}') # create a temporary file (tmp_fd, tmp_filename) = mkstemp('.' + export_format) diff --git a/rdmo/core/validators.py b/rdmo/core/validators.py index 4c48404d06..ecf8a8bc61 100644 --- a/rdmo/core/validators.py +++ b/rdmo/core/validators.py @@ -1,13 +1,12 @@ import re -from django.core.exceptions import (MultipleObjectsReturned, - ObjectDoesNotExist, ValidationError) +from django.core.exceptions import MultipleObjectsReturned, ObjectDoesNotExist, ValidationError from django.utils.translation import gettext_lazy as _ from rest_framework import serializers -class InstanceValidator(object): +class InstanceValidator: ''' BaseValidator which should work with model instances, used by diff --git a/rdmo/core/views.py b/rdmo/core/views.py index 45ec24bb76..3ec0ec2a47 100644 --- a/rdmo/core/views.py +++ b/rdmo/core/views.py @@ -2,9 +2,8 @@ from django.conf import settings from django.contrib.auth.decorators import login_required -from django.contrib.auth.mixins import \ - PermissionRequiredMixin as DjangoPermissionRequiredMixin from django.contrib.auth.mixins import LoginRequiredMixin +from django.contrib.auth.mixins import PermissionRequiredMixin as DjangoPermissionRequiredMixin from django.contrib.auth.views import redirect_to_login from django.core.exceptions import PermissionDenied from django.http import HttpResponseRedirect @@ -14,10 +13,11 @@ from django.utils.decorators import method_decorator from django.views.decorators.csrf import ensure_csrf_cookie from django.views.generic.base import View + from rest_framework import mixins, viewsets from rest_framework.response import Response -from rules.contrib.views import \ - PermissionRequiredMixin as RulesPermissionRequiredMixin + +from rules.contrib.views import PermissionRequiredMixin as RulesPermissionRequiredMixin from .serializers import ChoicesSerializer from .utils import get_next, get_referer, get_referer_path_info @@ -89,10 +89,10 @@ def post(self, request, *args, **kwargs): if 'cancel' in request.POST: return HttpResponseRedirect(get_next(request)) else: - return super(RedirectViewMixin, self).post(request, *args, **kwargs) + return super().post(request, *args, **kwargs) def get_context_data(self, **kwargs): - context_data = super(RedirectViewMixin, self).get_context_data(**kwargs) + context_data = super().get_context_data(**kwargs) if 'next' in self.request.GET: context_data['next'] = self.request.GET['next'] else: @@ -103,10 +103,10 @@ def get_success_url(self): if 'next' in self.request.GET: return self.request.GET['next'] else: - return super(RedirectViewMixin, self).get_success_url() + return super().get_success_url() -class PermissionRedirectMixin(object): +class PermissionRedirectMixin: def handle_no_permission(self): if self.request.user.is_authenticated: @@ -114,11 +114,11 @@ def handle_no_permission(self): return redirect_to_login(self.request.get_full_path(), self.get_login_url(), self.get_redirect_field_name()) -class ModelPermissionMixin(LoginRequiredMixin, PermissionRedirectMixin, DjangoPermissionRequiredMixin, object): +class ModelPermissionMixin(LoginRequiredMixin, PermissionRedirectMixin, DjangoPermissionRequiredMixin): pass -class ObjectPermissionMixin(PermissionRedirectMixin, RulesPermissionRequiredMixin, object): +class ObjectPermissionMixin(PermissionRedirectMixin, RulesPermissionRequiredMixin): pass diff --git a/rdmo/core/viewsets.py b/rdmo/core/viewsets.py index 1b6af7cbbe..eaa97a2072 100644 --- a/rdmo/core/viewsets.py +++ b/rdmo/core/viewsets.py @@ -1,8 +1,8 @@ from django.conf import settings from django.contrib.auth.models import Group from django.contrib.sites.models import Site -from rest_framework import status, viewsets -from rest_framework.decorators import action + +from rest_framework import viewsets from rest_framework.permissions import IsAuthenticated from rest_framework.response import Response diff --git a/rdmo/core/xml.py b/rdmo/core/xml.py index a4f67f4963..6e2c53b82d 100644 --- a/rdmo/core/xml.py +++ b/rdmo/core/xml.py @@ -59,7 +59,7 @@ def flat_xml_to_elements(root): element[tag]['model'] = models[sub_node.tag] elif 'lang' in sub_node.attrib: # this node has the lang attribute! - element['%s_%s' % (tag, sub_node.attrib['lang'])] = sub_node.text + element['{}_{}'.format(tag, sub_node.attrib['lang'])] = sub_node.text elif list(sub_node): # this node is a list! element[tag] = [] @@ -86,7 +86,7 @@ def flat_xml_to_elements(root): def get_ns_tag(tag, ns_map): tag_split = tag.split(':') try: - return '{%s}%s' % (ns_map[tag_split[0]], tag_split[1]) + return f'{{{ns_map[tag_split[0]]}}}{tag_split[1]}' except KeyError: return None diff --git a/rdmo/domain/admin.py b/rdmo/domain/admin.py index b7abc1552b..756e41f4d3 100644 --- a/rdmo/domain/admin.py +++ b/rdmo/domain/admin.py @@ -3,8 +3,7 @@ from django.db import models from .models import Attribute -from .validators import (AttributeLockedValidator, AttributeParentValidator, - AttributeUniqueURIValidator) +from .validators import AttributeLockedValidator, AttributeParentValidator, AttributeUniqueURIValidator class AttributeAdminForm(forms.ModelForm): diff --git a/rdmo/domain/imports.py b/rdmo/domain/imports.py index 6cd660b116..5a97f6bcfa 100644 --- a/rdmo/domain/imports.py +++ b/rdmo/domain/imports.py @@ -2,12 +2,10 @@ from django.contrib.sites.models import Site -from rdmo.core.imports import (check_permissions, set_common_fields, - set_foreign_field, validate_instance) +from rdmo.core.imports import check_permissions, set_common_fields, set_foreign_field, validate_instance from .models import Attribute -from .validators import (AttributeLockedValidator, AttributeParentValidator, - AttributeUniqueURIValidator) +from .validators import AttributeLockedValidator, AttributeParentValidator, AttributeUniqueURIValidator logger = logging.getLogger(__name__) diff --git a/rdmo/domain/models.py b/rdmo/domain/models.py index fd377d4887..3da76b9137 100644 --- a/rdmo/domain/models.py +++ b/rdmo/domain/models.py @@ -2,9 +2,10 @@ from django.contrib.sites.models import Site from django.db import models from django.utils.translation import gettext_lazy as _ + from mptt.models import MPTTModel, TreeForeignKey -from rdmo.core.utils import copy_model, join_url +from rdmo.core.utils import join_url class Attribute(MPTTModel): diff --git a/rdmo/domain/serializers/v1.py b/rdmo/domain/serializers/v1.py index 4e2b5f3e3a..cf3254e06c 100644 --- a/rdmo/domain/serializers/v1.py +++ b/rdmo/domain/serializers/v1.py @@ -3,13 +3,11 @@ from rest_framework import serializers from rdmo.conditions.models import Condition -from rdmo.core.serializers import (ElementModelSerializerMixin, - ReadOnlyObjectPermissionSerializerMixin) +from rdmo.core.serializers import ElementModelSerializerMixin, ReadOnlyObjectPermissionSerializerMixin from rdmo.questions.models import Page, Question, QuestionSet from ..models import Attribute -from ..validators import (AttributeLockedValidator, AttributeParentValidator, - AttributeUniqueURIValidator) +from ..validators import AttributeLockedValidator, AttributeParentValidator, AttributeUniqueURIValidator log = logging.getLogger(__name__) @@ -52,7 +50,8 @@ class AttributeSerializer(BaseAttributeSerializer): values_count = serializers.IntegerField(read_only=True) class Meta(BaseAttributeSerializer.Meta): - fields = BaseAttributeSerializer.Meta.fields + ( + fields = ( + *BaseAttributeSerializer.Meta.fields, 'conditions', 'pages', 'questionsets', @@ -78,8 +77,9 @@ def get_attributes(self, obj): class AttributeListSerializer(BaseAttributeSerializer): class Meta(BaseAttributeSerializer.Meta): - fields = BaseAttributeSerializer.Meta.fields + ( - 'is_leaf_node', + fields = ( + *BaseAttributeSerializer.Meta.fields, + 'is_leaf_node' ) @@ -88,8 +88,9 @@ class AttributeNestedSerializer(AttributeListSerializer): elements = serializers.SerializerMethodField() class Meta(AttributeListSerializer.Meta): - fields = AttributeListSerializer.Meta.fields + ( - 'elements', + fields = ( + *AttributeListSerializer.Meta.fields, + 'elements' ) def get_elements(self, obj): diff --git a/rdmo/domain/tests/test_validator_locked.py b/rdmo/domain/tests/test_validator_locked.py index 90741c8e0e..b3f5f35247 100644 --- a/rdmo/domain/tests/test_validator_locked.py +++ b/rdmo/domain/tests/test_validator_locked.py @@ -1,8 +1,8 @@ import pytest -from django.conf import settings + from django.core.exceptions import ValidationError -from rest_framework.exceptions import \ - ValidationError as RestFameworkValidationError + +from rest_framework.exceptions import ValidationError as RestFameworkValidationError from ..models import Attribute from ..serializers.v1 import AttributeSerializer diff --git a/rdmo/domain/tests/test_validator_parent.py b/rdmo/domain/tests/test_validator_parent.py index 501fd5606d..30baee9dbe 100644 --- a/rdmo/domain/tests/test_validator_parent.py +++ b/rdmo/domain/tests/test_validator_parent.py @@ -1,8 +1,9 @@ import pytest + from django.conf import settings from django.core.exceptions import ValidationError -from rest_framework.exceptions import \ - ValidationError as RestFameworkValidationError + +from rest_framework.exceptions import ValidationError as RestFameworkValidationError from ..models import Attribute from ..serializers.v1 import AttributeSerializer @@ -37,7 +38,7 @@ def test_update_error(db): def test_serializer_create(db): - class MockedView(object): + class MockedView: action = 'create' validator = AttributeParentValidator() diff --git a/rdmo/domain/tests/test_validator_unique_uri.py b/rdmo/domain/tests/test_validator_unique_uri.py index fb41240567..a92f3c2fed 100644 --- a/rdmo/domain/tests/test_validator_unique_uri.py +++ b/rdmo/domain/tests/test_validator_unique_uri.py @@ -1,8 +1,9 @@ import pytest + from django.conf import settings from django.core.exceptions import ValidationError -from rest_framework.exceptions import \ - ValidationError as RestFameworkValidationError + +from rest_framework.exceptions import ValidationError as RestFameworkValidationError from ..models import Attribute from ..serializers.v1 import AttributeSerializer diff --git a/rdmo/domain/tests/test_viewset_attribute.py b/rdmo/domain/tests/test_viewset_attribute.py index e1607df934..09a1b2d27f 100644 --- a/rdmo/domain/tests/test_viewset_attribute.py +++ b/rdmo/domain/tests/test_viewset_attribute.py @@ -1,6 +1,7 @@ import xml.etree.ElementTree as et import pytest + from django.urls import reverse from ..models import Attribute @@ -98,7 +99,7 @@ def test_create(db, client, username, password): url = reverse(urlnames['list']) data = { 'uri_prefix': instance.uri_prefix, - 'key': '%s_new_%s' % (instance.key, username), + 'key': f'{instance.key}_new_{username}', 'comment': '', 'parent': instance.parent.pk if instance.parent else '' } @@ -117,7 +118,7 @@ def test_create_condition(db, client, username, password): url = reverse(urlnames['list']) data = { 'uri_prefix': instance.uri_prefix, - 'key': '%s_new_%s' % (instance.key, username), + 'key': f'{instance.key}_new_{username}', 'comment': '', 'parent': instance.parent.pk if instance.parent else '', 'conditions': [condition.id] @@ -141,7 +142,7 @@ def test_create_page(db, client, username, password): url = reverse(urlnames['list']) data = { 'uri_prefix': instance.uri_prefix, - 'key': '%s_new_%s' % (instance.key, username), + 'key': f'{instance.key}_new_{username}', 'comment': '', 'parent': instance.parent.pk if instance.parent else '', 'pages': [page.id] @@ -165,7 +166,7 @@ def test_create_questionset(db, client, username, password): url = reverse(urlnames['list']) data = { 'uri_prefix': instance.uri_prefix, - 'key': '%s_new_%s' % (instance.key, username), + 'key': f'{instance.key}_new_{username}', 'comment': '', 'parent': instance.parent.pk if instance.parent else '', 'questionsets': [questionset.id] @@ -189,7 +190,7 @@ def test_create_question(db, client, username, password): url = reverse(urlnames['list']) data = { 'uri_prefix': instance.uri_prefix, - 'key': '%s_new_%s' % (instance.key, username), + 'key': f'{instance.key}_new_{username}', 'comment': '', 'parent': instance.parent.pk if instance.parent else '', 'questions': [question.id] diff --git a/rdmo/domain/tests/test_viewset_attribute_multisite.py b/rdmo/domain/tests/test_viewset_attribute_multisite.py index aa8cd26e9e..674469a694 100644 --- a/rdmo/domain/tests/test_viewset_attribute_multisite.py +++ b/rdmo/domain/tests/test_viewset_attribute_multisite.py @@ -4,13 +4,10 @@ from django.urls import reverse -from ..models import Attribute - - +from ...core.tests import get_obj_perms_status_code from ...core.tests import multisite_status_map as status_map from ...core.tests import multisite_users as users -from ...core.tests import get_obj_perms_status_code - +from ..models import Attribute from .test_viewset_attribute import urlnames @@ -73,7 +70,7 @@ def test_create(db, client, username, password): url = reverse(urlnames['list']) data = { 'uri_prefix': instance.uri_prefix, - 'key': '%s_new_%s' % (instance.key, username), + 'key': f'{instance.key}_new_{username}', 'comment': '', 'parent': instance.parent.pk if instance.parent else '' } diff --git a/rdmo/domain/validators.py b/rdmo/domain/validators.py index e6cb5be542..c6939d5ed9 100644 --- a/rdmo/domain/validators.py +++ b/rdmo/domain/validators.py @@ -1,7 +1,6 @@ from django.utils.translation import gettext_lazy as _ -from rdmo.core.validators import (InstanceValidator, LockedValidator, - UniqueURIValidator) +from rdmo.core.validators import InstanceValidator, LockedValidator, UniqueURIValidator from .models import Attribute @@ -46,14 +45,18 @@ def __call__(self, data, serializer=None): # get the original from the view when cloning an attribute if parent in view.get_object().get_descendants(include_self=True): self.raise_validation_error({ - 'parent': [_('An attribute may not be cloned to be a child of itself or one of its descendants.')] + 'parent': [ + _('An attribute may not be cloned to be a child of itself or one of its descendants.') + ] }) # only check updated attributes if self.instance: if parent in self.instance.get_descendants(include_self=True): self.raise_validation_error({ - 'parent': [_('An attribute may not be moved to be a child of itself or one of its descendants.')] + 'parent': [ + _('An attribute may not be moved to be a child of itself or one of its descendants.') + ] }) diff --git a/rdmo/domain/viewsets.py b/rdmo/domain/viewsets.py index f4f042bbdd..268702fc7f 100644 --- a/rdmo/domain/viewsets.py +++ b/rdmo/domain/viewsets.py @@ -1,10 +1,12 @@ from django.db import models -from django_filters.rest_framework import DjangoFilterBackend + from rest_framework.decorators import action from rest_framework.filters import SearchFilter from rest_framework.response import Response from rest_framework.viewsets import ModelViewSet +from django_filters.rest_framework import DjangoFilterBackend + from rdmo.core.exports import XMLResponse from rdmo.core.permissions import HasModelPermission, HasObjectPermission from rdmo.core.utils import render_to_csv, render_to_format @@ -12,8 +14,12 @@ from .models import Attribute from .renderers import AttributeRenderer from .serializers.export import AttributeExportSerializer -from .serializers.v1 import (AttributeIndexSerializer, AttributeListSerializer, - AttributeNestedSerializer, AttributeSerializer) +from .serializers.v1 import ( + AttributeIndexSerializer, + AttributeListSerializer, + AttributeNestedSerializer, + AttributeSerializer, +) class AttributeViewSet(ModelViewSet): @@ -76,6 +82,8 @@ def detail_export(self, request, pk=None, export_format='xml'): delimiter = ',' if export_format == 'csvcomma' else ';' return render_to_csv('domain', rows, delimiter) else: - return render_to_format(self.request, export_format, self.get_object().key, 'domain/export/attributes.html', { - 'attributes': attributes - }) + return render_to_format( + self.request, export_format, self.get_object().key, 'domain/export/attributes.html', { + 'attributes': attributes + } + ) diff --git a/rdmo/management/apps.py b/rdmo/management/apps.py index 60aed1e190..40349d3b19 100644 --- a/rdmo/management/apps.py +++ b/rdmo/management/apps.py @@ -7,4 +7,4 @@ class ManagementConfig(AppConfig): verbose_name = _('Management') def ready(self): - from . import rules + from . import rules # noqa: F401 diff --git a/rdmo/management/assets/js/components/import/ImportCondition.js b/rdmo/management/assets/js/components/import/ImportCondition.js index f6edc4907a..58598f2429 100644 --- a/rdmo/management/assets/js/components/import/ImportCondition.js +++ b/rdmo/management/assets/js/components/import/ImportCondition.js @@ -47,4 +47,4 @@ ImportCondition.propTypes = { importActions: PropTypes.object.isRequired } -export default ImportCondition \ No newline at end of file +export default ImportCondition diff --git a/rdmo/management/assets/js/components/import/ImportOption.js b/rdmo/management/assets/js/components/import/ImportOption.js index e2ca7d4828..af8f0166ac 100644 --- a/rdmo/management/assets/js/components/import/ImportOption.js +++ b/rdmo/management/assets/js/components/import/ImportOption.js @@ -47,4 +47,4 @@ ImportOption.propTypes = { importActions: PropTypes.object.isRequired } -export default ImportOption \ No newline at end of file +export default ImportOption diff --git a/rdmo/management/assets/js/components/import/ImportOptionSet.js b/rdmo/management/assets/js/components/import/ImportOptionSet.js index 6eebc93543..e7798c051d 100644 --- a/rdmo/management/assets/js/components/import/ImportOptionSet.js +++ b/rdmo/management/assets/js/components/import/ImportOptionSet.js @@ -47,4 +47,4 @@ ImportOptionSet.propTypes = { importActions: PropTypes.object.isRequired } -export default ImportOptionSet \ No newline at end of file +export default ImportOptionSet diff --git a/rdmo/management/assets/js/components/import/ImportPage.js b/rdmo/management/assets/js/components/import/ImportPage.js index 42a9b210fc..2fb438f904 100644 --- a/rdmo/management/assets/js/components/import/ImportPage.js +++ b/rdmo/management/assets/js/components/import/ImportPage.js @@ -47,4 +47,4 @@ ImportPage.propTypes = { importActions: PropTypes.object.isRequired } -export default ImportPage \ No newline at end of file +export default ImportPage diff --git a/rdmo/management/assets/js/components/import/ImportQuestion.js b/rdmo/management/assets/js/components/import/ImportQuestion.js index 441beaca84..12c757ee28 100644 --- a/rdmo/management/assets/js/components/import/ImportQuestion.js +++ b/rdmo/management/assets/js/components/import/ImportQuestion.js @@ -47,4 +47,4 @@ ImportQuestion.propTypes = { importActions: PropTypes.object.isRequired } -export default ImportQuestion \ No newline at end of file +export default ImportQuestion diff --git a/rdmo/management/assets/js/components/import/ImportQuestionSet.js b/rdmo/management/assets/js/components/import/ImportQuestionSet.js index 23f44988a5..8163015cbe 100644 --- a/rdmo/management/assets/js/components/import/ImportQuestionSet.js +++ b/rdmo/management/assets/js/components/import/ImportQuestionSet.js @@ -47,4 +47,4 @@ ImportQuestionSet.propTypes = { importActions: PropTypes.object.isRequired } -export default ImportQuestionSet \ No newline at end of file +export default ImportQuestionSet diff --git a/rdmo/management/assets/js/components/import/ImportSection.js b/rdmo/management/assets/js/components/import/ImportSection.js index 986c0e5e75..92203a8771 100644 --- a/rdmo/management/assets/js/components/import/ImportSection.js +++ b/rdmo/management/assets/js/components/import/ImportSection.js @@ -47,4 +47,4 @@ ImportSection.propTypes = { importActions: PropTypes.object.isRequired } -export default ImportSection \ No newline at end of file +export default ImportSection diff --git a/rdmo/management/assets/js/components/import/ImportTask.js b/rdmo/management/assets/js/components/import/ImportTask.js index 964ef7ffef..72b2b73158 100644 --- a/rdmo/management/assets/js/components/import/ImportTask.js +++ b/rdmo/management/assets/js/components/import/ImportTask.js @@ -49,4 +49,4 @@ ImportTask.propTypes = { importActions: PropTypes.object.isRequired } -export default ImportTask \ No newline at end of file +export default ImportTask diff --git a/rdmo/management/assets/js/components/import/ImportView.js b/rdmo/management/assets/js/components/import/ImportView.js index 903b1676de..e48196efb5 100644 --- a/rdmo/management/assets/js/components/import/ImportView.js +++ b/rdmo/management/assets/js/components/import/ImportView.js @@ -49,4 +49,4 @@ ImportView.propTypes = { importActions: PropTypes.object.isRequired } -export default ImportView \ No newline at end of file +export default ImportView diff --git a/rdmo/management/imports.py b/rdmo/management/imports.py index 70a47a4bff..e47a3c7001 100644 --- a/rdmo/management/imports.py +++ b/rdmo/management/imports.py @@ -3,9 +3,7 @@ from rdmo.conditions.imports import import_condition from rdmo.domain.imports import import_attribute from rdmo.options.imports import import_option, import_optionset -from rdmo.questions.imports import (import_catalog, import_page, - import_question, import_questionset, - import_section) +from rdmo.questions.imports import import_catalog, import_page, import_question, import_questionset, import_section from rdmo.tasks.imports import import_task from rdmo.views.imports import import_view diff --git a/rdmo/management/management/commands/import.py b/rdmo/management/management/commands/import.py index 81159e6b1a..3438164220 100644 --- a/rdmo/management/management/commands/import.py +++ b/rdmo/management/management/commands/import.py @@ -3,8 +3,7 @@ from django.core.management.base import BaseCommand, CommandError from django.utils.translation import gettext_lazy as _ -from rdmo.core.xml import (convert_elements, flat_xml_to_elements, - order_elements, read_xml_file) +from rdmo.core.xml import convert_elements, flat_xml_to_elements, order_elements, read_xml_file from rdmo.management.imports import import_elements logger = logging.getLogger(__name__) diff --git a/rdmo/management/tests/test_commands.py b/rdmo/management/tests/test_commands.py index 22d10d7688..b9b252d776 100644 --- a/rdmo/management/tests/test_commands.py +++ b/rdmo/management/tests/test_commands.py @@ -1,9 +1,8 @@ -import pytest - import io - from pathlib import Path +import pytest + from django.core.management import call_command from django.core.management.base import CommandError diff --git a/rdmo/management/tests/test_import_conditions.py b/rdmo/management/tests/test_import_conditions.py index b19ad8e70c..4c97ced3fd 100644 --- a/rdmo/management/tests/test_import_conditions.py +++ b/rdmo/management/tests/test_import_conditions.py @@ -1,8 +1,7 @@ from pathlib import Path from rdmo.conditions.models import Condition -from rdmo.core.xml import (convert_elements, flat_xml_to_elements, - order_elements, read_xml_file) +from rdmo.core.xml import convert_elements, flat_xml_to_elements, order_elements, read_xml_file from rdmo.management.imports import import_elements @@ -20,8 +19,8 @@ def test_create_conditions(db, settings): import_elements(elements) assert len(root) == len(elements) == Condition.objects.count() == 15 - assert all([element['created'] is True for element in elements]) - assert all([element['updated'] is False for element in elements]) + assert all(element['created'] is True for element in elements) + assert all(element['updated'] is False for element in elements) def test_update_conditions(db, settings): @@ -36,8 +35,8 @@ def test_update_conditions(db, settings): import_elements(elements) assert len(root) == len(elements) - assert all([element['created'] is False for element in elements]) - assert all([element['updated'] is True for element in elements]) + assert all(element['created'] is False for element in elements) + assert all(element['updated'] is True for element in elements) def test_create_legacy_conditions(db, settings): @@ -54,8 +53,8 @@ def test_create_legacy_conditions(db, settings): import_elements(elements) assert len(root) == len(elements) == Condition.objects.count() == 15 - assert all([element['created'] is True for element in elements]) - assert all([element['updated'] is False for element in elements]) + assert all(element['created'] is True for element in elements) + assert all(element['updated'] is False for element in elements) def test_update_legacy_conditions(db, settings): @@ -70,5 +69,5 @@ def test_update_legacy_conditions(db, settings): import_elements(elements) assert len(root) == len(elements) == 15 - assert all([element['created'] is False for element in elements]) - assert all([element['updated'] is True for element in elements]) + assert all(element['created'] is False for element in elements) + assert all(element['updated'] is True for element in elements) diff --git a/rdmo/management/tests/test_import_domain.py b/rdmo/management/tests/test_import_domain.py index 33ae0d53c7..b9f032b92f 100644 --- a/rdmo/management/tests/test_import_domain.py +++ b/rdmo/management/tests/test_import_domain.py @@ -1,7 +1,6 @@ from pathlib import Path -from rdmo.core.xml import (convert_elements, flat_xml_to_elements, - order_elements, read_xml_file) +from rdmo.core.xml import convert_elements, flat_xml_to_elements, order_elements, read_xml_file from rdmo.domain.models import Attribute from rdmo.management.imports import import_elements @@ -20,8 +19,8 @@ def test_create_domain(db, settings): import_elements(elements) assert len(root) == len(elements) == Attribute.objects.count() == 86 - assert all([element['created'] is True for element in elements]) - assert all([element['updated'] is False for element in elements]) + assert all(element['created'] is True for element in elements) + assert all(element['updated'] is False for element in elements) def test_update_domain(db, settings): @@ -36,8 +35,8 @@ def test_update_domain(db, settings): import_elements(elements) assert len(root) == len(elements) - assert all([element['created'] is False for element in elements]) - assert all([element['updated'] is True for element in elements]) + assert all(element['created'] is False for element in elements) + assert all(element['updated'] is True for element in elements) def test_create_legacy_domain(db, settings): @@ -55,8 +54,8 @@ def test_create_legacy_domain(db, settings): assert len(root) == len(elements) == 86 assert Attribute.objects.count() == 86 - assert all([element['created'] is True for element in elements]) - assert all([element['updated'] is False for element in elements]) + assert all(element['created'] is True for element in elements) + assert all(element['updated'] is False for element in elements) def test_update_legacy_domain(db, settings): @@ -71,5 +70,5 @@ def test_update_legacy_domain(db, settings): import_elements(elements) assert len(root) == len(elements) == 86 - assert all([element['created'] is False for element in elements]) - assert all([element['updated'] is True for element in elements]) + assert all(element['created'] is False for element in elements) + assert all(element['updated'] is True for element in elements) diff --git a/rdmo/management/tests/test_import_options.py b/rdmo/management/tests/test_import_options.py index 45eb499f96..8d98989901 100644 --- a/rdmo/management/tests/test_import_options.py +++ b/rdmo/management/tests/test_import_options.py @@ -1,7 +1,6 @@ from pathlib import Path -from rdmo.core.xml import (convert_elements, flat_xml_to_elements, - order_elements, read_xml_file) +from rdmo.core.xml import convert_elements, flat_xml_to_elements, order_elements, read_xml_file from rdmo.management.imports import import_elements from rdmo.options.models import Option, OptionSet @@ -22,8 +21,8 @@ def test_create_optionsets(db, settings): assert len(root) == len(elements) == 12 assert OptionSet.objects.count() == 4 assert Option.objects.count() == 8 - assert all([element['created'] is True for element in elements]) - assert all([element['updated'] is False for element in elements]) + assert all(element['created'] is True for element in elements) + assert all(element['updated'] is False for element in elements) def test_update_optionsets(db, settings): @@ -37,8 +36,8 @@ def test_update_optionsets(db, settings): import_elements(elements) assert len(root) == len(elements) == 12 - assert all([element['created'] is False for element in elements]) - assert all([element['updated'] is True for element in elements]) + assert all(element['created'] is False for element in elements) + assert all(element['updated'] is True for element in elements) def test_create_options(db, settings): @@ -54,8 +53,8 @@ def test_create_options(db, settings): import_elements(elements) assert len(root) == len(elements) == Option.objects.count() == 8 - assert all([element['created'] is True for element in elements]) - assert all([element['updated'] is False for element in elements]) + assert all(element['created'] is True for element in elements) + assert all(element['updated'] is False for element in elements) def test_update_options(db, settings): @@ -69,8 +68,8 @@ def test_update_options(db, settings): import_elements(elements) assert len(root) == len(elements) == 8 - assert all([element['created'] is False for element in elements]) - assert all([element['updated'] is True for element in elements]) + assert all(element['created'] is False for element in elements) + assert all(element['updated'] is True for element in elements) def test_create_legacy_options(db, settings): @@ -90,8 +89,8 @@ def test_create_legacy_options(db, settings): assert len(root) == len(elements) == 12 assert OptionSet.objects.count() == 4 assert Option.objects.count() == 8 - assert all([element['created'] is True for element in elements]) - assert all([element['updated'] is False for element in elements]) + assert all(element['created'] is True for element in elements) + assert all(element['updated'] is False for element in elements) def test_update_legacy_options(db, settings): @@ -106,5 +105,5 @@ def test_update_legacy_options(db, settings): import_elements(elements) assert len(root) == len(elements) == 12 - assert all([element['created'] is False for element in elements]) - assert all([element['updated'] is True for element in elements]) + assert all(element['created'] is False for element in elements) + assert all(element['updated'] is True for element in elements) diff --git a/rdmo/management/tests/test_import_questions.py b/rdmo/management/tests/test_import_questions.py index c52db2924c..fa79bdf28b 100644 --- a/rdmo/management/tests/test_import_questions.py +++ b/rdmo/management/tests/test_import_questions.py @@ -1,7 +1,6 @@ from pathlib import Path -from rdmo.core.xml import (convert_elements, flat_xml_to_elements, - order_elements, read_xml_file) +from rdmo.core.xml import convert_elements, flat_xml_to_elements, order_elements, read_xml_file from rdmo.management.imports import import_elements from rdmo.questions.models import Catalog, Page, Question, QuestionSet, Section @@ -29,8 +28,8 @@ def test_create_catalogs(db, settings): assert Page.objects.count() == 48 assert QuestionSet.objects.count() == 3 assert Question.objects.count() == 89 - assert all([element['created'] is True for element in elements]) - assert all([element['updated'] is False for element in elements]) + assert all(element['created'] is True for element in elements) + assert all(element['updated'] is False for element in elements) def test_update_catalogs(db, settings): @@ -45,8 +44,8 @@ def test_update_catalogs(db, settings): import_elements(elements) assert len(root) == len(elements) == 148 - assert all([element['created'] is False for element in elements]) - assert all([element['updated'] is True for element in elements]) + assert all(element['created'] is False for element in elements) + assert all(element['updated'] is True for element in elements) def test_create_sections(db, settings): @@ -70,8 +69,8 @@ def test_create_sections(db, settings): assert Page.objects.count() == 48 assert QuestionSet.objects.count() == 3 assert Question.objects.count() == 89 - assert all([element['created'] is True for element in elements]) - assert all([element['updated'] is False for element in elements]) + assert all(element['created'] is True for element in elements) + assert all(element['updated'] is False for element in elements) def test_update_sections(db, settings): @@ -86,8 +85,8 @@ def test_update_sections(db, settings): import_elements(elements) assert len(root) == len(elements) == 146 - assert all([element['created'] is False for element in elements]) - assert all([element['updated'] is True for element in elements]) + assert all(element['created'] is False for element in elements) + assert all(element['updated'] is True for element in elements) def test_create_pages(db, settings): @@ -109,8 +108,8 @@ def test_create_pages(db, settings): assert Page.objects.count() == 48 assert QuestionSet.objects.count() == 3 assert Question.objects.count() == 89 - assert all([element['created'] is True for element in elements]) - assert all([element['updated'] is False for element in elements]) + assert all(element['created'] is True for element in elements) + assert all(element['updated'] is False for element in elements) def test_update_pages(db, settings): @@ -125,8 +124,8 @@ def test_update_pages(db, settings): import_elements(elements) assert len(root) == len(elements) == 140 - assert all([element['created'] is False for element in elements]) - assert all([element['updated'] is True for element in elements]) + assert all(element['created'] is False for element in elements) + assert all(element['updated'] is True for element in elements) def test_create_questionsets(db, settings): @@ -148,8 +147,8 @@ def test_create_questionsets(db, settings): assert len(elements) == 8 assert QuestionSet.objects.count() == 3 assert Question.objects.count() == 5 - assert all([element['created'] is True for element in elements]) - assert all([element['updated'] is False for element in elements]) + assert all(element['created'] is True for element in elements) + assert all(element['updated'] is False for element in elements) def test_update_questionsets(db, settings): @@ -164,8 +163,8 @@ def test_update_questionsets(db, settings): import_elements(elements) assert len(root) == 10 # two questionsets apear twice in the export file - assert all([element['created'] is False for element in elements]) - assert all([element['updated'] is True for element in elements]) + assert all(element['created'] is False for element in elements) + assert all(element['updated'] is True for element in elements) def test_create_questions(db, settings): @@ -185,8 +184,8 @@ def test_create_questions(db, settings): assert len(root) == len(elements) == 89 assert Question.objects.count() == 89 - assert all([element['created'] is True for element in elements]) - assert all([element['updated'] is False for element in elements]) + assert all(element['created'] is True for element in elements) + assert all(element['updated'] is False for element in elements) def test_update_questions(db, settings): @@ -201,8 +200,8 @@ def test_update_questions(db, settings): import_elements(elements) assert len(root) == len(elements) == 89 - assert all([element['created'] is False for element in elements]) - assert all([element['updated'] is True for element in elements]) + assert all(element['created'] is False for element in elements) + assert all(element['updated'] is True for element in elements) def test_create_legacy_questions(db, settings): @@ -228,13 +227,13 @@ def test_create_legacy_questions(db, settings): assert Page.objects.count() == 48 assert QuestionSet.objects.count() == 3 assert Question.objects.count() == 89 - assert all([element['created'] is True for element in elements]) - assert all([element['updated'] is False for element in elements]) + assert all(element['created'] is True for element in elements) + assert all(element['updated'] is False for element in elements) # check that all elements ended up in the catalog catalog = Catalog.objects.prefetch_elements().first() - descendant_uris = set(element.uri for element in catalog.descendants) - element_uris = set(element['uri'] for element in elements if element['uri'] != catalog.uri) + descendant_uris = {element.uri for element in catalog.descendants} + element_uris = {element['uri'] for element in elements if element['uri'] != catalog.uri} assert descendant_uris == element_uris @@ -250,11 +249,11 @@ def test_update_legacy_questions(db, settings): import_elements(elements) assert len(root) == len(elements) == 147 - assert all([element['created'] is False for element in elements]) - assert all([element['updated'] is True for element in elements]) + assert all(element['created'] is False for element in elements) + assert all(element['updated'] is True for element in elements) # check that all elements ended up in the catalog catalog = Catalog.objects.prefetch_elements().first() - descendant_uris = set(element.uri for element in catalog.descendants) - element_uris = set(element['uri'] for element in elements if element['uri'] != catalog.uri) + descendant_uris = {element.uri for element in catalog.descendants} + element_uris = {element['uri'] for element in elements if element['uri'] != catalog.uri} assert descendant_uris == element_uris diff --git a/rdmo/management/tests/test_import_tasks.py b/rdmo/management/tests/test_import_tasks.py index 6f80c35d78..08071990cf 100644 --- a/rdmo/management/tests/test_import_tasks.py +++ b/rdmo/management/tests/test_import_tasks.py @@ -1,7 +1,6 @@ from pathlib import Path -from rdmo.core.xml import (convert_elements, flat_xml_to_elements, - order_elements, read_xml_file) +from rdmo.core.xml import convert_elements, flat_xml_to_elements, order_elements, read_xml_file from rdmo.management.imports import import_elements from rdmo.tasks.models import Task @@ -20,8 +19,8 @@ def test_create_tasks(db, settings): import_elements(elements) assert len(root) == len(elements) == Task.objects.count() == 2 - assert all([element['created'] is True for element in elements]) - assert all([element['updated'] is False for element in elements]) + assert all(element['created'] is True for element in elements) + assert all(element['updated'] is False for element in elements) def test_update_tasks(db, settings): @@ -36,8 +35,8 @@ def test_update_tasks(db, settings): import_elements(elements) assert len(root) == len(elements) == 2 - assert all([element['created'] is False for element in elements]) - assert all([element['updated'] is True for element in elements]) + assert all(element['created'] is False for element in elements) + assert all(element['updated'] is True for element in elements) def test_create_legacy_tasks(db, settings): @@ -54,8 +53,8 @@ def test_create_legacy_tasks(db, settings): import_elements(elements) assert len(root) == len(elements) == Task.objects.count() == 2 - assert all([element['created'] is True for element in elements]) - assert all([element['updated'] is False for element in elements]) + assert all(element['created'] is True for element in elements) + assert all(element['updated'] is False for element in elements) def test_update_legacy_tasks(db, settings): @@ -70,5 +69,5 @@ def test_update_legacy_tasks(db, settings): import_elements(elements) assert len(root) == len(elements) == 2 - assert all([element['created'] is False for element in elements]) - assert all([element['updated'] is True for element in elements]) + assert all(element['created'] is False for element in elements) + assert all(element['updated'] is True for element in elements) diff --git a/rdmo/management/tests/test_import_views.py b/rdmo/management/tests/test_import_views.py index cec79e5dab..a6c7bc7d06 100644 --- a/rdmo/management/tests/test_import_views.py +++ b/rdmo/management/tests/test_import_views.py @@ -1,7 +1,6 @@ from pathlib import Path -from rdmo.core.xml import (convert_elements, flat_xml_to_elements, - order_elements, read_xml_file) +from rdmo.core.xml import convert_elements, flat_xml_to_elements, order_elements, read_xml_file from rdmo.management.imports import import_elements from rdmo.views.models import View @@ -20,8 +19,8 @@ def test_create_tasks(db, settings): import_elements(elements) assert len(root) == len(elements) == View.objects.count() == 3 - assert all([element['created'] is True for element in elements]) - assert all([element['updated'] is False for element in elements]) + assert all(element['created'] is True for element in elements) + assert all(element['updated'] is False for element in elements) def test_update_tasks(db, settings): @@ -36,8 +35,8 @@ def test_update_tasks(db, settings): import_elements(elements) assert len(root) == len(elements) == 3 - assert all([element['created'] is False for element in elements]) - assert all([element['updated'] is True for element in elements]) + assert all(element['created'] is False for element in elements) + assert all(element['updated'] is True for element in elements) def test_create_legacy_tasks(db, settings): @@ -54,8 +53,8 @@ def test_create_legacy_tasks(db, settings): import_elements(elements) assert len(root) == len(elements) == View.objects.count() == 3 - assert all([element['created'] is True for element in elements]) - assert all([element['updated'] is False for element in elements]) + assert all(element['created'] is True for element in elements) + assert all(element['updated'] is False for element in elements) def test_update_legacy_tasks(db, settings): @@ -70,5 +69,5 @@ def test_update_legacy_tasks(db, settings): import_elements(elements) assert len(root) == len(elements) == 3 - assert all([element['created'] is False for element in elements]) - assert all([element['updated'] is True for element in elements]) + assert all(element['created'] is False for element in elements) + assert all(element['updated'] is True for element in elements) diff --git a/rdmo/management/tests/test_view.py b/rdmo/management/tests/test_view.py index 46df0ff0fb..8e049f1602 100644 --- a/rdmo/management/tests/test_view.py +++ b/rdmo/management/tests/test_view.py @@ -1,4 +1,5 @@ import pytest + from django.urls import reverse users = ( diff --git a/rdmo/management/tests/test_viewset_import_multisite.py b/rdmo/management/tests/test_viewset_import_multisite.py index 0be8805755..5a0e2f3d35 100644 --- a/rdmo/management/tests/test_viewset_import_multisite.py +++ b/rdmo/management/tests/test_viewset_import_multisite.py @@ -2,12 +2,10 @@ from django.urls import reverse -from rdmo.core.tests import multisite_users as users from rdmo.core.tests import get_obj_perms_status_code - +from rdmo.core.tests import multisite_users as users from rdmo.questions.models import Catalog, Page, Question, QuestionSet, Section - status_map = { 'list': { 'default': 405, 'anonymous': 401 @@ -89,7 +87,7 @@ def test_create_update(db, client, username, password, json_data): def test_create_update_certain_catalog(db, client, username, password, catalog_uri_path, json_data): client.login(username=username, password=password) - instance_json = [i for i in json_data['elements'] if i['uri_path'] == catalog_uri_path][0] + instance_json = next(i for i in json_data['elements'] if i['uri_path'] == catalog_uri_path) instance_json['title_en'] += ' (updated)' instance_json['title_de'] += ' (updated)' instance_data = {'elements': [instance_json]} @@ -114,7 +112,8 @@ def test_create_empty(db, client, username, password): url = reverse(urlnames['list']) response = client.post(url, {}, content_type='application/json') - assert response.status_code == status_map['create_error'].get(username, status_map['create_error']['default']), response.json() + assert response.status_code == status_map['create_error'].get(username, status_map['create_error']['default']), \ + response.json() @pytest.mark.parametrize('username,password', users) @@ -125,4 +124,5 @@ def test_create_error(db, client, username, password): url = reverse(urlnames['list']) response = client.post(url, json_data, content_type='application/json') - assert response.status_code == status_map['create_error'].get(username, status_map['create_error']['default']), response.json() + assert response.status_code == status_map['create_error'].get(username, status_map['create_error']['default']), \ + response.json() diff --git a/rdmo/management/tests/test_viewset_upload.py b/rdmo/management/tests/test_viewset_upload.py index 3f8ccb44f3..3715a44e0f 100644 --- a/rdmo/management/tests/test_viewset_upload.py +++ b/rdmo/management/tests/test_viewset_upload.py @@ -1,6 +1,7 @@ from pathlib import Path import pytest + from django.conf import settings from django.urls import reverse diff --git a/rdmo/management/urls/v1.py b/rdmo/management/urls/v1.py index 59458ac612..d0bee28c52 100644 --- a/rdmo/management/urls/v1.py +++ b/rdmo/management/urls/v1.py @@ -1,4 +1,5 @@ from django.urls import include, path + from rest_framework import routers from ..viewsets import ImportViewSet, MetaViewSet, UploadViewSet diff --git a/rdmo/management/views.py b/rdmo/management/views.py index a9136f9dbd..18ce86b5bf 100644 --- a/rdmo/management/views.py +++ b/rdmo/management/views.py @@ -2,16 +2,17 @@ from django.contrib.auth.mixins import LoginRequiredMixin from django.views.generic import TemplateView + from rules import test_rule -from rules.contrib.views import \ - PermissionRequiredMixin as RulesPermissionRequiredMixin +from rules.contrib.views import PermissionRequiredMixin as RulesPermissionRequiredMixin from rdmo.core.views import CSRFViewMixin, PermissionRedirectMixin logger = logging.getLogger(__name__) -class ManagementView(LoginRequiredMixin, PermissionRedirectMixin, RulesPermissionRequiredMixin, CSRFViewMixin, TemplateView): +class ManagementView(LoginRequiredMixin, PermissionRedirectMixin, RulesPermissionRequiredMixin, + CSRFViewMixin, TemplateView): template_name = 'management/management.html' def has_permission(self): diff --git a/rdmo/management/viewsets.py b/rdmo/management/viewsets.py index 414431c583..36c625dc85 100644 --- a/rdmo/management/viewsets.py +++ b/rdmo/management/viewsets.py @@ -1,6 +1,7 @@ import logging from django.utils.translation import gettext_lazy as _ + from rest_framework import viewsets from rest_framework.permissions import IsAuthenticated from rest_framework.response import Response @@ -9,8 +10,7 @@ from rdmo.conditions.models import Condition from rdmo.core.imports import handle_uploaded_file from rdmo.core.utils import get_model_field_meta, is_truthy -from rdmo.core.xml import (convert_elements, flat_xml_to_elements, - order_elements, read_xml_file) +from rdmo.core.xml import convert_elements, flat_xml_to_elements, order_elements, read_xml_file from rdmo.domain.models import Attribute from rdmo.options.models import Option, OptionSet from rdmo.questions.models import Catalog, Page, Question, QuestionSet, Section @@ -50,8 +50,8 @@ def create(self, request, *args, **kwargs): # step 1: store xml file as tmp file try: uploaded_file = request.FILES['file'] - except KeyError: - raise ValidationError({'file': [_('This field may not be blank.')]}) + except KeyError as e: + raise ValidationError({'file': [_('This field may not be blank.')]}) from e else: import_tmpfile_name = handle_uploaded_file(uploaded_file) @@ -59,20 +59,22 @@ def create(self, request, *args, **kwargs): root = read_xml_file(import_tmpfile_name) if root is None: logger.info('XML parsing error. Import failed.') - raise ValidationError({'file': [_('The content of the xml file does not consist of well formed data or markup.')]}) + raise ValidationError({'file': [ + _('The content of the xml file does not consist of well formed data or markup.') + ]}) # step 3: create element dicts from xml try: elements = flat_xml_to_elements(root) except KeyError as e: logger.info('Import failed with KeyError (%s)' % e) - raise ValidationError({'file': [_('This is not a valid RDMO XML file.')]}) + raise ValidationError({'file': [_('This is not a valid RDMO XML file.')]}) from e except TypeError as e: logger.info('Import failed with TypeError (%s)' % e) - raise ValidationError({'file': [_('This is not a valid RDMO XML file.')]}) + raise ValidationError({'file': [_('This is not a valid RDMO XML file.')]}) from e except AttributeError as e: logger.info('Import failed with AttributeError (%s)' % e) - raise ValidationError({'file': [_('This is not a valid RDMO XML file.')]}) + raise ValidationError({'file': [_('This is not a valid RDMO XML file.')]}) from e # step 4: convert elements from previous versions elements = convert_elements(elements, root.attrib.get('version')) @@ -98,10 +100,10 @@ def create(self, request, *args, **kwargs): # step 1: store xml file as tmp file try: elements = request.data['elements'] - except KeyError: - raise ValidationError({'elements': [_('This field may not be blank.')]}) - except TypeError: - raise ValidationError({'elements': [_('This is not a valid RDMO import JSON.')]}) + except KeyError as e: + raise ValidationError({'elements': [_('This field may not be blank.')]}) from e + except TypeError as e: + raise ValidationError({'elements': [_('This is not a valid RDMO import JSON.')]}) from e # step 3: import the elements import_elements(elements, user=request.user) diff --git a/rdmo/options/admin.py b/rdmo/options/admin.py index baaffdafde..bf5e21c65c 100644 --- a/rdmo/options/admin.py +++ b/rdmo/options/admin.py @@ -4,8 +4,12 @@ from rdmo.core.utils import get_language_fields from .models import Option, OptionSet, OptionSetOption -from .validators import (OptionLockedValidator, OptionSetLockedValidator, - OptionSetUniqueURIValidator, OptionUniqueURIValidator) +from .validators import ( + OptionLockedValidator, + OptionSetLockedValidator, + OptionSetUniqueURIValidator, + OptionUniqueURIValidator, +) class OptionSetAdminForm(ElementAdminForm): @@ -48,7 +52,7 @@ class OptionSetAdmin(admin.ModelAdmin): class OptionAdmin(admin.ModelAdmin): form = OptionAdminForm - search_fields = ['uri'] + get_language_fields('text') + search_fields = ['uri', *get_language_fields('text')] list_display = ('uri', 'text', 'additional_input') readonly_fields = ('uri', ) list_filter = ('editors', 'optionsets', 'additional_input') diff --git a/rdmo/options/imports.py b/rdmo/options/imports.py index fa2023a0a0..757fc34778 100644 --- a/rdmo/options/imports.py +++ b/rdmo/options/imports.py @@ -2,15 +2,23 @@ from django.contrib.sites.models import Site -from rdmo.core.imports import (check_permissions, set_common_fields, - set_lang_field, set_m2m_instances, - set_m2m_through_instances, - set_reverse_m2m_through_instance, - validate_instance) +from rdmo.core.imports import ( + check_permissions, + set_common_fields, + set_lang_field, + set_m2m_instances, + set_m2m_through_instances, + set_reverse_m2m_through_instance, + validate_instance, +) from .models import Option, OptionSet -from .validators import (OptionLockedValidator, OptionSetLockedValidator, - OptionSetUniqueURIValidator, OptionUniqueURIValidator) +from .validators import ( + OptionLockedValidator, + OptionSetLockedValidator, + OptionSetUniqueURIValidator, + OptionUniqueURIValidator, +) logger = logging.getLogger(__name__) diff --git a/rdmo/options/models.py b/rdmo/options/models.py index 565d30ec20..f6b77f67eb 100644 --- a/rdmo/options/models.py +++ b/rdmo/options/models.py @@ -226,7 +226,7 @@ def text(self): @property def label(self): - return '%s ("%s")' % (self.uri, self.text) + return f'{self.uri} ("{self.text}")' @property def is_locked(self): diff --git a/rdmo/options/serializers/v1/__init__.py b/rdmo/options/serializers/v1/__init__.py index 2dc884099a..def6624fd8 100644 --- a/rdmo/options/serializers/v1/__init__.py +++ b/rdmo/options/serializers/v1/__init__.py @@ -1,3 +1,2 @@ from .option import OptionIndexSerializer, OptionSerializer -from .optionset import (OptionSetIndexSerializer, OptionSetNestedSerializer, - OptionSetSerializer) +from .optionset import OptionSetIndexSerializer, OptionSetNestedSerializer, OptionSetSerializer diff --git a/rdmo/options/serializers/v1/option.py b/rdmo/options/serializers/v1/option.py index d685a1d40b..b608dcdd5b 100644 --- a/rdmo/options/serializers/v1/option.py +++ b/rdmo/options/serializers/v1/option.py @@ -1,10 +1,12 @@ from rest_framework import serializers -from rdmo.core.serializers import (ElementModelSerializerMixin, - ElementWarningSerializerMixin, - ReadOnlyObjectPermissionSerializerMixin, - ThroughModelSerializerMixin, - TranslationSerializerMixin) +from rdmo.core.serializers import ( + ElementModelSerializerMixin, + ElementWarningSerializerMixin, + ReadOnlyObjectPermissionSerializerMixin, + ThroughModelSerializerMixin, + TranslationSerializerMixin, +) from ...models import Option, OptionSet from ...validators import OptionLockedValidator, OptionUniqueURIValidator diff --git a/rdmo/options/serializers/v1/optionset.py b/rdmo/options/serializers/v1/optionset.py index 885a439c53..264c2f7128 100644 --- a/rdmo/options/serializers/v1/optionset.py +++ b/rdmo/options/serializers/v1/optionset.py @@ -1,8 +1,10 @@ from rest_framework import serializers -from rdmo.core.serializers import (ElementModelSerializerMixin, - ReadOnlyObjectPermissionSerializerMixin, - ThroughModelSerializerMixin) +from rdmo.core.serializers import ( + ElementModelSerializerMixin, + ReadOnlyObjectPermissionSerializerMixin, + ThroughModelSerializerMixin, +) from rdmo.questions.models import Question from ...models import OptionSet, OptionSetOption @@ -64,8 +66,9 @@ class OptionSetNestedSerializer(OptionSetSerializer): elements = serializers.SerializerMethodField() class Meta(OptionSetSerializer.Meta): - fields = OptionSetSerializer.Meta.fields + ( - 'elements', + fields = ( + *OptionSetSerializer.Meta.fields, + 'elements' ) def get_elements(self, obj): diff --git a/rdmo/options/tests/test_validator_locked_options.py b/rdmo/options/tests/test_validator_locked_options.py index 7ba31764e8..4af2ecacba 100644 --- a/rdmo/options/tests/test_validator_locked_options.py +++ b/rdmo/options/tests/test_validator_locked_options.py @@ -1,7 +1,8 @@ import pytest + from django.core.exceptions import ValidationError -from rest_framework.exceptions import \ - ValidationError as RestFameworkValidationError + +from rest_framework.exceptions import ValidationError as RestFameworkValidationError from ..models import Option, OptionSet from ..serializers.v1 import OptionSerializer diff --git a/rdmo/options/tests/test_validator_locked_optionsets.py b/rdmo/options/tests/test_validator_locked_optionsets.py index f9ca4660da..511b2b733e 100644 --- a/rdmo/options/tests/test_validator_locked_optionsets.py +++ b/rdmo/options/tests/test_validator_locked_optionsets.py @@ -1,7 +1,8 @@ import pytest + from django.core.exceptions import ValidationError -from rest_framework.exceptions import \ - ValidationError as RestFameworkValidationError + +from rest_framework.exceptions import ValidationError as RestFameworkValidationError from ..models import OptionSet from ..serializers.v1 import OptionSetSerializer diff --git a/rdmo/options/tests/test_validator_unique_uri_options.py b/rdmo/options/tests/test_validator_unique_uri_options.py index cd807cd620..b29ec193c3 100644 --- a/rdmo/options/tests/test_validator_unique_uri_options.py +++ b/rdmo/options/tests/test_validator_unique_uri_options.py @@ -1,8 +1,9 @@ import pytest + from django.conf import settings from django.core.exceptions import ValidationError -from rest_framework.exceptions import \ - ValidationError as RestFameworkValidationError + +from rest_framework.exceptions import ValidationError as RestFameworkValidationError from ..models import Option, OptionSet from ..serializers.v1 import OptionSerializer diff --git a/rdmo/options/tests/test_validator_unique_uri_optionsets.py b/rdmo/options/tests/test_validator_unique_uri_optionsets.py index f7ef3ac7b2..bf77068096 100644 --- a/rdmo/options/tests/test_validator_unique_uri_optionsets.py +++ b/rdmo/options/tests/test_validator_unique_uri_optionsets.py @@ -1,10 +1,11 @@ import pytest + from django.conf import settings from django.core.exceptions import ValidationError -from rest_framework.exceptions import \ - ValidationError as RestFameworkValidationError -from ..models import OptionSet, Option +from rest_framework.exceptions import ValidationError as RestFameworkValidationError + +from ..models import Option, OptionSet from ..serializers.v1 import OptionSetSerializer from ..validators import OptionSetUniqueURIValidator diff --git a/rdmo/options/tests/test_viewset_options.py b/rdmo/options/tests/test_viewset_options.py index 833b68b084..47542460cd 100644 --- a/rdmo/options/tests/test_viewset_options.py +++ b/rdmo/options/tests/test_viewset_options.py @@ -1,6 +1,7 @@ import xml.etree.ElementTree as et import pytest + from django.db.models import Max from django.urls import reverse @@ -98,7 +99,7 @@ def test_create(db, client, username, password): url = reverse(urlnames['list']) data = { 'uri_prefix': instance.uri_prefix, - 'uri_path': '%s_new_%s' % (instance.uri_path, username), + 'uri_path': f'{instance.uri_path}_new_{username}', 'comment': instance.comment, 'text_en': instance.text_lang1, 'text_de': instance.text_lang2 @@ -121,7 +122,7 @@ def test_create_optionset(db, client, username, password): url = reverse(urlnames['list']) data = { 'uri_prefix': instance.uri_prefix, - 'uri_path': '%s_new_%s' % (instance.uri_path, username), + 'uri_path': f'{instance.uri_path}_new_{username}', 'comment': instance.comment, 'text_en': instance.text_lang1, 'text_de': instance.text_lang2, @@ -133,7 +134,7 @@ def test_create_optionset(db, client, username, password): if response.status_code == 201: new_instance = Option.objects.get(id=response.json().get('id')) optionset.refresh_from_db() - assert catalog_sections + [(new_instance.id, order)] == \ + assert [*catalog_sections, (new_instance.id, order)] == \ list(optionset.optionset_options.values_list('option', 'order')) diff --git a/rdmo/options/tests/test_viewset_options_multisite.py b/rdmo/options/tests/test_viewset_options_multisite.py index bae8a3aba9..9062de7f74 100644 --- a/rdmo/options/tests/test_viewset_options_multisite.py +++ b/rdmo/options/tests/test_viewset_options_multisite.py @@ -1,14 +1,13 @@ import xml.etree.ElementTree as et import pytest -from django.urls import reverse -from ..models import Option +from django.urls import reverse +from ...core.tests import get_obj_perms_status_code from ...core.tests import multisite_status_map as status_map from ...core.tests import multisite_users as users -from ...core.tests import get_obj_perms_status_code - +from ..models import Option from .test_viewset_options import urlnames @@ -65,7 +64,7 @@ def test_create(db, client, username, password): url = reverse(urlnames['list']) data = { 'uri_prefix': instance.uri_prefix, - 'uri_path': '%s_new_%s' % (instance.uri_path, username), + 'uri_path': f'{instance.uri_path}_new_{username}', 'comment': instance.comment, 'text_en': instance.text_lang1, 'text_de': instance.text_lang2 diff --git a/rdmo/options/tests/test_viewset_optionsets.py b/rdmo/options/tests/test_viewset_optionsets.py index 881d35d9d8..8ad51c009b 100644 --- a/rdmo/options/tests/test_viewset_optionsets.py +++ b/rdmo/options/tests/test_viewset_optionsets.py @@ -1,6 +1,7 @@ import xml.etree.ElementTree as et import pytest + from django.urls import reverse from ..models import OptionSet @@ -109,7 +110,7 @@ def test_create(db, client, username, password): url = reverse(urlnames['list']) data = { 'uri_prefix': instance.uri_prefix, - 'uri_path': '%s_new_%s' % (instance.uri_path, username), + 'uri_path': f'{instance.uri_path}_new_{username}', 'comment': instance.comment, 'order': instance.order } @@ -128,7 +129,7 @@ def test_create_question(db, client, username, password): url = reverse(urlnames['list']) data = { 'uri_prefix': instance.uri_prefix, - 'uri_path': '%s_new_%s' % (instance.uri_path, username), + 'uri_path': f'{instance.uri_path}_new_{username}', 'comment': instance.comment, 'order': instance.order, 'questions': [question.id] @@ -156,7 +157,7 @@ def test_create_m2m(db, client, username, password): url = reverse(urlnames['list']) data = { 'uri_prefix': instance.uri_prefix, - 'uri_path': '%s_new_%s' % (instance.uri_path, username), + 'uri_path': f'{instance.uri_path}_new_{username}', 'comment': instance.comment, 'order': instance.order, 'options': optionset_options, diff --git a/rdmo/options/tests/test_viewset_optionsets_multisite.py b/rdmo/options/tests/test_viewset_optionsets_multisite.py index 483b8e3005..2c74bd8e79 100644 --- a/rdmo/options/tests/test_viewset_optionsets_multisite.py +++ b/rdmo/options/tests/test_viewset_optionsets_multisite.py @@ -1,16 +1,13 @@ import xml.etree.ElementTree as et import pytest -from django.contrib.sites.models import Site -from django.urls import reverse -from ..models import OptionSet +from django.urls import reverse +from ...core.tests import get_obj_perms_status_code from ...core.tests import multisite_status_map as status_map from ...core.tests import multisite_users as users -from ...core.tests import get_obj_perms_status_code - -from .test_viewset_optionsets import export_formats +from ..models import OptionSet from .test_viewset_optionsets import urlnames @@ -78,7 +75,7 @@ def test_create(db, client, username, password): url = reverse(urlnames['list']) data = { 'uri_prefix': instance.uri_prefix, - 'uri_path': '%s_new_%s' % (instance.uri_path, username), + 'uri_path': f'{instance.uri_path}_new_{username}', 'comment': instance.comment, 'order': instance.order, 'conditions': [condition.pk for condition in instance.conditions.all()], diff --git a/rdmo/options/urls/v1.py b/rdmo/options/urls/v1.py index 5a5eb1ce11..88e879bb23 100644 --- a/rdmo/options/urls/v1.py +++ b/rdmo/options/urls/v1.py @@ -1,4 +1,5 @@ from django.urls import include, path + from rest_framework import routers from ..viewsets import OptionSetViewSet, OptionViewSet, ProviderViewSet diff --git a/rdmo/options/viewsets.py b/rdmo/options/viewsets.py index 8e423b85de..804e003fcc 100644 --- a/rdmo/options/viewsets.py +++ b/rdmo/options/viewsets.py @@ -1,12 +1,14 @@ from django.conf import settings from django.db import models -from django_filters.rest_framework import DjangoFilterBackend + from rest_framework.decorators import action from rest_framework.filters import SearchFilter from rest_framework.permissions import IsAuthenticated from rest_framework.response import Response from rest_framework.viewsets import ModelViewSet +from django_filters.rest_framework import DjangoFilterBackend + from rdmo.core.exports import XMLResponse from rdmo.core.permissions import HasModelPermission, HasObjectPermission from rdmo.core.utils import is_truthy, render_to_format @@ -14,11 +16,14 @@ from .models import Option, OptionSet from .renderers import OptionRenderer, OptionSetRenderer -from .serializers.export import (OptionExportSerializer, - OptionSetExportSerializer) -from .serializers.v1 import (OptionIndexSerializer, OptionSerializer, - OptionSetIndexSerializer, - OptionSetNestedSerializer, OptionSetSerializer) +from .serializers.export import OptionExportSerializer, OptionSetExportSerializer +from .serializers.v1 import ( + OptionIndexSerializer, + OptionSerializer, + OptionSetIndexSerializer, + OptionSetNestedSerializer, + OptionSetSerializer, +) class OptionSetViewSet(ModelViewSet): @@ -70,9 +75,11 @@ def detail_export(self, request, pk=None, export_format='xml'): xml = OptionSetRenderer().render([serializer.data], context=self.get_export_renderer_context(request)) return XMLResponse(xml, name=self.get_object().uri_path) else: - return render_to_format(self.request, export_format, self.get_object().uri_path, 'options/export/optionsets.html', { - 'optionsets': [self.get_object()] - }) + return render_to_format( + self.request, export_format, self.get_object().uri_path, 'options/export/optionsets.html', { + 'optionsets': [self.get_object()] + } + ) def get_export_renderer_context(self, request): full = is_truthy(request.GET.get('full')) @@ -127,9 +134,11 @@ def detail_export(self, request, pk=None, export_format='xml'): xml = OptionRenderer().render([serializer.data]) return XMLResponse(xml, name=self.get_object().uri_path) else: - return render_to_format(self.request, export_format, self.get_object().uri_path, 'options/export/options.html', { - 'options': [self.get_object()] - }) + return render_to_format( + self.request, export_format, self.get_object().uri_path, 'options/export/options.html', { + 'options': [self.get_object()] + } + ) class ProviderViewSet(ChoicesViewSet): diff --git a/rdmo/overlays/models.py b/rdmo/overlays/models.py index 86371ccfb4..16703a497b 100644 --- a/rdmo/overlays/models.py +++ b/rdmo/overlays/models.py @@ -33,4 +33,4 @@ class Meta: verbose_name_plural = _('Overlays') def __str__(self): - return '{} / {}'.format(self.user, self.url_name) + return f'{self.user} / {self.url_name}' diff --git a/rdmo/overlays/tests/test_views.py b/rdmo/overlays/tests/test_views.py index 8d3a26a7ae..8f6fa8aa0e 100644 --- a/rdmo/overlays/tests/test_views.py +++ b/rdmo/overlays/tests/test_views.py @@ -1,4 +1,5 @@ import pytest + from django.urls import reverse from ..models import Overlay diff --git a/rdmo/overlays/tests/test_viewsets.py b/rdmo/overlays/tests/test_viewsets.py index f875a66d6d..931a71944a 100644 --- a/rdmo/overlays/tests/test_viewsets.py +++ b/rdmo/overlays/tests/test_viewsets.py @@ -1,4 +1,5 @@ import pytest + from django.urls import reverse from ..models import Overlay diff --git a/rdmo/overlays/urls/v1.py b/rdmo/overlays/urls/v1.py index b560efb73b..9054f26c1d 100644 --- a/rdmo/overlays/urls/v1.py +++ b/rdmo/overlays/urls/v1.py @@ -1,4 +1,5 @@ from django.urls import include, path + from rest_framework import routers from ..viewsets import OverlayViewSet diff --git a/rdmo/overlays/viewsets.py b/rdmo/overlays/viewsets.py index 120c8692b2..20a1aa3895 100644 --- a/rdmo/overlays/viewsets.py +++ b/rdmo/overlays/viewsets.py @@ -1,5 +1,6 @@ from django.conf import settings from django.contrib.sites.models import Site + from rest_framework.decorators import action from rest_framework.exceptions import NotFound from rest_framework.permissions import IsAuthenticated @@ -11,7 +12,8 @@ class OverlayViewSet(ViewSet): - @action(detail=False, methods=['post'], url_path='(?P[-\\w]+)/current', permission_classes=[IsAuthenticated]) + @action(detail=False, methods=['post'], url_path='(?P[-\\w]+)/current', + permission_classes=[IsAuthenticated]) def current(self, request, url_name=None): site = Site.objects.get_current() overlays = settings.OVERLAYS.get(url_name)[:] @@ -29,7 +31,8 @@ def current(self, request, url_name=None): 'last': overlay.current == overlays[-1] }) - @action(detail=False, methods=['post'], url_path='(?P[-\\w]+)/next', permission_classes=[IsAuthenticated]) + @action(detail=False, methods=['post'], url_path='(?P[-\\w]+)/next', + permission_classes=[IsAuthenticated]) def next(self, request, url_name=None): site = Site.objects.get_current() overlays = settings.OVERLAYS.get(url_name)[:] @@ -51,7 +54,8 @@ def next(self, request, url_name=None): 'last': overlay.current == overlays[-1] }) - @action(detail=False, methods=['post'], url_path='(?P[-\\w]+)/dismiss', permission_classes=[IsAuthenticated]) + @action(detail=False, methods=['post'], url_path='(?P[-\\w]+)/dismiss', + permission_classes=[IsAuthenticated]) def dismiss(self, request, url_name=None): site = Site.objects.get_current() overlays = settings.OVERLAYS.get(url_name)[:] diff --git a/rdmo/projects/admin.py b/rdmo/projects/admin.py index 70d2af6cda..e8890cf19d 100644 --- a/rdmo/projects/admin.py +++ b/rdmo/projects/admin.py @@ -1,9 +1,18 @@ from django.contrib import admin from django.db.models import Prefetch -from .models import (Continuation, Integration, IntegrationOption, Invite, - Issue, IssueResource, Membership, Project, Snapshot, - Value) +from .models import ( + Continuation, + Integration, + IntegrationOption, + Invite, + Issue, + IssueResource, + Membership, + Project, + Snapshot, + Value, +) class ProjectAdmin(admin.ModelAdmin): diff --git a/rdmo/projects/apps.py b/rdmo/projects/apps.py index a64d38c848..178bb4ccd3 100644 --- a/rdmo/projects/apps.py +++ b/rdmo/projects/apps.py @@ -8,7 +8,7 @@ class ProjectsConfig(AppConfig): verbose_name = _('Projects') def ready(self): - from . import rules # noqa: F401 + from . import rules # noqa: F401 if settings.PROJECT_REMOVE_VIEWS: - from . import handlers # noqa: F401 + from . import handlers # noqa: F401 diff --git a/rdmo/projects/exports.py b/rdmo/projects/exports.py index 52f728a850..6be2125f7c 100644 --- a/rdmo/projects/exports.py +++ b/rdmo/projects/exports.py @@ -5,8 +5,8 @@ from rdmo.core.exports import prettify_xml from rdmo.core.plugins import Plugin from rdmo.core.utils import render_to_csv, render_to_json -from rdmo.views.utils import ProjectWrapper from rdmo.views.templatetags import view_tags +from rdmo.views.utils import ProjectWrapper from .renderers import XMLRenderer from .serializers.export import ProjectSerializer as ExportSerializer @@ -31,7 +31,8 @@ def get_set(self, path, set_prefix=''): .order_by('set_index', 'collection_index') def get_values(self, path, set_prefix='', set_index=0): - return self.project.values.filter(snapshot=self.snapshot, attribute__path=path, set_prefix=set_prefix, set_index=set_index) \ + return self.project.values.filter(snapshot=self.snapshot, attribute__path=path, + set_prefix=set_prefix, set_index=set_index) \ .order_by('collection_index') def get_value(self, path, set_prefix='', set_index=0, collection_index=0): diff --git a/rdmo/projects/filters.py b/rdmo/projects/filters.py index 843bc576fb..064f712363 100644 --- a/rdmo/projects/filters.py +++ b/rdmo/projects/filters.py @@ -1,6 +1,7 @@ -from django_filters import CharFilter, FilterSet from rest_framework.filters import BaseFilterBackend +from django_filters import CharFilter, FilterSet + from .models import Project diff --git a/rdmo/projects/forms.py b/rdmo/projects/forms.py index 7ef1761f3a..a6e32f7195 100644 --- a/rdmo/projects/forms.py +++ b/rdmo/projects/forms.py @@ -12,8 +12,7 @@ from rdmo.core.utils import markdown2html from .constants import ROLE_CHOICES -from .models import (Integration, IntegrationOption, Invite, Membership, - Project, Snapshot) +from .models import Integration, IntegrationOption, Invite, Membership, Project, Snapshot class CatalogChoiceField(forms.ModelChoiceField): @@ -22,21 +21,23 @@ class CatalogChoiceField(forms.ModelChoiceField): def label_from_instance(self, obj): if obj.available is False: - return mark_safe('
    %s%s
    %s
    ' % (obj.title, self._unavailable_icon, markdown2html(obj.help))) + return mark_safe('
    {}{}
    {}
    '.format( + obj.title, self._unavailable_icon, markdown2html(obj.help) + )) - return mark_safe('%s
    %s' % (obj.title, markdown2html(obj.help))) + return mark_safe(f'{obj.title}
    {markdown2html(obj.help)}') class TasksMultipleChoiceField(forms.ModelMultipleChoiceField): def label_from_instance(self, obj): - return mark_safe('%s
    %s' % (obj.title, markdown2html(obj.text))) + return mark_safe(f'{obj.title}
    {markdown2html(obj.text)}') class ViewsMultipleChoiceField(forms.ModelMultipleChoiceField): def label_from_instance(self, obj): - return mark_safe('%s
    %s' % (obj.title, markdown2html(obj.help))) + return mark_safe(f'{obj.title}
    {markdown2html(obj.help)}') class ProjectForm(forms.ModelForm): @@ -166,11 +167,11 @@ class Meta: def __init__(self, *args, **kwargs): self.project = kwargs.pop('project') - super(SnapshotCreateForm, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) def save(self, *args, **kwargs): self.instance.project = self.project - return super(SnapshotCreateForm, self).save(*args, **kwargs) + return super().save(*args, **kwargs) class MembershipCreateForm(forms.Form): @@ -192,7 +193,8 @@ def __init__(self, *args, **kwargs): self.fields['silent'] = forms.BooleanField( required=False, label=_('Add member silently'), - help_text=_('As site manager or admin, you can directly add users without notifying them via e-mail, when you check the following checkbox.') + help_text=_('As site manager or admin, you can directly add users without notifying them via e-mail, ' + 'when you check the following checkbox.') ) def clean_username_or_email(self): @@ -201,13 +203,14 @@ def clean_username_or_email(self): # check if it is a registered user try: - self.cleaned_data['user'] = usermodel.objects.get(Q(username=username_or_email) | Q(email__iexact=username_or_email)) + self.cleaned_data['user'] = usermodel.objects.get(Q(username=username_or_email) | + Q(email__iexact=username_or_email)) self.cleaned_data['email'] = self.cleaned_data['user'].email if self.cleaned_data['user'] in self.project.user.all(): raise ValidationError(_('The user is already a member of the project.')) - except (usermodel.DoesNotExist, usermodel.MultipleObjectsReturned): + except (usermodel.DoesNotExist, usermodel.MultipleObjectsReturned) as e: if settings.PROJECT_SEND_INVITE: # check if it is a valid email address, this will raise the correct ValidationError EmailValidator()(username_or_email) @@ -217,7 +220,8 @@ def clean_username_or_email(self): else: self.cleaned_data['user'] = None self.cleaned_data['email'] = None - raise ValidationError(_('A user with this username or e-mail was not found. Only registered users can be invited.')) + raise ValidationError(_('A user with this username or e-mail was not found. ' + 'Only registered users can be invited.')) from e def clean(self): if self.cleaned_data.get('silent') is True and self.cleaned_data.get('user') is None: diff --git a/rdmo/projects/handlers.py b/rdmo/projects/handlers.py index b6cf047684..6f65709cbe 100644 --- a/rdmo/projects/handlers.py +++ b/rdmo/projects/handlers.py @@ -5,8 +5,8 @@ from django.db.models.signals import m2m_changed from django.dispatch import receiver +from rdmo.projects.models import Membership, Project from rdmo.questions.models import Catalog -from rdmo.projects.models import Project, Membership from rdmo.views.models import View logger = logging.getLogger(__name__) diff --git a/rdmo/projects/imports.py b/rdmo/projects/imports.py index 1ace407925..3b20736a4e 100644 --- a/rdmo/projects/imports.py +++ b/rdmo/projects/imports.py @@ -4,15 +4,15 @@ import mimetypes from urllib.parse import quote -import requests - from django import forms from django.core.files import File from django.shortcuts import redirect, render from django.utils.translation import gettext_lazy as _ -from rdmo.core.plugins import Plugin +import requests + from rdmo.core.imports import handle_fetched_file +from rdmo.core.plugins import Plugin from rdmo.core.xml import get_ns_map, get_uri, read_xml_file from rdmo.domain.models import Attribute from rdmo.options.models import Option @@ -20,6 +20,7 @@ from rdmo.services.providers import GitHubProviderMixin, GitLabProviderMixin from rdmo.tasks.models import Task from rdmo.views.models import View + from .models import Project, Snapshot, Value log = logging.getLogger(__name__) diff --git a/rdmo/projects/management/commands/export_projects.py b/rdmo/projects/management/commands/export_projects.py index e65fd0b83f..f754b3f433 100644 --- a/rdmo/projects/management/commands/export_projects.py +++ b/rdmo/projects/management/commands/export_projects.py @@ -1,5 +1,4 @@ import logging - from pathlib import Path from django.conf import settings @@ -14,7 +13,6 @@ from rdmo.views.models import View from rdmo.views.utils import ProjectWrapper - logger = logging.getLogger(__name__) @@ -53,7 +51,7 @@ def export_answers(self): current_snapshot = None if self.format not in dict(settings.EXPORT_FORMATS): - raise CommandError('Format "{}" is not supported for answers.'.format(self.format)) + raise CommandError(f'Format "{self.format}" is not supported for answers.') for project in self.get_queryset(): context = { @@ -65,19 +63,20 @@ def export_answers(self): 'resource_path': get_value_path(project, current_snapshot) } - response = render_to_format(None, context['format'], context['title'], 'projects/project_answers_export.html', context) + response = render_to_format(None, context['format'], context['title'], + 'projects/project_answers_export.html', context) self.write_file(self.path / str(project.id) / 'answers', response) def export_view(self, key): current_snapshot = None if self.format not in dict(settings.EXPORT_FORMATS): - raise CommandError('Format "{}" is not supported for answers.'.format(self.format)) + raise CommandError(f'Format "{self.format}" is not supported for answers.') try: view = View.objects.get(key=key) - except View.DoesNotExist: - raise CommandError('A view with the key "{}" was not found.'.format(key)) + except View.DoesNotExist as e: + raise CommandError(f'A view with the key "{key}" was not found.') from e for project in self.get_queryset(): context = { @@ -91,14 +90,15 @@ def export_view(self, key): 'resource_path': get_value_path(project, current_snapshot) } - response = render_to_format(None, context['format'], context['title'], 'projects/project_view_export.html', context) + response = render_to_format(None, context['format'], context['title'], + 'projects/project_view_export.html', context) self.write_file(self.path / str(project.id) / key, response) def export_projects(self): for project in self.get_queryset(): export_plugin = get_plugin('PROJECT_EXPORTS', self.format) if export_plugin is None: - raise CommandError('Format "{}" is not supported.'.format(self.format)) + raise CommandError(f'Format "{self.format}" is not supported.') export_plugin.project = project export_plugin.snapshot = None @@ -111,7 +111,7 @@ def write_file(self, path, response): file_path = path / file_name file_path.parent.mkdir(exist_ok=True, parents=True) - print('Writing {}'.format(file_path)) + print(f'Writing {file_path}') with file_path.open('wb') as fp: fp.write(response.content) diff --git a/rdmo/projects/management/commands/find_inactive_projects.py b/rdmo/projects/management/commands/find_inactive_projects.py index 63f1713fac..a540846022 100644 --- a/rdmo/projects/management/commands/find_inactive_projects.py +++ b/rdmo/projects/management/commands/find_inactive_projects.py @@ -1,14 +1,12 @@ -import sys import csv - +import sys from datetime import datetime -import pytz - +from django.core.management.base import BaseCommand from django.db import models from django.db.models.functions import Greatest -from django.core.management.base import BaseCommand +import pytz from rdmo.projects.models import Project, Value diff --git a/rdmo/projects/management/commands/prune_projects.py b/rdmo/projects/management/commands/prune_projects.py index c101cc91c7..41cd724ee3 100644 --- a/rdmo/projects/management/commands/prune_projects.py +++ b/rdmo/projects/management/commands/prune_projects.py @@ -1,6 +1,6 @@ from django.core.management.base import BaseCommand, CommandError -from rdmo.projects.models import Project, Membership +from rdmo.projects.models import Membership, Project class Command(BaseCommand): @@ -36,7 +36,7 @@ def handle(self, *args, **options): self.stdout.write('Found projects without %s:' % (roles)) for proj in candidates: - self.stdout.write('%s (id=%s)' % (proj, proj.id)) + self.stdout.write(f'{proj} (id={proj.id})') if options['remove']: self.stdout.write('...removing...', ending='') proj.delete() diff --git a/rdmo/projects/managers.py b/rdmo/projects/managers.py index 95bd5c763d..8ee22b0da2 100644 --- a/rdmo/projects/managers.py +++ b/rdmo/projects/managers.py @@ -1,5 +1,6 @@ from django.conf import settings from django.db import models + from mptt.models import TreeManager from mptt.querysets import TreeQuerySet diff --git a/rdmo/projects/mixins.py b/rdmo/projects/mixins.py index b7be2b3b46..5a18cab513 100644 --- a/rdmo/projects/mixins.py +++ b/rdmo/projects/mixins.py @@ -3,8 +3,8 @@ from django.contrib.sites.shortcuts import get_current_site from django.core.exceptions import ValidationError -from django.http import HttpResponseRedirect, Http404 -from django.shortcuts import get_object_or_404, render, redirect +from django.http import Http404, HttpResponseRedirect +from django.shortcuts import get_object_or_404, redirect, render from django.utils.translation import gettext_lazy as _ from rdmo.core.imports import handle_uploaded_file @@ -12,11 +12,10 @@ from rdmo.questions.models import Question from .models import Membership, Project -from .utils import (save_import_snapshot_values, save_import_tasks, - save_import_values, save_import_views) +from .utils import save_import_snapshot_values, save_import_tasks, save_import_values, save_import_views -class ProjectImportMixin(object): +class ProjectImportMixin: def get_current_values(self, current_project): queryset = current_project.values.filter(snapshot=None).select_related('attribute', 'option') @@ -116,7 +115,8 @@ def import_form(self): self.request.session['import_key'] = import_key # attach questions and current values - self.update_values(current_project, import_plugin.catalog, import_plugin.values, import_plugin.snapshots) + self.update_values(current_project, import_plugin.catalog, + import_plugin.values, import_plugin.snapshots) return render(self.request, 'projects/project_import.html', { 'method': 'import_file', @@ -168,7 +168,8 @@ def import_file(self): }, status=400) # attach questions and current values - self.update_values(current_project, import_plugin.catalog, import_plugin.values, import_plugin.snapshots) + self.update_values(current_project, import_plugin.catalog, + import_plugin.values, import_plugin.snapshots) if current_project: save_import_values(self.object, import_plugin.values, checked) diff --git a/rdmo/projects/models/continuation.py b/rdmo/projects/models/continuation.py index e01b858970..05b32baf12 100644 --- a/rdmo/projects/models/continuation.py +++ b/rdmo/projects/models/continuation.py @@ -30,4 +30,4 @@ class Meta: verbose_name_plural = _('Continuations') def __str__(self): - return '%s/%s/%s' % (self.project, self.user, self.page) + return f'{self.project}/{self.user}/{self.page}' diff --git a/rdmo/projects/models/integration.py b/rdmo/projects/models/integration.py index b8e29c2926..b8cdc52169 100644 --- a/rdmo/projects/models/integration.py +++ b/rdmo/projects/models/integration.py @@ -27,7 +27,7 @@ class Meta: verbose_name_plural = _('Integrations') def __str__(self): - return '%s / %s' % (self.project.title, self.provider_key) + return f'{self.project.title} / {self.provider_key}' @property def provider(self): @@ -76,4 +76,4 @@ class Meta: verbose_name_plural = _('Integration options') def __str__(self): - return '%s / %s / %s = %s' % (self.integration.project.title, self.integration.provider_key, self.key, self.value) + return f'{self.integration.project.title} / {self.integration.provider_key} / {self.key} = {self.value}' diff --git a/rdmo/projects/models/invite.py b/rdmo/projects/models/invite.py index 174868e580..1c1c202b79 100644 --- a/rdmo/projects/models/invite.py +++ b/rdmo/projects/models/invite.py @@ -51,7 +51,7 @@ class Meta: verbose_name_plural = _('Invites') def __str__(self): - return '%s / %s / %s' % (self.project.title, self.email, self.role) + return f'{self.project.title} / {self.email} / {self.role}' def save(self, *args, **kwargs): if self.timestamp is None: diff --git a/rdmo/projects/models/issue.py b/rdmo/projects/models/issue.py index 0d0c1ab3fa..bfaf95474d 100644 --- a/rdmo/projects/models/issue.py +++ b/rdmo/projects/models/issue.py @@ -45,7 +45,7 @@ class Meta: verbose_name_plural = _('Issues') def __str__(self): - return '%s / %s / %s' % (self.project.title, self.task, self.status) + return f'{self.project.title} / {self.task} / {self.status}' def get_absolute_url(self): return reverse('project', kwargs={'pk': self.project.pk}) @@ -118,4 +118,4 @@ class Meta: verbose_name_plural = _('Issue resources') def __str__(self): - return '%s / %s / %s' % (self.issue.project.title, self.issue, self.url) + return f'{self.issue.project.title} / {self.issue} / {self.url}' diff --git a/rdmo/projects/models/membership.py b/rdmo/projects/models/membership.py index 0ec26a92f1..f794dff3f9 100644 --- a/rdmo/projects/models/membership.py +++ b/rdmo/projects/models/membership.py @@ -39,7 +39,7 @@ class Meta: verbose_name_plural = _('Memberships') def __str__(self): - return '%s / %s / %s' % (self.project.title, self.user.username, self.role) + return f'{self.project.title} / {self.user.username} / {self.role}' def get_absolute_url(self): return reverse('project', kwargs={'pk': self.project.pk}) diff --git a/rdmo/projects/models/project.py b/rdmo/projects/models/project.py index e1e1afbfb7..eae2128fe2 100644 --- a/rdmo/projects/models/project.py +++ b/rdmo/projects/models/project.py @@ -8,7 +8,9 @@ from django.urls import reverse from django.utils.functional import cached_property from django.utils.translation import gettext_lazy as _ + from mptt.models import MPTTModel, TreeForeignKey + from rdmo.core.models import Model from rdmo.domain.models import Attribute from rdmo.questions.models import Catalog, Question @@ -88,7 +90,8 @@ def clean(self): def progress(self): # create a queryset for the attributes of the catalog for this project # the subquery is used to query only attributes which have a question in the catalog, which is not optional - questions = Question.objects.filter_by_catalog(self.catalog).filter(attribute_id=OuterRef('pk')).exclude(is_optional=True) + questions = Question.objects.filter_by_catalog(self.catalog) \ + .filter(attribute_id=OuterRef('pk')).exclude(is_optional=True) attributes = Attribute.objects.annotate(active=Exists(questions)).filter(active=True).distinct() # query the total number of attributes from the qs above diff --git a/rdmo/projects/models/snapshot.py b/rdmo/projects/models/snapshot.py index 284f9947cd..0403da40cc 100644 --- a/rdmo/projects/models/snapshot.py +++ b/rdmo/projects/models/snapshot.py @@ -34,7 +34,7 @@ class Meta: verbose_name_plural = _('Snapshots') def __str__(self): - return '%s / %s' % (self.project.title, self.title) + return f'{self.project.title} / {self.title}' def get_absolute_url(self): return reverse('project', kwargs={'pk': self.project.pk}) diff --git a/rdmo/projects/models/value.py b/rdmo/projects/models/value.py index 9540cd72d7..39fec66834 100644 --- a/rdmo/projects/models/value.py +++ b/rdmo/projects/models/value.py @@ -1,14 +1,14 @@ import mimetypes from pathlib import Path -import iso8601 from django.db import models from django.urls import reverse from django.utils.translation import gettext_lazy as _ + +import iso8601 from django_cleanup import cleanup -from rdmo.core.constants import (VALUE_TYPE_BOOLEAN, VALUE_TYPE_CHOICES, - VALUE_TYPE_DATETIME, VALUE_TYPE_TEXT) +from rdmo.core.constants import VALUE_TYPE_BOOLEAN, VALUE_TYPE_CHOICES, VALUE_TYPE_DATETIME, VALUE_TYPE_TEXT from rdmo.core.models import Model from rdmo.domain.models import Attribute from rdmo.options.models import Option @@ -170,7 +170,7 @@ def value_and_unit(self): if value is None: return '' elif self.unit: - return '%s %s' % (value, self.unit) + return f'{value} {self.unit}' else: return value diff --git a/rdmo/projects/permissions.py b/rdmo/projects/permissions.py index 54aaaf8e8e..c549b02e49 100644 --- a/rdmo/projects/permissions.py +++ b/rdmo/projects/permissions.py @@ -1,4 +1,4 @@ -from rdmo.core.permissions import log_result, HasObjectPermission +from rdmo.core.permissions import HasObjectPermission, log_result class HasProjectsPermission(HasObjectPermission): diff --git a/rdmo/projects/providers.py b/rdmo/projects/providers.py index 9bbadf0447..a891ffc8ee 100644 --- a/rdmo/projects/providers.py +++ b/rdmo/projects/providers.py @@ -1,13 +1,14 @@ import hmac import json -from urllib.parse import quote, urlencode +from urllib.parse import quote from django.core.exceptions import ObjectDoesNotExist from django.http import Http404, HttpResponse, HttpResponseRedirect +from django.shortcuts import render from django.utils.translation import gettext_lazy as _ from rdmo.core.plugins import Plugin -from rdmo.services.providers import OauthProviderMixin, GitHubProviderMixin, GitLabProviderMixin +from rdmo.services.providers import GitHubProviderMixin, GitLabProviderMixin, OauthProviderMixin class IssueProvider(Plugin): @@ -37,7 +38,7 @@ def send_issue(self, request, issue, integration, subject, message, attachments) return self.post(request, url, data) def post_success(self, request, response): - from rdmo.projects.models import Issue, Integration, IssueResource + from rdmo.projects.models import Integration, Issue, IssueResource # get the upstream url of the issue remote_url = self.get_issue_url(response) @@ -92,7 +93,7 @@ def get_secret(self, integration): def get_post_url(self, request, issue, integration, subject, message, attachments): repo = self.get_repo(integration) if repo: - return 'https://api.github.com/repos/{}/issues'.format(repo) + return f'https://api.github.com/repos/{repo}/issues' def get_post_data(self, request, issue, integration, subject, message, attachments): return { @@ -159,8 +160,8 @@ class GitLabIssueProvider(GitLabProviderMixin, OauthIssueProvider): @property def description(self): - return _('This integration allow the creation of issues in arbitrary repositories on {}. ' - 'The upload of attachments is not supported by GitLab.'.format(self.gitlab_url)) + return _(f'This integration allow the creation of issues in arbitrary repositories on {self.gitlab_url}. ' + 'The upload of attachments is not supported by GitLab.') def get_repo(self, integration): try: diff --git a/rdmo/projects/rules.py b/rdmo/projects/rules.py index 898867aecf..93a2fec87e 100644 --- a/rdmo/projects/rules.py +++ b/rdmo/projects/rules.py @@ -1,7 +1,8 @@ +from django.contrib.sites.shortcuts import get_current_site + import rules from rules.predicates import is_superuser -from django.contrib.sites.shortcuts import get_current_site @rules.predicate def is_project_member(user, project): @@ -78,7 +79,7 @@ def is_site_manager_for_current_site(user, request): rules.add_perm('projects.view_issue_object', is_project_member | is_site_manager) rules.add_perm('projects.add_issue_object', is_project_manager | is_project_owner | is_site_manager) -rules.add_perm('projects.change_issue_object', is_project_author | is_project_manager | is_project_owner | is_site_manager) +rules.add_perm('projects.change_issue_object', is_project_author | is_project_manager | is_project_owner | is_site_manager) # noqa: E501 rules.add_perm('projects.delete_issue_object', is_project_manager | is_project_owner | is_site_manager) rules.add_perm('projects.view_snapshot_object', is_project_member | is_site_manager) @@ -88,8 +89,8 @@ def is_site_manager_for_current_site(user, request): rules.add_perm('projects.view_value_object', is_project_member | is_site_manager) rules.add_perm('projects.add_value_object', is_project_author | is_project_manager | is_project_owner | is_site_manager) -rules.add_perm('projects.change_value_object', is_project_author | is_project_manager | is_project_owner | is_site_manager) -rules.add_perm('projects.delete_value_object', is_project_author | is_project_manager | is_project_owner | is_site_manager) +rules.add_perm('projects.change_value_object', is_project_author | is_project_manager | is_project_owner | is_site_manager) # noqa: E501 +rules.add_perm('projects.delete_value_object', is_project_author | is_project_manager | is_project_owner | is_site_manager) # noqa: E501 rules.add_perm('projects.view_page_object', is_project_member | is_site_manager) diff --git a/rdmo/projects/serializers/v1/__init__.py b/rdmo/projects/serializers/v1/__init__.py index d0f4fc7c3a..7e65e7cb9f 100644 --- a/rdmo/projects/serializers/v1/__init__.py +++ b/rdmo/projects/serializers/v1/__init__.py @@ -1,13 +1,13 @@ from django.conf import settings from django.contrib.auth import get_user_model from django.utils.translation import gettext_lazy as _ + from rest_framework import serializers from rdmo.questions.models import Catalog from rdmo.services.validators import ProviderValidator -from ...models import (Integration, IntegrationOption, Invite, Issue, - IssueResource, Membership, Project, Snapshot, Value) +from ...models import Integration, IntegrationOption, Invite, Issue, IssueResource, Membership, Project, Snapshot, Value from ...validators import ValueValidator diff --git a/rdmo/projects/serializers/v1/page.py b/rdmo/projects/serializers/v1/page.py index dd72906cad..838cfe4591 100644 --- a/rdmo/projects/serializers/v1/page.py +++ b/rdmo/projects/serializers/v1/page.py @@ -1,6 +1,7 @@ from django.utils.translation import gettext_lazy as _ from rest_framework import serializers + from rdmo.conditions.models import Condition from rdmo.core.serializers import MarkdownSerializerMixin from rdmo.options.models import Option, OptionSet diff --git a/rdmo/projects/static/projects/css/project.scss b/rdmo/projects/static/projects/css/project.scss index 1feccf80a7..1fda0c6b55 100644 --- a/rdmo/projects/static/projects/css/project.scss +++ b/rdmo/projects/static/projects/css/project.scss @@ -21,4 +21,4 @@ .fa-sign-out { width: 11px; } -} \ No newline at end of file +} diff --git a/rdmo/projects/tests/test_commands.py b/rdmo/projects/tests/test_commands.py index f698364f91..cdb0cd3450 100644 --- a/rdmo/projects/tests/test_commands.py +++ b/rdmo/projects/tests/test_commands.py @@ -1,6 +1,7 @@ import io import pytest + from django.core.management import call_command from django.core.management.base import CommandError @@ -18,7 +19,7 @@ def get_prune_output(projects, remove=False): project_output = "" for proj in projects: - project_output += '%s (id=%s)\n' % (proj.title, proj.id) + project_output += f'{proj.title} (id={proj.id})\n' if remove: project_output += "...removing...OK\n" return project_output @@ -36,12 +37,12 @@ def test_prune_projects_error(db, settings): @pytest.mark.parametrize('min_role,role_list,projects', prune_assignments) def test_prune_projects_output(db, settings, min_role, role_list, projects): stdout, stderr = io.StringIO(), io.StringIO() - + instances = Project.objects.filter(id__in=projects).all() call_command('prune_projects', '--min_role', min_role, stdout=stdout, stderr=stderr) assert stdout.getvalue() == \ - "Found projects without %s:\n%s" % (role_list, get_prune_output(instances)) + f"Found projects without {role_list}:\n{get_prune_output(instances)}" assert not stderr.getvalue() diff --git a/rdmo/projects/tests/test_handlers.py b/rdmo/projects/tests/test_handlers.py index 33ea718603..83a9c3c3c8 100644 --- a/rdmo/projects/tests/test_handlers.py +++ b/rdmo/projects/tests/test_handlers.py @@ -1,4 +1,5 @@ import itertools + import pytest from django.contrib.auth.models import Group @@ -49,9 +50,9 @@ def test_update_projects(db, view_id, sites, catalogs, groups, project_id, proje view.catalogs.set(Catalog.objects.filter(pk__in=catalogs)) view.groups.set(Group.objects.filter(pk__in=groups)) - assert sorted(list(itertools.chain.from_iterable(view.sites.all().values_list('pk')))) == sites - assert sorted(list(itertools.chain.from_iterable(view.catalogs.all().values_list('pk')))) == catalogs - assert sorted(list(itertools.chain.from_iterable(view.groups.all().values_list('pk')))) == groups + assert sorted(itertools.chain.from_iterable(view.sites.all().values_list('pk'))) == sites + assert sorted(itertools.chain.from_iterable(view.catalogs.all().values_list('pk'))) == catalogs + assert sorted(itertools.chain.from_iterable(view.groups.all().values_list('pk'))) == groups if not project_exists: with pytest.raises(Project.DoesNotExist): diff --git a/rdmo/projects/tests/test_utils.py b/rdmo/projects/tests/test_utils.py index 86f3d99dce..edbc5a8d3b 100644 --- a/rdmo/projects/tests/test_utils.py +++ b/rdmo/projects/tests/test_utils.py @@ -2,8 +2,8 @@ from django.http import QueryDict -from ..utils import set_context_querystring_with_filter_and_page from ..filters import ProjectFilter +from ..utils import set_context_querystring_with_filter_and_page GET_queries = [ 'page=2&title=project', diff --git a/rdmo/projects/tests/test_view_integration.py b/rdmo/projects/tests/test_view_integration.py index d5df46746c..fe626862c0 100644 --- a/rdmo/projects/tests/test_view_integration.py +++ b/rdmo/projects/tests/test_view_integration.py @@ -2,6 +2,7 @@ import json import pytest + from django.urls import reverse from ..models import Integration, Issue @@ -122,7 +123,8 @@ def test_integration_update_post(db, client, username, password, project_id, int if integration: if project_id in change_integration_permission_map.get(username, []): assert response.status_code == 302 - values = Integration.objects.filter(project_id=project_id, id=integration_id).first().options.values('key', 'value', 'secret') + values = Integration.objects.filter(project_id=project_id, id=integration_id).first() \ + .options.values('key', 'value', 'secret') assert sorted(values, key=lambda obj: obj['key']) == [ { 'key': 'repo', diff --git a/rdmo/projects/tests/test_view_issue.py b/rdmo/projects/tests/test_view_issue.py index ead06d1473..5404ca277e 100644 --- a/rdmo/projects/tests/test_view_issue.py +++ b/rdmo/projects/tests/test_view_issue.py @@ -1,4 +1,5 @@ import pytest + from django.core import mail from django.urls import reverse diff --git a/rdmo/projects/tests/test_view_membership.py b/rdmo/projects/tests/test_view_membership.py index e9e38a0175..4eef8c5c98 100644 --- a/rdmo/projects/tests/test_view_membership.py +++ b/rdmo/projects/tests/test_view_membership.py @@ -97,10 +97,12 @@ def test_membership_create_post_mail(db, client, settings, if project_id in add_membership_permission_map.get(username, []): if not PROJECT_SEND_INVITE: assert response.status_code == 200 - assert not Invite.objects.filter(project_id=project_id, user=None, email='someuser@example.com', role=membership_role) + assert not Invite.objects.filter(project_id=project_id, user=None, + email='someuser@example.com', role=membership_role) else: assert response.status_code == 302 - assert Invite.objects.get(project_id=project_id, user=None, email='someuser@example.com', role=membership_role) + assert Invite.objects.get(project_id=project_id, user=None, + email='someuser@example.com', role=membership_role) assert len(mail.outbox) == int(PROJECT_SEND_INVITE) else: if password: @@ -184,8 +186,10 @@ def test_membership_create_post_silent(db, client, username, password, membershi assert Membership.objects.get(project_id=project_id, user__username='user', role=membership_role) assert len(mail.outbox) == 0 else: - assert Invite.objects.get(project_id=project_id, user__username='user', email='user@example.com', role=membership_role) - assert not Membership.objects.filter(project_id=project_id, user__username='user', role=membership_role).exists() + assert Invite.objects.get(project_id=project_id, user__username='user', + email='user@example.com', role=membership_role) + assert not Membership.objects.filter(project_id=project_id, user__username='user', + role=membership_role).exists() assert len(mail.outbox) == 1 else: diff --git a/rdmo/projects/tests/test_view_membership_multisite.py b/rdmo/projects/tests/test_view_membership_multisite.py index 7107a9344b..6bf58c3af4 100644 --- a/rdmo/projects/tests/test_view_membership_multisite.py +++ b/rdmo/projects/tests/test_view_membership_multisite.py @@ -7,6 +7,7 @@ from ..models import Invite, Project from ..utils import get_invite_email_project_path + users = ( ('owner', 'owner'), ('manager', 'manager'), @@ -30,22 +31,26 @@ sites_domains = ('example.com', 'foo.com', 'bar.com') + @pytest.fixture() -def multisite_setting(settings): +def multisite(settings): settings.MULTISITE = True + @pytest.mark.parametrize('username,password', users) @pytest.mark.parametrize('project_id', projects) @pytest.mark.parametrize('membership_role', membership_roles) @pytest.mark.parametrize('site_domain', sites_domains) -def test_get_invite_email_project_path_function(db, client, username, password, project_id, membership_role, site_domain, multisite_setting): +def test_get_invite_email_project_path_function(db, client, username, password, project_id, + membership_role, site_domain, multisite): client.login(username=username, password=password) current_site = Site.objects.get_current() foo_site, _created = Site.objects.get_or_create(domain=site_domain, name=site_domain) foo_username = f'{site_domain}-test-user' foo_email = f'{foo_username}@{site_domain}' - foo_user, _created = get_user_model().objects.get_or_create(username=foo_username, email=foo_email, password=foo_username) + foo_user, _created = get_user_model().objects.get_or_create(username=foo_username, email=foo_email, + password=foo_username) foo_user.role.member.set([foo_site]) project = Project.objects.get(pk=project_id) @@ -59,21 +64,24 @@ def test_get_invite_email_project_path_function(db, client, username, password, else: assert invite_email_project_path.startswith('http://' + site_domain + '/projects') + @pytest.mark.parametrize('username,password', users) @pytest.mark.parametrize('project_id', projects) @pytest.mark.parametrize('membership_role', membership_roles) @pytest.mark.parametrize('site_domain', sites_domains) -def test_invite_email_project_path_email_body(db, client, username, password, project_id, membership_role, site_domain, multisite_setting): +def test_invite_email_project_path_email_body(db, client, username, password, project_id, + membership_role, site_domain, multisite): client.login(username=username, password=password) current_site = Site.objects.get_current() foo_site, _created = Site.objects.get_or_create(domain=site_domain, name=site_domain) foo_username = f'{site_domain}-user' foo_email = f'{foo_username}@{site_domain}' - foo_user, _created = get_user_model().objects.get_or_create(username=f'{site_domain}-user', email=foo_email, password=foo_username) + foo_user, _created = get_user_model().objects.get_or_create(username=f'{site_domain}-user', email=foo_email, + password=foo_username) foo_user.role.member.set([foo_site]) project = Project.objects.get(pk=project_id) - + url = reverse('membership_create', args=[project_id]) data = { 'username_or_email': foo_email, diff --git a/rdmo/projects/tests/test_view_project.py b/rdmo/projects/tests/test_view_project.py index ee04828a70..7d63410f9c 100644 --- a/rdmo/projects/tests/test_view_project.py +++ b/rdmo/projects/tests/test_view_project.py @@ -1,9 +1,11 @@ import re import pytest -from pytest_django.asserts import assertContains, assertNotContains, assertTemplateUsed + from django.urls import reverse +from pytest_django.asserts import assertContains, assertNotContains, assertTemplateUsed + from rdmo.questions.models import Catalog from rdmo.views.models import View @@ -80,7 +82,7 @@ def test_list(db, client, username, password): assertContains(response, 'View all projects on') else: user_projects_map = view_project_permission_map.get(username, []) - assert sorted(list(set(map(int, projects)))) == user_projects_map + assert sorted(set(map(int, projects))) == user_projects_map assert response.context['number_of_projects'] == len(user_projects_map) assertNotContains(response, 'View all projects on') else: @@ -101,7 +103,7 @@ def test_site(db, client, username, password): assert response.status_code == 200 assertTemplateUsed(response, 'projects/site_projects.html') user_projects_map = view_project_permission_map.get(username, []) - assert sorted(list(set(map(int, projects)))) == user_projects_map + assert sorted(set(map(int, projects))) == user_projects_map assert response.context['number_of_projects'] == len(user_projects_map) else: assert response.status_code == 403 diff --git a/rdmo/projects/tests/test_view_project_create_import.py b/rdmo/projects/tests/test_view_project_create_import.py index 068832068b..a1c3880719 100644 --- a/rdmo/projects/tests/test_view_project_create_import.py +++ b/rdmo/projects/tests/test_view_project_create_import.py @@ -3,6 +3,7 @@ from pathlib import Path import pytest + from django.urls import reverse from rdmo.core.constants import VALUE_TYPE_FILE @@ -160,7 +161,7 @@ def test_project_create_import_post_import_file(db, settings, client, files, use if password: project = Project.objects.order_by('updated').last() assert response.status_code == 302 - assert response.url == '/projects/{}/'.format(project.pk) + assert response.url == f'/projects/{project.pk}/' # a new project, new values values assert Project.objects.count() == projects_count + 1 @@ -265,7 +266,7 @@ def test_project_create_import_post_import_empty(db, settings, client, username, if password: new_project = Project.objects.order_by('updated').last() assert response.status_code == 302 - assert response.url == '/projects/{}/'.format(new_project.id) + assert response.url == f'/projects/{new_project.id}/' # a new project, but no values assert Project.objects.count() == projects_count + 1 diff --git a/rdmo/projects/tests/test_view_project_join.py b/rdmo/projects/tests/test_view_project_join.py index 1c4e03cd74..ecde517d49 100644 --- a/rdmo/projects/tests/test_view_project_join.py +++ b/rdmo/projects/tests/test_view_project_join.py @@ -1,4 +1,5 @@ import pytest + from django.contrib.auth import get_user_model from django.urls import reverse diff --git a/rdmo/projects/tests/test_view_project_leave.py b/rdmo/projects/tests/test_view_project_leave.py index db134daca5..fa0fd55bbd 100644 --- a/rdmo/projects/tests/test_view_project_leave.py +++ b/rdmo/projects/tests/test_view_project_leave.py @@ -1,4 +1,5 @@ import pytest + from django.urls import reverse from ..models import Membership diff --git a/rdmo/projects/tests/test_view_project_update_import.py b/rdmo/projects/tests/test_view_project_update_import.py index e1358463a2..d2da3816b3 100644 --- a/rdmo/projects/tests/test_view_project_update_import.py +++ b/rdmo/projects/tests/test_view_project_update_import.py @@ -3,6 +3,7 @@ from pathlib import Path import pytest + from django.urls import reverse from rdmo.core.constants import VALUE_TYPE_FILE @@ -206,7 +207,7 @@ def test_project_update_import_post_import_file(db, settings, client, files, use if project_id in change_project_permission_map.get(username, []): assert response.status_code == 302 - assert response.url == '/projects/{}/'.format(project_id) + assert response.url == f'/projects/{project_id}/' else: if password: @@ -274,7 +275,7 @@ def test_project_update_import_post_import_file_cancel(db, settings, client, fil assert project.updated == project_updated assert response.status_code == 302 - assert response.url == '/projects/{}/'.format(project_id) + assert response.url == f'/projects/{project_id}/' elif password: assert response.status_code == 403 else: @@ -330,7 +331,7 @@ def test_project_update_import_post_import_file_empty(db, settings, client, user assert project.updated == project_updated assert response.status_code == 302 - assert response.url == '/projects/{}/'.format(project_id) + assert response.url == f'/projects/{project_id}/' elif password: assert response.status_code == 403 else: @@ -341,7 +342,8 @@ def test_project_update_import_post_import_file_empty(db, settings, client, user @pytest.mark.parametrize('username,password', users) @pytest.mark.parametrize('project_id', projects) @pytest.mark.parametrize('source_id', projects) -def test_project_update_import_post_import_project_step1(db, settings, client, username, password, project_id, source_id): +def test_project_update_import_post_import_project_step1(db, settings, client, username, password, + project_id, source_id): client.login(username=username, password=password) url = reverse('project_update_import', args=[project_id]) @@ -366,7 +368,8 @@ def test_project_update_import_post_import_project_step1(db, settings, client, u @pytest.mark.parametrize('username,password', users) @pytest.mark.parametrize('project_id', projects) @pytest.mark.parametrize('source_id', projects) -def test_project_update_import_post_import_project_step2(db, settings, client, username, password, project_id, source_id): +def test_project_update_import_post_import_project_step2(db, settings, client, username, password, + project_id, source_id): client.login(username=username, password=password) projects_count = Project.objects.count() @@ -430,7 +433,7 @@ def test_project_update_import_post_import_project_step2(db, settings, client, u if project_id in change_project_permission_map.get(username, []): assert response.status_code == 302 - assert response.url == '/projects/{}/'.format(project_id) + assert response.url == f'/projects/{project_id}/' else: if password: diff --git a/rdmo/projects/tests/test_view_snapshot.py b/rdmo/projects/tests/test_view_snapshot.py index 2b8a21c00d..a49b161d0f 100644 --- a/rdmo/projects/tests/test_view_snapshot.py +++ b/rdmo/projects/tests/test_view_snapshot.py @@ -1,6 +1,7 @@ from pathlib import Path import pytest + from django.conf import settings from django.urls import reverse diff --git a/rdmo/projects/tests/test_viewset_integration.py b/rdmo/projects/tests/test_viewset_integration.py index 481d5e91d7..7034498134 100644 --- a/rdmo/projects/tests/test_viewset_integration.py +++ b/rdmo/projects/tests/test_viewset_integration.py @@ -1,4 +1,5 @@ import pytest + from django.urls import reverse from ..models import Integration diff --git a/rdmo/projects/tests/test_viewset_invite.py b/rdmo/projects/tests/test_viewset_invite.py index ffa24601f8..e95b2c6533 100644 --- a/rdmo/projects/tests/test_viewset_invite.py +++ b/rdmo/projects/tests/test_viewset_invite.py @@ -1,4 +1,5 @@ import pytest + from django.urls import reverse from ..models import Invite diff --git a/rdmo/projects/tests/test_viewset_issue.py b/rdmo/projects/tests/test_viewset_issue.py index 6966c46cac..96f354478f 100644 --- a/rdmo/projects/tests/test_viewset_issue.py +++ b/rdmo/projects/tests/test_viewset_issue.py @@ -1,4 +1,5 @@ import pytest + from django.urls import reverse from ..models import Issue diff --git a/rdmo/projects/tests/test_viewset_membership.py b/rdmo/projects/tests/test_viewset_membership.py index 47519ca9f7..6bad007774 100644 --- a/rdmo/projects/tests/test_viewset_membership.py +++ b/rdmo/projects/tests/test_viewset_membership.py @@ -1,4 +1,5 @@ import pytest + from django.urls import reverse from ..models import Membership diff --git a/rdmo/projects/tests/test_viewset_project.py b/rdmo/projects/tests/test_viewset_project.py index 88b940eb2a..c5d08d0c40 100644 --- a/rdmo/projects/tests/test_viewset_project.py +++ b/rdmo/projects/tests/test_viewset_project.py @@ -1,4 +1,5 @@ import pytest + from django.urls import reverse from ..models import Project @@ -243,7 +244,7 @@ def test_overview(db, client, username, password, project_id, condition_id): def test_resolve(db, client, username, password, project_id, condition_id): client.login(username=username, password=password) - url = reverse(urlnames['resolve'], args=[project_id]) + '?condition={}'.format(condition_id) + url = reverse(urlnames['resolve'], args=[project_id]) + f'?condition={condition_id}' response = client.get(url) if project_id in view_project_permission_map.get(username, []): diff --git a/rdmo/projects/tests/test_viewset_project_integration.py b/rdmo/projects/tests/test_viewset_project_integration.py index 1702af757a..94129a3d06 100644 --- a/rdmo/projects/tests/test_viewset_project_integration.py +++ b/rdmo/projects/tests/test_viewset_project_integration.py @@ -1,6 +1,7 @@ import json import pytest + from django.urls import reverse from ..models import Integration diff --git a/rdmo/projects/tests/test_viewset_project_invite.py b/rdmo/projects/tests/test_viewset_project_invite.py index f0d11b958f..a99fc9fd97 100644 --- a/rdmo/projects/tests/test_viewset_project_invite.py +++ b/rdmo/projects/tests/test_viewset_project_invite.py @@ -1,4 +1,5 @@ import pytest + from django.contrib.auth import get_user_model from django.urls import reverse diff --git a/rdmo/projects/tests/test_viewset_project_issue.py b/rdmo/projects/tests/test_viewset_project_issue.py index 2cbefa4d26..16bd036b41 100644 --- a/rdmo/projects/tests/test_viewset_project_issue.py +++ b/rdmo/projects/tests/test_viewset_project_issue.py @@ -1,4 +1,5 @@ import pytest + from django.urls import reverse from ..models import Issue diff --git a/rdmo/projects/tests/test_viewset_project_membership.py b/rdmo/projects/tests/test_viewset_project_membership.py index 6bbb1ccb49..d935bff060 100644 --- a/rdmo/projects/tests/test_viewset_project_membership.py +++ b/rdmo/projects/tests/test_viewset_project_membership.py @@ -1,4 +1,5 @@ import pytest + from django.contrib.auth import get_user_model from django.urls import reverse diff --git a/rdmo/projects/tests/test_viewset_project_page.py b/rdmo/projects/tests/test_viewset_project_page.py index 15265c0444..e64eb4bce3 100644 --- a/rdmo/projects/tests/test_viewset_project_page.py +++ b/rdmo/projects/tests/test_viewset_project_page.py @@ -1,4 +1,5 @@ import pytest + from django.urls import reverse users = ( diff --git a/rdmo/projects/tests/test_viewset_project_snapshot.py b/rdmo/projects/tests/test_viewset_project_snapshot.py index 770faf5e22..2c3c7c7337 100644 --- a/rdmo/projects/tests/test_viewset_project_snapshot.py +++ b/rdmo/projects/tests/test_viewset_project_snapshot.py @@ -1,6 +1,7 @@ from pathlib import Path import pytest + from django.conf import settings from django.urls import reverse diff --git a/rdmo/projects/tests/test_viewset_project_value.py b/rdmo/projects/tests/test_viewset_project_value.py index 4f1885b860..ecccedb347 100644 --- a/rdmo/projects/tests/test_viewset_project_value.py +++ b/rdmo/projects/tests/test_viewset_project_value.py @@ -1,12 +1,11 @@ from pathlib import Path import pytest + from django.conf import settings from django.urls import reverse -from rdmo.core.constants import (VALUE_TYPE_CHOICES, VALUE_TYPE_FILE, - VALUE_TYPE_TEXT) -from rdmo.questions.models import Question +from rdmo.core.constants import VALUE_TYPE_CHOICES, VALUE_TYPE_FILE, VALUE_TYPE_TEXT from ..models import Value @@ -269,7 +268,7 @@ def test_file_get(db, client, files, username, password, project_id, value_id): if value and value.value_type == VALUE_TYPE_FILE and project_id in view_value_permission_map.get(username, []): assert response.status_code == 200 assert response['Content-Type'] == value.file_type - assert response['Content-Disposition'] == 'attachment; filename={}'.format(value.file_name) + assert response['Content-Disposition'] == f'attachment; filename={value.file_name}' assert response.content == value.file.read() else: assert response.status_code == 404 diff --git a/rdmo/projects/tests/test_viewset_snapshot.py b/rdmo/projects/tests/test_viewset_snapshot.py index 57653a7f16..40fedc1bda 100644 --- a/rdmo/projects/tests/test_viewset_snapshot.py +++ b/rdmo/projects/tests/test_viewset_snapshot.py @@ -1,4 +1,5 @@ import pytest + from django.urls import reverse from ..models import Snapshot diff --git a/rdmo/projects/tests/test_viewset_value.py b/rdmo/projects/tests/test_viewset_value.py index 62c32f4957..a2b3dfbf80 100644 --- a/rdmo/projects/tests/test_viewset_value.py +++ b/rdmo/projects/tests/test_viewset_value.py @@ -1,4 +1,5 @@ import pytest + from django.urls import reverse from rdmo.core.constants import VALUE_TYPE_FILE @@ -62,7 +63,7 @@ def test_list(db, client, username, password): def test_list_snapshot(db, client, username, password, snapshot_id): client.login(username=username, password=password) - url = reverse(urlnames['list']) + '?snapshot={}'.format(snapshot_id) + url = reverse(urlnames['list']) + f'?snapshot={snapshot_id}' response = client.get(url) if password: @@ -153,7 +154,7 @@ def test_file(db, client, files, username, password, value_id): if value.value_type == VALUE_TYPE_FILE and value.project.id in view_value_permission_map.get(username, []): assert response.status_code == 200 assert response['Content-Type'] == value.file_type - assert response['Content-Disposition'] == 'attachment; filename={}'.format(value.file_name) + assert response['Content-Disposition'] == f'attachment; filename={value.file_name}' assert response.content == value.file.read() elif password: assert response.status_code == 404 diff --git a/rdmo/projects/urls/__init__.py b/rdmo/projects/urls/__init__.py index f2bad7fc27..b71fb82221 100644 --- a/rdmo/projects/urls/__init__.py +++ b/rdmo/projects/urls/__init__.py @@ -1,75 +1,141 @@ from django.urls import re_path -from ..views import (IntegrationCreateView, IntegrationDeleteView, - IntegrationUpdateView, IntegrationWebhookView, - InviteDeleteView, IssueDetailView, IssueSendView, - IssueUpdateView, MembershipCreateView, - MembershipDeleteView, MembershipUpdateView, - ProjectAnswersExportView, ProjectAnswersView, - ProjectCancelView, ProjectCreateImportView, - ProjectCreateView, ProjectDeleteView, ProjectDetailView, - ProjectErrorView, ProjectExportView, ProjectJoinView, - ProjectLeaveView, ProjectQuestionsView, ProjectsView, - ProjectUpdateCatalogView, ProjectUpdateImportView, - ProjectUpdateInformationView, ProjectUpdateParentView, - ProjectUpdateTasksView, ProjectUpdateView, - ProjectUpdateViewsView, ProjectViewExportView, - ProjectViewView, SiteProjectsView, SnapshotCreateView, - SnapshotRollbackView, SnapshotUpdateView) +from ..views import ( + IntegrationCreateView, + IntegrationDeleteView, + IntegrationUpdateView, + IntegrationWebhookView, + InviteDeleteView, + IssueDetailView, + IssueSendView, + IssueUpdateView, + MembershipCreateView, + MembershipDeleteView, + MembershipUpdateView, + ProjectAnswersExportView, + ProjectAnswersView, + ProjectCancelView, + ProjectCreateImportView, + ProjectCreateView, + ProjectDeleteView, + ProjectDetailView, + ProjectErrorView, + ProjectExportView, + ProjectJoinView, + ProjectLeaveView, + ProjectQuestionsView, + ProjectsView, + ProjectUpdateCatalogView, + ProjectUpdateImportView, + ProjectUpdateInformationView, + ProjectUpdateParentView, + ProjectUpdateTasksView, + ProjectUpdateView, + ProjectUpdateViewsView, + ProjectViewExportView, + ProjectViewView, + SiteProjectsView, + SnapshotCreateView, + SnapshotRollbackView, + SnapshotUpdateView, +) urlpatterns = [ - re_path(r'^$', ProjectsView.as_view(), name='projects'), - re_path(r'^all/$', SiteProjectsView.as_view(), name='site_projects'), + re_path(r'^$', + ProjectsView.as_view(), name='projects'), + re_path(r'^all/$', + SiteProjectsView.as_view(), name='site_projects'), - re_path(r'^create/$', ProjectCreateView.as_view(), name='project_create'), - re_path(r'^join/(?P.+)/$', ProjectJoinView.as_view(), name='project_join'), - re_path(r'^cancel/(?P.+)/$', ProjectCancelView.as_view(), name='project_cancel'), - re_path(r'^import/$', ProjectCreateImportView.as_view(), name='project_create_import'), - re_path(r'^import/(?P[a-z-]+)/$', ProjectCreateImportView.as_view(), name='project_create_import'), + re_path(r'^create/$', + ProjectCreateView.as_view(), name='project_create'), + re_path(r'^join/(?P.+)/$', + ProjectJoinView.as_view(), name='project_join'), + re_path(r'^cancel/(?P.+)/$', + ProjectCancelView.as_view(), name='project_cancel'), + re_path(r'^import/$', + ProjectCreateImportView.as_view(), name='project_create_import'), + re_path(r'^import/(?P[a-z-]+)/$', + ProjectCreateImportView.as_view(), name='project_create_import'), - re_path(r'^(?P[0-9]+)/$', ProjectDetailView.as_view(), name='project'), - re_path(r'^(?P[0-9]+)/update/$', ProjectUpdateView.as_view(), name='project_update'), - re_path(r'^(?P[0-9]+)/update/information/$', ProjectUpdateInformationView.as_view(), name='project_update_information'), - re_path(r'^(?P[0-9]+)/update/catalog/$', ProjectUpdateCatalogView.as_view(), name='project_update_catalog'), - re_path(r'^(?P[0-9]+)/update/parent/$', ProjectUpdateParentView.as_view(), name='project_update_parent'), - re_path(r'^(?P[0-9]+)/update/tasks/$', ProjectUpdateTasksView.as_view(), name='project_update_tasks'), - re_path(r'^(?P[0-9]+)/update/views/$', ProjectUpdateViewsView.as_view(), name='project_update_views'), - re_path(r'^(?P[0-9]+)/delete/$', ProjectDeleteView.as_view(), name='project_delete'), - re_path(r'^(?P[0-9]+)/leave/$', ProjectLeaveView.as_view(), name='project_leave'), - re_path(r'^(?P[0-9]+)/export/(?P[a-z-]+)/$', ProjectExportView.as_view(), name='project_export'), - re_path(r'^(?P[0-9]+)/import/$', ProjectUpdateImportView.as_view(), name='project_update_import'), - re_path(r'^(?P[0-9]+)/import/(?P[a-z-]+)/$', ProjectUpdateImportView.as_view(), name='project_update_import'), + re_path(r'^(?P[0-9]+)/$', + ProjectDetailView.as_view(), name='project'), + re_path(r'^(?P[0-9]+)/update/$', + ProjectUpdateView.as_view(), name='project_update'), + re_path(r'^(?P[0-9]+)/update/information/$', + ProjectUpdateInformationView.as_view(), name='project_update_information'), + re_path(r'^(?P[0-9]+)/update/catalog/$', + ProjectUpdateCatalogView.as_view(), name='project_update_catalog'), + re_path(r'^(?P[0-9]+)/update/parent/$', + ProjectUpdateParentView.as_view(), name='project_update_parent'), + re_path(r'^(?P[0-9]+)/update/tasks/$', + ProjectUpdateTasksView.as_view(), name='project_update_tasks'), + re_path(r'^(?P[0-9]+)/update/views/$', + ProjectUpdateViewsView.as_view(), name='project_update_views'), + re_path(r'^(?P[0-9]+)/delete/$', + ProjectDeleteView.as_view(), name='project_delete'), + re_path(r'^(?P[0-9]+)/leave/$', + ProjectLeaveView.as_view(), name='project_leave'), + re_path(r'^(?P[0-9]+)/export/(?P[a-z-]+)/$', + ProjectExportView.as_view(), name='project_export'), + re_path(r'^(?P[0-9]+)/import/$', + ProjectUpdateImportView.as_view(), name='project_update_import'), + re_path(r'^(?P[0-9]+)/import/(?P[a-z-]+)/$', + ProjectUpdateImportView.as_view(), name='project_update_import'), - re_path(r'^(?P[0-9]+)/memberships/create/$', MembershipCreateView.as_view(), name='membership_create'), - re_path(r'^(?P[0-9]+)/memberships/(?P[0-9]+)/update/$', MembershipUpdateView.as_view(), name='membership_update'), - re_path(r'^(?P[0-9]+)/memberships/(?P[0-9]+)/delete/$', MembershipDeleteView.as_view(), name='membership_delete'), - re_path(r'^(?P[0-9]+)/invite/(?P[0-9]+)/delete/$', InviteDeleteView.as_view(), name='invite_delete'), + re_path(r'^(?P[0-9]+)/memberships/create/$', + MembershipCreateView.as_view(), name='membership_create'), + re_path(r'^(?P[0-9]+)/memberships/(?P[0-9]+)/update/$', + MembershipUpdateView.as_view(), name='membership_update'), + re_path(r'^(?P[0-9]+)/memberships/(?P[0-9]+)/delete/$', + MembershipDeleteView.as_view(), name='membership_delete'), + re_path(r'^(?P[0-9]+)/invite/(?P[0-9]+)/delete/$', + InviteDeleteView.as_view(), name='invite_delete'), - re_path(r'^(?P[0-9]+)/integrations/create/(?P[a-z]+)/$', IntegrationCreateView.as_view(), name='integration_create'), - re_path(r'^(?P[0-9]+)/integrations/(?P[0-9]+)/update/$', IntegrationUpdateView.as_view(), name='integration_update'), - re_path(r'^(?P[0-9]+)/integrations/(?P[0-9]+)/delete/$', IntegrationDeleteView.as_view(), name='integration_delete'), - re_path(r'^(?P[0-9]+)/integrations/(?P[0-9]+)/webhook/$', IntegrationWebhookView.as_view(), name='integration_webhook'), + re_path(r'^(?P[0-9]+)/integrations/create/(?P[a-z]+)/$', + IntegrationCreateView.as_view(), name='integration_create'), + re_path(r'^(?P[0-9]+)/integrations/(?P[0-9]+)/update/$', + IntegrationUpdateView.as_view(), name='integration_update'), + re_path(r'^(?P[0-9]+)/integrations/(?P[0-9]+)/delete/$', + IntegrationDeleteView.as_view(), name='integration_delete'), + re_path(r'^(?P[0-9]+)/integrations/(?P[0-9]+)/webhook/$', + IntegrationWebhookView.as_view(), name='integration_webhook'), - re_path(r'^(?P[0-9]+)/issues/(?P[0-9]+)/$', IssueDetailView.as_view(), name='issue'), - re_path(r'^(?P[0-9]+)/issues/(?P[0-9]+)/update/$', IssueUpdateView.as_view(), name='issue_update'), - re_path(r'^(?P[0-9]+)/issues/(?P[0-9]+)/send/$', IssueSendView.as_view(), name='issue_send'), + re_path(r'^(?P[0-9]+)/issues/(?P[0-9]+)/$', + IssueDetailView.as_view(), name='issue'), + re_path(r'^(?P[0-9]+)/issues/(?P[0-9]+)/update/$', + IssueUpdateView.as_view(), name='issue_update'), + re_path(r'^(?P[0-9]+)/issues/(?P[0-9]+)/send/$', + IssueSendView.as_view(), name='issue_send'), - re_path(r'^(?P[0-9]+)/snapshots/create/$', SnapshotCreateView.as_view(), name='snapshot_create'), - re_path(r'^(?P[0-9]+)/snapshots/(?P[0-9]+)/update/$', SnapshotUpdateView.as_view(), name='snapshot_update'), - re_path(r'^(?P[0-9]+)/snapshots/(?P[0-9]+)/rollback/$', SnapshotRollbackView.as_view(), name='snapshot_rollback'), + re_path(r'^(?P[0-9]+)/snapshots/create/$', + SnapshotCreateView.as_view(), name='snapshot_create'), + re_path(r'^(?P[0-9]+)/snapshots/(?P[0-9]+)/update/$', + SnapshotUpdateView.as_view(), name='snapshot_update'), + re_path(r'^(?P[0-9]+)/snapshots/(?P[0-9]+)/rollback/$', + SnapshotRollbackView.as_view(), name='snapshot_rollback'), - re_path(r'^(?P[0-9]+)/answers/$', ProjectAnswersView.as_view(), name='project_answers'), - re_path(r'^(?P[0-9]+)/answers/export/(?P[a-z]+)/$', ProjectAnswersExportView.as_view(), name='project_answers_export'), + re_path(r'^(?P[0-9]+)/answers/$', + ProjectAnswersView.as_view(), name='project_answers'), + re_path(r'^(?P[0-9]+)/answers/export/(?P[a-z]+)/$', + ProjectAnswersExportView.as_view(), name='project_answers_export'), - re_path(r'^(?P[0-9]+)/snapshots/(?P[0-9]+)/answers/$', ProjectAnswersView.as_view(), name='project_answers'), - re_path(r'^(?P[0-9]+)/snapshots/(?P[0-9]+)/answers/export/(?P[a-z]+)/$', ProjectAnswersExportView.as_view(), name='project_answers_export'), + re_path(r'^(?P[0-9]+)/snapshots/(?P[0-9]+)/answers/$', + ProjectAnswersView.as_view(), name='project_answers'), + re_path(r'^(?P[0-9]+)/snapshots/(?P[0-9]+)/answers/export/(?P[a-z]+)/$', + ProjectAnswersExportView.as_view(), name='project_answers_export'), - re_path(r'^(?P[0-9]+)/views/(?P[0-9]+)/$', ProjectViewView.as_view(), name='project_view'), - re_path(r'^(?P[0-9]+)/views/(?P[0-9]+)/export/(?P[a-z]+)/$', ProjectViewExportView.as_view(), name='project_view_export'), + re_path(r'^(?P[0-9]+)/views/(?P[0-9]+)/$', + ProjectViewView.as_view(), name='project_view'), + re_path(r'^(?P[0-9]+)/views/(?P[0-9]+)/export/(?P[a-z]+)/$', + ProjectViewExportView.as_view(), name='project_view_export'), - re_path(r'^(?P[0-9]+)/snapshots/(?P[0-9]+)/views/(?P[0-9]+)/$', ProjectViewView.as_view(), name='project_view'), - re_path(r'^(?P[0-9]+)/snapshots/(?P[0-9]+)/views/(?P[0-9]+)/export/(?P[a-z]+)/$', ProjectViewExportView.as_view(), name='project_view_export'), + re_path(r'^(?P[0-9]+)/snapshots/(?P[0-9]+)/views/(?P[0-9]+)/$', + ProjectViewView.as_view(), name='project_view'), + re_path(r'^(?P[0-9]+)/snapshots/(?P[0-9]+)/views/(?P[0-9]+)/export/(?P[a-z]+)/$', + ProjectViewExportView.as_view(), name='project_view_export'), - re_path(r'^(?P[0-9]+)/questions/', ProjectQuestionsView.as_view(), name='project_questions'), - re_path(r'^(?P[0-9]+)/error/', ProjectErrorView.as_view(), name='project_error'), + re_path(r'^(?P[0-9]+)/questions/', + ProjectQuestionsView.as_view(), name='project_questions'), + re_path(r'^(?P[0-9]+)/error/', + ProjectErrorView.as_view(), name='project_error'), ] diff --git a/rdmo/projects/urls/v1.py b/rdmo/projects/urls/v1.py index 2af97ae2dd..8f8f373a3d 100644 --- a/rdmo/projects/urls/v1.py +++ b/rdmo/projects/urls/v1.py @@ -1,12 +1,23 @@ from django.urls import include, path + from rest_framework_extensions.routers import ExtendedDefaultRouter -from ..viewsets import (IntegrationViewSet, InviteViewSet, IssueViewSet, - MembershipViewSet, ProjectIntegrationViewSet, - ProjectInviteViewSet, ProjectIssueViewSet, - ProjectMembershipViewSet, ProjectPageViewSet, - ProjectSnapshotViewSet, ProjectValueViewSet, - ProjectViewSet, SnapshotViewSet, ValueViewSet) +from ..viewsets import ( + IntegrationViewSet, + InviteViewSet, + IssueViewSet, + MembershipViewSet, + ProjectIntegrationViewSet, + ProjectInviteViewSet, + ProjectIssueViewSet, + ProjectMembershipViewSet, + ProjectPageViewSet, + ProjectSnapshotViewSet, + ProjectValueViewSet, + ProjectViewSet, + SnapshotViewSet, + ValueViewSet, +) app_name = 'v1-projects' diff --git a/rdmo/projects/utils.py b/rdmo/projects/utils.py index 931052d0db..94b57e0793 100644 --- a/rdmo/projects/utils.py +++ b/rdmo/projects/utils.py @@ -40,9 +40,7 @@ def check_conditions(conditions, values, set_prefix=None, set_index=None): def save_import_values(project, values, checked): for value in values: if value.attribute: - value_key = '{value.attribute.uri}[{value.set_prefix}][{value.set_index}][{value.collection_index}]'.format( - value=value - ) + value_key = f'{value.attribute.uri}[{value.set_prefix}][{value.set_index}][{value.collection_index}]' if value_key in checked: current_value = value.current @@ -103,7 +101,7 @@ def save_import_snapshot_values(project, snapshots, checked): for value in snapshot.snapshot_values: if value.attribute: - value_key = '{value.attribute.uri}[{snapshot_index}][{value.set_prefix}][{value.set_index}][{value.collection_index}]'.format( + value_key = '{value.attribute.uri}[{snapshot_index}][{value.set_prefix}][{value.set_index}][{value.collection_index}]'.format( # noqa: E501 value=value, snapshot_index=snapshot.snapshot_index ) diff --git a/rdmo/projects/validators.py b/rdmo/projects/validators.py index 1bd9cba4d4..3c60a66a32 100644 --- a/rdmo/projects/validators.py +++ b/rdmo/projects/validators.py @@ -1,12 +1,13 @@ from django.conf import settings from django.utils.translation import gettext_lazy as _ + from rest_framework import serializers from rdmo.core.constants import VALUE_TYPE_FILE from rdmo.core.utils import human2bytes -class ValueValidator(object): +class ValueValidator: requires_context = True @@ -14,10 +15,10 @@ def __call__(self, data, serializer): if data.get('value_type') == VALUE_TYPE_FILE: try: serializer.context['view'].get_object() - except AssertionError: + except AssertionError as e: project = serializer.context['view'].project if project.file_size > human2bytes(settings.PROJECT_FILE_QUOTA): raise serializers.ValidationError({ 'value': [_('You reached the file quota for this project.')] - }) + }) from e diff --git a/rdmo/projects/views/__init__.py b/rdmo/projects/views/__init__.py index b9fb680aca..063dc97280 100644 --- a/rdmo/projects/views/__init__.py +++ b/rdmo/projects/views/__init__.py @@ -1,19 +1,29 @@ -from .integration import (IntegrationCreateView, IntegrationDeleteView, - IntegrationUpdateView, IntegrationWebhookView) +from .integration import IntegrationCreateView, IntegrationDeleteView, IntegrationUpdateView, IntegrationWebhookView from .invite import InviteDeleteView from .issue import IssueDetailView, IssueSendView, IssueUpdateView -from .membership import (MembershipCreateView, MembershipDeleteView, - MembershipUpdateView) -from .project import (ProjectCancelView, ProjectDeleteView, ProjectDetailView, - ProjectErrorView, ProjectExportView, ProjectJoinView, - ProjectLeaveView, ProjectQuestionsView, ProjectsView, - SiteProjectsView) +from .membership import MembershipCreateView, MembershipDeleteView, MembershipUpdateView +from .project import ( + ProjectCancelView, + ProjectDeleteView, + ProjectDetailView, + ProjectErrorView, + ProjectExportView, + ProjectJoinView, + ProjectLeaveView, + ProjectQuestionsView, + ProjectsView, + SiteProjectsView, +) from .project_answers import ProjectAnswersExportView, ProjectAnswersView from .project_create import ProjectCreateImportView, ProjectCreateView -from .project_update import (ProjectUpdateCatalogView, ProjectUpdateImportView, - ProjectUpdateInformationView, - ProjectUpdateParentView, ProjectUpdateTasksView, - ProjectUpdateView, ProjectUpdateViewsView) +from .project_update import ( + ProjectUpdateCatalogView, + ProjectUpdateImportView, + ProjectUpdateInformationView, + ProjectUpdateParentView, + ProjectUpdateTasksView, + ProjectUpdateView, + ProjectUpdateViewsView, +) from .project_view import ProjectViewExportView, ProjectViewView -from .snapshot import (SnapshotCreateView, SnapshotRollbackView, - SnapshotUpdateView) +from .snapshot import SnapshotCreateView, SnapshotRollbackView, SnapshotUpdateView diff --git a/rdmo/projects/views/integration.py b/rdmo/projects/views/integration.py index 631d2264c9..a0191c0b85 100644 --- a/rdmo/projects/views/integration.py +++ b/rdmo/projects/views/integration.py @@ -88,5 +88,5 @@ def post(self, request, *args, **kwargs): try: integration = Integration.objects.filter(project_id=project_id).get(pk=pk) return integration.provider.webhook(request, integration) - except Integration.DoesNotExist: - raise Http404 + except Integration.DoesNotExist as e: + raise Http404 from e diff --git a/rdmo/projects/views/issue.py b/rdmo/projects/views/issue.py index 3a97c39de7..4c01fda62c 100644 --- a/rdmo/projects/views/issue.py +++ b/rdmo/projects/views/issue.py @@ -7,6 +7,7 @@ from django.template import TemplateSyntaxError from django.template.loader import render_to_string from django.views.generic import DetailView, UpdateView + from rest_framework.reverse import reverse from rdmo.core.mail import send_mail @@ -130,12 +131,12 @@ def post(self, request, *args, **kwargs): attachments = [] if form.cleaned_data.get('attachments_answers'): - title = '{}.{}'.format(project.title, attachments_format) + title = f'{project.title}.{attachments_format}' response = self.render_project_answers(project, snapshot, attachments_format) attachments.append((title, response.content, response['content-type'])) for view in form.cleaned_data.get('attachments_views'): - title = '{}.{}'.format(project.title, attachments_format) + title = f'{project.title}.{attachments_format}' response = self.render_project_views(project, snapshot, view, attachments_format) attachments.append((title, response.content, response['content-type'])) @@ -152,7 +153,8 @@ def post(self, request, *args, **kwargs): pass else: if mail_form.is_valid(): - to_emails = mail_form.cleaned_data.get('recipients', []) + mail_form.cleaned_data.get('recipients_input', []) + to_emails = [*mail_form.cleaned_data.get('recipients', []), + *mail_form.cleaned_data.get('recipients_input', [])] cc_emails = [request.user.email] reply_to = [request.user.email] @@ -168,12 +170,14 @@ def post(self, request, *args, **kwargs): return self.render_to_response(self.get_context_data(form=form, mail_form=mail_form)) def render_project_answers(self, project, snapshot, attachments_format): - return render_to_format(self.request, attachments_format, project.title, 'projects/project_answers_export.html', { - 'project': ProjectWrapper(project, snapshot), - 'format': attachments_format, - 'title': project.title, - 'resource_path': get_value_path(project, snapshot) - }) + return render_to_format( + self.request, attachments_format, project.title, 'projects/project_answers_export.html', { + 'project': ProjectWrapper(project, snapshot), + 'format': attachments_format, + 'title': project.title, + 'resource_path': get_value_path(project, snapshot) + } + ) def render_project_views(self, project, snapshot, view, attachments_format): try: @@ -181,10 +185,12 @@ def render_project_views(self, project, snapshot, view, attachments_format): except TemplateSyntaxError: return HttpResponse() - return render_to_format(self.request, attachments_format, project.title, 'projects/project_view_export.html', { - 'format': attachments_format, - 'title': project.title, - 'view': view, - 'rendered_view': rendered_view, - 'resource_path': get_value_path(project, snapshot) - }) + return render_to_format( + self.request, attachments_format, project.title, 'projects/project_view_export.html', { + 'format': attachments_format, + 'title': project.title, + 'view': view, + 'rendered_view': rendered_view, + 'resource_path': get_value_path(project, snapshot) + } + ) diff --git a/rdmo/projects/views/membership.py b/rdmo/projects/views/membership.py index 19c76fff4a..8392ff2d18 100644 --- a/rdmo/projects/views/membership.py +++ b/rdmo/projects/views/membership.py @@ -1,8 +1,7 @@ import logging from django.conf import settings -from django.http import (HttpResponseBadRequest, HttpResponseForbidden, - HttpResponseRedirect) +from django.http import HttpResponseBadRequest, HttpResponseForbidden, HttpResponseRedirect from django.shortcuts import get_object_or_404 from django.urls import reverse from django.views.generic import DeleteView, UpdateView @@ -73,7 +72,8 @@ def delete(self, *args, **kwargs): if (self.request.user in self.obj.project.owners) or is_site_manager(self.request.user): # user is owner or site manager if is_last_owner(self.obj.project, self.obj.user): - logger.info('User "%s" not allowed to remove last user "%s"', self.request.user.username, self.obj.user.username) + logger.info('User "%s" not allowed to remove last user "%s"', + self.request.user.username, self.obj.user.username) return HttpResponseBadRequest() else: logger.info('User "%s" deletes user "%s"', self.request.user.username, self.obj.user.username) diff --git a/rdmo/projects/views/project.py b/rdmo/projects/views/project.py index 16050259d0..6bbe56fbd5 100644 --- a/rdmo/projects/views/project.py +++ b/rdmo/projects/views/project.py @@ -10,11 +10,11 @@ from django.shortcuts import get_object_or_404, redirect from django.urls import reverse_lazy from django.utils.decorators import method_decorator -from django.utils.http import urlencode from django.utils.translation import gettext_lazy as _ from django.views.decorators.csrf import ensure_csrf_cookie from django.views.generic import DeleteView, DetailView, TemplateView from django.views.generic.edit import FormMixin + from django_filters.views import FilterView from rdmo.accounts.utils import is_site_manager @@ -66,7 +66,7 @@ def get_queryset(self): return queryset def get_context_data(self, **kwargs): - context = super(ProjectsView, self).get_context_data(**kwargs) + context = super().get_context_data(**kwargs) context['number_of_projects'] = self.get_queryset().count() context['invites'] = Invite.objects.filter(user=self.request.user) context['is_site_manager'] = is_site_manager(self.request.user) @@ -101,13 +101,13 @@ def get_queryset(self): raise PermissionDenied() def get_context_data(self, **kwargs): - context = super(SiteProjectsView, self).get_context_data(**kwargs) + context = super().get_context_data(**kwargs) context['number_of_projects'] = self.get_queryset().count() context['number_of_filtered_projects'] = context["filter"].qs.count() context = set_context_querystring_with_filter_and_page(context) context['catalogs'] = Catalog.objects.filter_current_site() \ - .filter_group(self.request.user) \ - .filter_availability(self.request.user) + .filter_group(self.request.user) \ + .filter_availability(self.request.user) return context @@ -126,11 +126,12 @@ class ProjectDetailView(ObjectPermissionMixin, DetailView): permission_required = 'projects.view_project_object' def get_context_data(self, **kwargs): - context = super(ProjectDetailView, self).get_context_data(**kwargs) + context = super().get_context_data(**kwargs) project = context['project'] ancestors = project.get_ancestors(include_self=True) values = project.values.filter(snapshot=None).select_related('attribute', 'option') - highest = Membership.objects.filter(project__in=ancestors, user_id=OuterRef('user_id')).order_by('-project__level') + highest = Membership.objects.filter(project__in=ancestors, user_id=OuterRef('user_id')) \ + .order_by('-project__level') memberships = Membership.objects.filter(project__in=ancestors) \ .annotate(highest=Subquery(highest.values('project__level')[:1])) \ .filter(highest=F('project__level')) \ diff --git a/rdmo/projects/views/project_answers.py b/rdmo/projects/views/project_answers.py index dd216fadd3..754a2bd4ed 100644 --- a/rdmo/projects/views/project_answers.py +++ b/rdmo/projects/views/project_answers.py @@ -3,6 +3,7 @@ from django.conf import settings from django.shortcuts import redirect from django.views.generic import DetailView + from rdmo.core.constants import VALUE_TYPE_FILE from rdmo.core.utils import render_to_format from rdmo.core.views import ObjectPermissionMixin @@ -10,6 +11,7 @@ from ..models import Project, Snapshot from ..utils import get_value_path + logger = logging.getLogger(__name__) @@ -81,4 +83,5 @@ def get_context_data(self, **kwargs): return context def render_to_response(self, context, **response_kwargs): - return render_to_format(self.request, context['format'], context['title'], 'projects/project_answers_export.html', context) + return render_to_format(self.request, context['format'], context['title'], + 'projects/project_answers_export.html', context) diff --git a/rdmo/projects/views/project_create.py b/rdmo/projects/views/project_create.py index 9e2f6e8137..eb1cac6f50 100644 --- a/rdmo/projects/views/project_create.py +++ b/rdmo/projects/views/project_create.py @@ -40,7 +40,7 @@ def form_valid(self, form): form.instance.site = get_current_site(self.request) # save the project - response = super(ProjectCreateView, self).form_valid(form) + response = super().form_valid(form) # add all tasks to project tasks = Task.objects.filter_current_site() \ diff --git a/rdmo/projects/views/project_update.py b/rdmo/projects/views/project_update.py index 98c7c4bab2..b6e79be2cd 100644 --- a/rdmo/projects/views/project_update.py +++ b/rdmo/projects/views/project_update.py @@ -1,17 +1,20 @@ import logging -from django.http import HttpResponseRedirect -from django.shortcuts import render -from django.utils.translation import gettext_lazy as _ from django.views.generic import UpdateView + from rdmo.core.views import ObjectPermissionMixin, RedirectViewMixin from rdmo.questions.models import Catalog from rdmo.tasks.models import Task from rdmo.views.models import View -from ..forms import (ProjectForm, ProjectUpdateCatalogForm, - ProjectUpdateInformationForm, ProjectUpdateParentForm, - ProjectUpdateTasksForm, ProjectUpdateViewsForm) +from ..forms import ( + ProjectForm, + ProjectUpdateCatalogForm, + ProjectUpdateInformationForm, + ProjectUpdateParentForm, + ProjectUpdateTasksForm, + ProjectUpdateViewsForm, +) from ..mixins import ProjectImportMixin from ..models import Project diff --git a/rdmo/projects/views/project_view.py b/rdmo/projects/views/project_view.py index 71cc6896f9..d7d1d95e2c 100644 --- a/rdmo/projects/views/project_view.py +++ b/rdmo/projects/views/project_view.py @@ -4,6 +4,7 @@ from django.http import Http404 from django.template import TemplateSyntaxError from django.views.generic import DetailView + from rdmo.core.constants import VALUE_TYPE_FILE from rdmo.core.utils import render_to_format from rdmo.core.views import ObjectPermissionMixin @@ -22,7 +23,7 @@ class ProjectViewView(ObjectPermissionMixin, DetailView): template_name = 'projects/project_view.html' def get_context_data(self, **kwargs): - context = super(ProjectViewView, self).get_context_data(**kwargs) + context = super().get_context_data(**kwargs) # prefetch most elements of the catalog context['project'].catalog.prefetch_elements() @@ -34,8 +35,8 @@ def get_context_data(self, **kwargs): try: context['view'] = context['project'].views.get(pk=self.kwargs.get('view_id')) - except View.DoesNotExist: - raise Http404 + except View.DoesNotExist as e: + raise Http404 from e try: context['rendered_view'] = context['view'].render(context['project'], @@ -64,7 +65,7 @@ class ProjectViewExportView(ObjectPermissionMixin, DetailView): def get_context_data(self, **kwargs): export_format = self.kwargs.get('format') - context = super(ProjectViewExportView, self).get_context_data(**kwargs) + context = super().get_context_data(**kwargs) # prefetch most elements of the catalog context['project'].catalog.prefetch_elements() @@ -76,8 +77,8 @@ def get_context_data(self, **kwargs): try: context['view'] = context['project'].views.get(pk=self.kwargs.get('view_id')) - except View.DoesNotExist: - raise Http404 + except View.DoesNotExist as e: + raise Http404 from e try: context['rendered_view'] = context['view'].render(context['project'], @@ -95,4 +96,5 @@ def get_context_data(self, **kwargs): return context def render_to_response(self, context, **response_kwargs): - return render_to_format(self.request, context['format'], context['title'], 'projects/project_view_export.html', context) + return render_to_format(self.request, context['format'], context['title'], + 'projects/project_view_export.html', context) diff --git a/rdmo/projects/views/snapshot.py b/rdmo/projects/views/snapshot.py index d693025754..ec378c5c99 100644 --- a/rdmo/projects/views/snapshot.py +++ b/rdmo/projects/views/snapshot.py @@ -20,13 +20,13 @@ class SnapshotCreateView(ObjectPermissionMixin, RedirectViewMixin, CreateView): def dispatch(self, *args, **kwargs): self.project = get_object_or_404(Project.objects.all(), pk=self.kwargs['project_id']) - return super(SnapshotCreateView, self).dispatch(*args, **kwargs) + return super().dispatch(*args, **kwargs) def get_permission_object(self): return self.project def get_form_kwargs(self): - kwargs = super(SnapshotCreateView, self).get_form_kwargs() + kwargs = super().get_form_kwargs() kwargs['project'] = self.project return kwargs diff --git a/rdmo/projects/viewsets.py b/rdmo/projects/viewsets.py index fcf562af9e..25352b1b29 100644 --- a/rdmo/projects/viewsets.py +++ b/rdmo/projects/viewsets.py @@ -3,17 +3,17 @@ from django.db.models import prefetch_related_objects from django.http import Http404, HttpResponseRedirect from django.utils.translation import gettext_lazy as _ -from django_filters.rest_framework import DjangoFilterBackend + from rest_framework import serializers from rest_framework.decorators import action from rest_framework.exceptions import NotFound -from rest_framework.mixins import (CreateModelMixin, ListModelMixin, - RetrieveModelMixin, UpdateModelMixin) +from rest_framework.mixins import CreateModelMixin, ListModelMixin, RetrieveModelMixin, UpdateModelMixin from rest_framework.permissions import IsAuthenticated from rest_framework.response import Response from rest_framework.reverse import reverse -from rest_framework.viewsets import (GenericViewSet, ModelViewSet, - ReadOnlyModelViewSet) +from rest_framework.viewsets import GenericViewSet, ModelViewSet, ReadOnlyModelViewSet + +from django_filters.rest_framework import DjangoFilterBackend from rest_framework_extensions.mixins import NestedViewSetMixin from rdmo.conditions.models import Condition @@ -25,21 +25,25 @@ from rdmo.views.models import View from .filters import SnapshotFilterBackend, ValueFilterBackend -from .models import (Continuation, Integration, Invite, Issue, Membership, - Project, Snapshot, Value) -from .permissions import (HasProjectPagePermission, HasProjectPermission, - HasProjectsPermission) -from .serializers.v1 import (IntegrationSerializer, InviteSerializer, - IssueSerializer, MembershipSerializer, - ProjectIntegrationSerializer, - ProjectInviteSerializer, - ProjectInviteUpdateSerializer, - ProjectIssueSerializer, - ProjectMembershipSerializer, - ProjectMembershipUpdateSerializer, - ProjectSerializer, ProjectSnapshotSerializer, - ProjectValueSerializer, SnapshotSerializer, - ValueSerializer) +from .models import Continuation, Integration, Invite, Issue, Membership, Project, Snapshot, Value +from .permissions import HasProjectPagePermission, HasProjectPermission, HasProjectsPermission +from .serializers.v1 import ( + IntegrationSerializer, + InviteSerializer, + IssueSerializer, + MembershipSerializer, + ProjectIntegrationSerializer, + ProjectInviteSerializer, + ProjectInviteUpdateSerializer, + ProjectIssueSerializer, + ProjectMembershipSerializer, + ProjectMembershipUpdateSerializer, + ProjectSerializer, + ProjectSnapshotSerializer, + ProjectValueSerializer, + SnapshotSerializer, + ValueSerializer, +) from .serializers.v1.overview import ProjectOverviewSerializer from .serializers.v1.page import PageSerializer from .utils import check_conditions, send_invite_email @@ -139,8 +143,8 @@ def options(self, request, pk=None): try: optionset_id = request.GET.get('optionset') optionset = OptionSet.objects.get(pk=optionset_id) - except (ValueError, OptionSet.DoesNotExist): - raise NotFound() + except (ValueError, OptionSet.DoesNotExist) as e: + raise NotFound() from e # check if the optionset belongs to this catalog and if it has a provider if Question.objects.filter_by_catalog(project.catalog).filter(optionsets=optionset) and \ @@ -194,8 +198,8 @@ def initial(self, request, *args, **kwargs): def get_project_from_parent_viewset(self): try: return Project.objects.filter_user(self.request.user).get(pk=self.get_parents_query_dict().get('project')) - except Project.DoesNotExist: - raise Http404 + except Project.DoesNotExist as e: + raise Http404 from e def perform_create(self, serializer): # this call provides the nested serializers with the project @@ -344,7 +348,8 @@ def set(self, request, parent_lookup_project, pk=None): if element.attribute == value.attribute: attributes.update([descendant.attribute for descendant in element.descendants]) - values = self.get_queryset().filter(attribute__in=attributes, set_prefix=value.set_prefix, set_index=value.set_index) + values = self.get_queryset().filter(attribute__in=attributes, set_prefix=value.set_prefix, + set_index=value.set_index) values.delete() return Response(status=204) @@ -419,7 +424,8 @@ def retrieve(self, request, *args, **kwargs): if request.GET.get('back') == 'true': prev_page = self.project.catalog.get_prev_page(page) if prev_page is not None: - url = reverse('v1-projects:project-page-detail', args=[self.project.id, prev_page.id]) + '?back=true' + url = reverse('v1-projects:project-page-detail', + args=[self.project.id, prev_page.id]) + '?back=true' return HttpResponseRedirect(url, status=303) else: next_page = self.project.catalog.get_next_page(page) diff --git a/rdmo/questions/admin.py b/rdmo/questions/admin.py index b4119821aa..9b52d07f74 100644 --- a/rdmo/questions/admin.py +++ b/rdmo/questions/admin.py @@ -5,18 +5,33 @@ from rdmo.core.admin import ElementAdminForm from rdmo.core.utils import get_language_fields -from .models import (Catalog, CatalogSection, Page, PageQuestion, - PageQuestionSet, Question, QuestionSet, - QuestionSetQuestion, QuestionSetQuestionSet, Section, - SectionPage) +from .models import ( + Catalog, + CatalogSection, + Page, + PageQuestion, + PageQuestionSet, + Question, + QuestionSet, + QuestionSetQuestion, + QuestionSetQuestionSet, + Section, + SectionPage, +) from .utils import get_widget_type_choices -from .validators import (CatalogLockedValidator, CatalogUniqueURIValidator, - PageLockedValidator, PageUniqueURIValidator, - QuestionLockedValidator, QuestionSetLockedValidator, - QuestionSetQuestionSetValidator, - QuestionSetUniqueURIValidator, - QuestionUniqueURIValidator, SectionLockedValidator, - SectionUniqueURIValidator) +from .validators import ( + CatalogLockedValidator, + CatalogUniqueURIValidator, + PageLockedValidator, + PageUniqueURIValidator, + QuestionLockedValidator, + QuestionSetLockedValidator, + QuestionSetQuestionSetValidator, + QuestionSetUniqueURIValidator, + QuestionUniqueURIValidator, + SectionLockedValidator, + SectionUniqueURIValidator, +) class CatalogAdminForm(ElementAdminForm): @@ -85,7 +100,7 @@ class CatalogAdmin(admin.ModelAdmin): form = CatalogAdminForm inlines = (CatalogSectionInline, ) - search_fields = ['uri'] + get_language_fields('title') + search_fields = ['uri', *get_language_fields('title')] list_display = ('uri', 'title', 'projects_count', 'available') readonly_fields = ('uri', ) list_filter = ('available', ) @@ -108,7 +123,7 @@ class SectionAdmin(admin.ModelAdmin): form = SectionAdminForm inlines = (SectionPageInline, ) - search_fields = ['uri'] + get_language_fields('title') + search_fields = ['uri', *get_language_fields('title')] list_display = ('uri', 'title') readonly_fields = ('uri', ) list_filter = ('catalogs', ) @@ -129,7 +144,7 @@ class PageAdmin(admin.ModelAdmin): form = PageAdminForm inlines = (PageQuestionSetInline, PageQuestionInline) - search_fields = ['uri'] + get_language_fields('title') + get_language_fields('help') + search_fields = ['uri', *get_language_fields('title'), *get_language_fields('help')] list_display = ('uri', 'attribute', 'is_collection') readonly_fields = ('uri', ) list_filter = ('sections__catalogs', 'sections', 'is_collection') @@ -151,7 +166,7 @@ class QuestionSetAdmin(admin.ModelAdmin): form = QuestionSetAdminForm inlines = (QuestionSetQuestionSetInline, QuestionSetQuestionInline) - search_fields = ['uri'] + get_language_fields('title') + get_language_fields('help') + search_fields = ['uri', *get_language_fields('title'), *get_language_fields('help')] list_display = ('uri', 'attribute', 'is_collection') readonly_fields = ('uri', ) list_filter = ('pages__sections__catalogs', 'pages__sections', 'pages', 'is_collection') @@ -161,10 +176,11 @@ class QuestionSetAdmin(admin.ModelAdmin): class QuestionAdmin(admin.ModelAdmin): form = QuestionAdminForm - search_fields = ['uri'] + get_language_fields('help') + get_language_fields('text') + search_fields = ['uri', *get_language_fields('help'), *get_language_fields('text')] list_display = ('uri', 'attribute', 'text', 'is_collection') readonly_fields = ('uri', ) - list_filter = ('pages__sections__catalogs', 'pages__sections', 'pages', 'is_collection', 'widget_type', 'value_type') + list_filter = ('pages__sections__catalogs', 'pages__sections', 'pages', 'is_collection', + 'widget_type', 'value_type') filter_horizontal = ('editors', 'optionsets', 'conditions') diff --git a/rdmo/questions/imports.py b/rdmo/questions/imports.py index 4616312036..ab79204b26 100644 --- a/rdmo/questions/imports.py +++ b/rdmo/questions/imports.py @@ -2,20 +2,31 @@ from django.contrib.sites.models import Site -from rdmo.core.imports import (check_permissions, set_common_fields, - set_foreign_field, set_lang_field, - set_m2m_instances, set_m2m_through_instances, - set_reverse_m2m_through_instance, - validate_instance) +from rdmo.core.imports import ( + check_permissions, + set_common_fields, + set_foreign_field, + set_lang_field, + set_m2m_instances, + set_m2m_through_instances, + set_reverse_m2m_through_instance, + validate_instance, +) from .models import Catalog, Page, Question, QuestionSet, Section from .utils import get_widget_types -from .validators import (CatalogLockedValidator, CatalogUniqueURIValidator, - PageLockedValidator, PageUniqueURIValidator, - QuestionLockedValidator, QuestionSetLockedValidator, - QuestionSetUniqueURIValidator, - QuestionUniqueURIValidator, SectionLockedValidator, - SectionUniqueURIValidator) +from .validators import ( + CatalogLockedValidator, + CatalogUniqueURIValidator, + PageLockedValidator, + PageUniqueURIValidator, + QuestionLockedValidator, + QuestionSetLockedValidator, + QuestionSetUniqueURIValidator, + QuestionUniqueURIValidator, + SectionLockedValidator, + SectionUniqueURIValidator, +) logger = logging.getLogger(__name__) @@ -154,8 +165,8 @@ def import_questionset(element, save=False, user=None): questionset.save() set_m2m_instances(questionset, 'conditions', element) set_reverse_m2m_through_instance(questionset, 'page', element, 'questionset', 'page', 'questionset_pages') - set_reverse_m2m_through_instance(questionset, 'questionset', element, 'questionset', 'parent', 'questionset_parents') - set_m2m_through_instances(questionset, 'questionsets', element, 'parent', 'questionset', 'questionset_questionsets') + set_reverse_m2m_through_instance(questionset, 'questionset', element, 'questionset', 'parent', 'questionset_parents') # noqa: E501 + set_m2m_through_instances(questionset, 'questionsets', element, 'parent', 'questionset', 'questionset_questionsets') # noqa: E501 set_m2m_through_instances(questionset, 'questions', element, 'questionset', 'question', 'questionset_questions') questionset.editors.add(Site.objects.get_current()) @@ -210,7 +221,7 @@ def import_question(element, save=False, user=None): question.save() set_reverse_m2m_through_instance(question, 'page', element, 'question', 'page', 'question_pages') - set_reverse_m2m_through_instance(question, 'questionset', element, 'question', 'questionset', 'question_questionsets') + set_reverse_m2m_through_instance(question, 'questionset', element, 'question', 'questionset', 'question_questionsets') # noqa: E501 set_m2m_instances(question, 'conditions', element) set_m2m_instances(question, 'optionsets', element) question.editors.add(Site.objects.get_current()) diff --git a/rdmo/questions/managers.py b/rdmo/questions/managers.py index 90e06debc6..31a57bceb9 100644 --- a/rdmo/questions/managers.py +++ b/rdmo/questions/managers.py @@ -1,10 +1,13 @@ from django.db import models -from rdmo.core.managers import (AvailabilityManagerMixin, - AvailabilityQuerySetMixin, - CurrentSiteManagerMixin, - CurrentSiteQuerySetMixin, GroupsManagerMixin, - GroupsQuerySetMixin) +from rdmo.core.managers import ( + AvailabilityManagerMixin, + AvailabilityQuerySetMixin, + CurrentSiteManagerMixin, + CurrentSiteQuerySetMixin, + GroupsManagerMixin, + GroupsQuerySetMixin, +) class CatalogQuestionSet(CurrentSiteQuerySetMixin, GroupsQuerySetMixin, AvailabilityQuerySetMixin, models.QuerySet): diff --git a/rdmo/questions/models/catalog.py b/rdmo/questions/models/catalog.py index 03e18f8643..592980bbe8 100644 --- a/rdmo/questions/models/catalog.py +++ b/rdmo/questions/models/catalog.py @@ -6,7 +6,7 @@ from django.utils.translation import gettext_lazy as _ from rdmo.core.models import Model, TranslationMixin -from rdmo.core.utils import copy_model, get_language_fields, join_url +from rdmo.core.utils import join_url from ..managers import CatalogManager @@ -170,7 +170,7 @@ def elements(self): def descendants(self): descendants = [] for element in self.elements: - descendants += [element] + element.descendants + descendants += [element, *element.descendants] return descendants @cached_property diff --git a/rdmo/questions/models/page.py b/rdmo/questions/models/page.py index 22f3d0cfd1..7b273ce4db 100644 --- a/rdmo/questions/models/page.py +++ b/rdmo/questions/models/page.py @@ -6,7 +6,7 @@ from rdmo.conditions.models import Condition from rdmo.core.models import Model, TranslationMixin -from rdmo.core.utils import copy_model, join_url +from rdmo.core.utils import join_url from rdmo.domain.models import Attribute from ..managers import PageManager @@ -232,7 +232,7 @@ def elements(self): def descendants(self): descendants = [] for element in self.elements: - descendants += [element] + element.descendants + descendants += [element, *element.descendants] return descendants def prefetch_elements(self): diff --git a/rdmo/questions/models/question.py b/rdmo/questions/models/question.py index 2b96a52354..d2a2e522c6 100644 --- a/rdmo/questions/models/question.py +++ b/rdmo/questions/models/question.py @@ -7,7 +7,7 @@ from rdmo.conditions.models import Condition from rdmo.core.constants import VALUE_TYPE_CHOICES from rdmo.core.models import Model, TranslationMixin -from rdmo.core.utils import copy_model, join_url +from rdmo.core.utils import join_url from rdmo.domain.models import Attribute from rdmo.options.models import Option @@ -284,8 +284,8 @@ def verbose_name_plural(self): @cached_property def is_locked(self): return self.locked or \ - any([page.is_locked for page in self.pages.all()]) or \ - any([questionset.is_locked for questionset in self.questionsets.all()]) + any(page.is_locked for page in self.pages.all()) or \ + any(questionset.is_locked for questionset in self.questionsets.all()) @cached_property def has_conditions(self): diff --git a/rdmo/questions/models/questionset.py b/rdmo/questions/models/questionset.py index 7b60247c55..8fd6821ef7 100644 --- a/rdmo/questions/models/questionset.py +++ b/rdmo/questions/models/questionset.py @@ -6,7 +6,7 @@ from rdmo.conditions.models import Condition from rdmo.core.models import Model, TranslationMixin -from rdmo.core.utils import copy_model, join_url +from rdmo.core.utils import join_url from rdmo.domain.models import Attribute from ..managers import QuestionSetManager @@ -213,8 +213,8 @@ def verbose_name_plural(self): @cached_property def is_locked(self): return self.locked or \ - any([page.is_locked for page in self.pages.all()]) or \ - any([questionset.is_locked for questionset in self.questionsets.all()]) + any(page.is_locked for page in self.pages.all()) or \ + any(questionset.is_locked for questionset in self.questionsets.all()) @cached_property def has_conditions(self): @@ -223,7 +223,8 @@ def has_conditions(self): @cached_property def elements(self): questionset_elements = list(self.questionset_questionsets.all()) + list(self.questionset_questions.all()) - return [questionset_element.element for questionset_element in sorted(questionset_elements, key=lambda e: e.order)] + return [questionset_element.element + for questionset_element in sorted(questionset_elements, key=lambda e: e.order)] @cached_property def descendants(self): @@ -231,7 +232,7 @@ def descendants(self): for element in self.elements: if element == self: raise RuntimeError(f'QuestionSet {self} is descendant of itself.') - descendants += [element] + element.descendants + descendants += [element, *element.descendants] return descendants def prefetch_elements(self): diff --git a/rdmo/questions/models/section.py b/rdmo/questions/models/section.py index 6a634e54e5..5feb4d9875 100644 --- a/rdmo/questions/models/section.py +++ b/rdmo/questions/models/section.py @@ -5,7 +5,7 @@ from django.utils.translation import gettext_lazy as _ from rdmo.core.models import Model, TranslationMixin -from rdmo.core.utils import copy_model, join_url +from rdmo.core.utils import join_url from ..managers import SectionManager @@ -108,7 +108,7 @@ def title(self): @cached_property def is_locked(self): - return self.locked or any([catalog.is_locked for catalog in self.catalogs.all()]) + return self.locked or any(catalog.is_locked for catalog in self.catalogs.all()) @cached_property def elements(self): @@ -119,7 +119,7 @@ def elements(self): def descendants(self): descendants = [] for element in self.elements: - descendants += [element] + element.descendants + descendants += [element, *element.descendants] return descendants def prefetch_elements(self): diff --git a/rdmo/questions/renderers/__init__.py b/rdmo/questions/renderers/__init__.py index c500ce0f70..112ee856be 100644 --- a/rdmo/questions/renderers/__init__.py +++ b/rdmo/questions/renderers/__init__.py @@ -1,12 +1,15 @@ from rdmo.conditions.renderers.mixins import ConditionRendererMixin from rdmo.core.renderers import BaseXMLRenderer from rdmo.domain.renderers.mixins import AttributeRendererMixin -from rdmo.options.renderers.mixins import (OptionRendererMixin, - OptionSetRendererMixin) - -from .mixins import (CatalogRendererMixin, PageRendererMixin, - QuestionRendererMixin, QuestionSetRendererMixin, - SectionRendererMixin) +from rdmo.options.renderers.mixins import OptionRendererMixin, OptionSetRendererMixin + +from .mixins import ( + CatalogRendererMixin, + PageRendererMixin, + QuestionRendererMixin, + QuestionSetRendererMixin, + SectionRendererMixin, +) class CatalogRenderer(CatalogRendererMixin, SectionRendererMixin, PageRendererMixin, diff --git a/rdmo/questions/renderers/mixins.py b/rdmo/questions/renderers/mixins.py index bff939d063..f22ef39a81 100644 --- a/rdmo/questions/renderers/mixins.py +++ b/rdmo/questions/renderers/mixins.py @@ -80,10 +80,14 @@ def render_page(self, xml, page): self.render_text_element(xml, 'is_collection', {}, page['is_collection']) for lang_code, lang_string, lang_field in get_languages(): - self.render_text_element(xml, 'title', {'lang': lang_code}, page['title_%s' % lang_code]) - self.render_text_element(xml, 'help', {'lang': lang_code}, page['help_%s' % lang_code]) - self.render_text_element(xml, 'verbose_name', {'lang': lang_code}, page['verbose_name_%s' % lang_code]) - self.render_text_element(xml, 'verbose_name_plural', {'lang': lang_code}, page['verbose_name_plural_%s' % lang_code]) + self.render_text_element(xml, 'title', {'lang': lang_code}, + page['title_%s' % lang_code]) + self.render_text_element(xml, 'help', {'lang': lang_code}, + page['help_%s' % lang_code]) + self.render_text_element(xml, 'verbose_name', {'lang': lang_code}, + page['verbose_name_%s' % lang_code]) + self.render_text_element(xml, 'verbose_name_plural', {'lang': lang_code}, + page['verbose_name_plural_%s' % lang_code]) xml.startElement('questionsets', {}) for page_questionset in page['page_questionsets']: @@ -144,10 +148,14 @@ def render_questionset(self, xml, questionset): self.render_text_element(xml, 'is_collection', {}, questionset['is_collection']) for lang_code, lang_string, lang_field in get_languages(): - self.render_text_element(xml, 'title', {'lang': lang_code}, questionset['title_%s' % lang_code]) - self.render_text_element(xml, 'help', {'lang': lang_code}, questionset['help_%s' % lang_code]) - self.render_text_element(xml, 'verbose_name', {'lang': lang_code}, questionset['verbose_name_%s' % lang_code]) - self.render_text_element(xml, 'verbose_name_plural', {'lang': lang_code}, questionset['verbose_name_plural_%s' % lang_code]) + self.render_text_element(xml, 'title', {'lang': lang_code}, + questionset['title_%s' % lang_code]) + self.render_text_element(xml, 'help', {'lang': lang_code}, + questionset['help_%s' % lang_code]) + self.render_text_element(xml, 'verbose_name', {'lang': lang_code}, + questionset['verbose_name_%s' % lang_code]) + self.render_text_element(xml, 'verbose_name_plural', {'lang': lang_code}, + questionset['verbose_name_plural_%s' % lang_code]) xml.startElement('questionsets', {}) for questionset_questionset in questionset['questionset_questionsets']: @@ -209,11 +217,16 @@ def render_question(self, xml, question): self.render_text_element(xml, 'is_optional', {}, question['is_optional']) for lang_code, lang_string, lang_field in get_languages(): - self.render_text_element(xml, 'help', {'lang': lang_code}, question['help_%s' % lang_code]) - self.render_text_element(xml, 'text', {'lang': lang_code}, question['text_%s' % lang_code]) - self.render_text_element(xml, 'default_text', {'lang': lang_code}, question['default_text_%s' % lang_code]) - self.render_text_element(xml, 'verbose_name', {'lang': lang_code}, question['verbose_name_%s' % lang_code]) - self.render_text_element(xml, 'verbose_name_plural', {'lang': lang_code}, question['verbose_name_plural_%s' % lang_code]) + self.render_text_element(xml, 'help', {'lang': lang_code}, + question['help_%s' % lang_code]) + self.render_text_element(xml, 'text', {'lang': lang_code}, + question['text_%s' % lang_code]) + self.render_text_element(xml, 'default_text', {'lang': lang_code}, + question['default_text_%s' % lang_code]) + self.render_text_element(xml, 'verbose_name', {'lang': lang_code}, + question['verbose_name_%s' % lang_code]) + self.render_text_element(xml, 'verbose_name_plural', {'lang': lang_code}, + question['verbose_name_plural_%s' % lang_code]) self.render_text_element(xml, 'default_option', {'dc:uri': question['default_option']}, None) self.render_text_element(xml, 'default_external_id', {}, question['default_external_id']) diff --git a/rdmo/questions/serializers/export.py b/rdmo/questions/serializers/export.py index cb6f9993fe..5f34bdb283 100644 --- a/rdmo/questions/serializers/export.py +++ b/rdmo/questions/serializers/export.py @@ -5,10 +5,19 @@ from rdmo.domain.serializers.export import AttributeExportSerializer from rdmo.options.serializers.export import OptionSetExportSerializer -from ..models import (Catalog, CatalogSection, Page, PageQuestion, - PageQuestionSet, Question, QuestionSet, - QuestionSetQuestion, QuestionSetQuestionSet, Section, - SectionPage) +from ..models import ( + Catalog, + CatalogSection, + Page, + PageQuestion, + PageQuestionSet, + Question, + QuestionSet, + QuestionSetQuestion, + QuestionSetQuestionSet, + Section, + SectionPage, +) class QuestionExportSerializer(TranslationSerializerMixin, serializers.ModelSerializer): diff --git a/rdmo/questions/serializers/v1/__init__.py b/rdmo/questions/serializers/v1/__init__.py index 0fde7e19c0..74c4f8b833 100644 --- a/rdmo/questions/serializers/v1/__init__.py +++ b/rdmo/questions/serializers/v1/__init__.py @@ -1,8 +1,5 @@ -from .catalog import (CatalogIndexSerializer, CatalogNestedSerializer, - CatalogSerializer) +from .catalog import CatalogIndexSerializer, CatalogNestedSerializer, CatalogSerializer from .page import PageIndexSerializer, PageNestedSerializer, PageSerializer from .question import QuestionIndexSerializer, QuestionSerializer -from .questionset import (QuestionSetIndexSerializer, - QuestionSetNestedSerializer, QuestionSetSerializer) -from .section import (SectionIndexSerializer, SectionNestedSerializer, - SectionSerializer) +from .questionset import QuestionSetIndexSerializer, QuestionSetNestedSerializer, QuestionSetSerializer +from .section import SectionIndexSerializer, SectionNestedSerializer, SectionSerializer diff --git a/rdmo/questions/serializers/v1/catalog.py b/rdmo/questions/serializers/v1/catalog.py index 4152f0df45..8d2290c1eb 100644 --- a/rdmo/questions/serializers/v1/catalog.py +++ b/rdmo/questions/serializers/v1/catalog.py @@ -1,10 +1,12 @@ from rest_framework import serializers -from rdmo.core.serializers import (ElementModelSerializerMixin, - ElementWarningSerializerMixin, - ReadOnlyObjectPermissionSerializerMixin, - ThroughModelSerializerMixin, - TranslationSerializerMixin) +from rdmo.core.serializers import ( + ElementModelSerializerMixin, + ElementWarningSerializerMixin, + ReadOnlyObjectPermissionSerializerMixin, + ThroughModelSerializerMixin, + TranslationSerializerMixin, +) from ...models import Catalog, CatalogSection from ...validators import CatalogLockedValidator, CatalogUniqueURIValidator @@ -78,8 +80,9 @@ class CatalogNestedSerializer(CatalogSerializer): elements = serializers.SerializerMethodField() class Meta(CatalogSerializer.Meta): - fields = CatalogSerializer.Meta.fields + ( - 'elements', + fields = ( + *CatalogSerializer.Meta.fields, + 'elements' ) def get_elements(self, obj): diff --git a/rdmo/questions/serializers/v1/page.py b/rdmo/questions/serializers/v1/page.py index 970ea65251..33dce4eaa0 100644 --- a/rdmo/questions/serializers/v1/page.py +++ b/rdmo/questions/serializers/v1/page.py @@ -1,10 +1,12 @@ from rest_framework import serializers -from rdmo.core.serializers import (ElementModelSerializerMixin, - ElementWarningSerializerMixin, - ReadOnlyObjectPermissionSerializerMixin, - ThroughModelSerializerMixin, - TranslationSerializerMixin) +from rdmo.core.serializers import ( + ElementModelSerializerMixin, + ElementWarningSerializerMixin, + ReadOnlyObjectPermissionSerializerMixin, + ThroughModelSerializerMixin, + TranslationSerializerMixin, +) from ...models import Page, PageQuestion, PageQuestionSet, QuestionSet, Section from ...validators import PageLockedValidator, PageUniqueURIValidator @@ -105,8 +107,9 @@ class PageNestedSerializer(PageSerializer): elements = serializers.SerializerMethodField() class Meta(PageSerializer.Meta): - fields = PageSerializer.Meta.fields + ( - 'elements', + fields = ( + *PageSerializer.Meta.fields, + 'elements' ) def get_elements(self, obj): diff --git a/rdmo/questions/serializers/v1/question.py b/rdmo/questions/serializers/v1/question.py index 5adab1511e..1a0f35ecda 100644 --- a/rdmo/questions/serializers/v1/question.py +++ b/rdmo/questions/serializers/v1/question.py @@ -1,13 +1,15 @@ -from rest_framework import serializers - from django.utils.translation import gettext_lazy as _ -from rdmo.core.constants import VALUE_TYPE_DATETIME, VALUE_TYPE_BOOLEAN -from rdmo.core.serializers import (ElementModelSerializerMixin, - ElementWarningSerializerMixin, - ReadOnlyObjectPermissionSerializerMixin, - ThroughModelSerializerMixin, - TranslationSerializerMixin) +from rest_framework import serializers + +from rdmo.core.constants import VALUE_TYPE_BOOLEAN, VALUE_TYPE_DATETIME +from rdmo.core.serializers import ( + ElementModelSerializerMixin, + ElementWarningSerializerMixin, + ReadOnlyObjectPermissionSerializerMixin, + ThroughModelSerializerMixin, + TranslationSerializerMixin, +) from ...models import Page, Question, QuestionSet from ...validators import QuestionLockedValidator, QuestionUniqueURIValidator diff --git a/rdmo/questions/serializers/v1/questionset.py b/rdmo/questions/serializers/v1/questionset.py index 0a7d83c35f..1fbb50f498 100644 --- a/rdmo/questions/serializers/v1/questionset.py +++ b/rdmo/questions/serializers/v1/questionset.py @@ -1,16 +1,15 @@ from rest_framework import serializers -from rdmo.core.serializers import (ElementModelSerializerMixin, - ElementWarningSerializerMixin, - ReadOnlyObjectPermissionSerializerMixin, - ThroughModelSerializerMixin, - TranslationSerializerMixin) - -from ...models import (Page, QuestionSet, QuestionSetQuestion, - QuestionSetQuestionSet) -from ...validators import (QuestionSetLockedValidator, - QuestionSetQuestionSetValidator, - QuestionSetUniqueURIValidator) +from rdmo.core.serializers import ( + ElementModelSerializerMixin, + ElementWarningSerializerMixin, + ReadOnlyObjectPermissionSerializerMixin, + ThroughModelSerializerMixin, + TranslationSerializerMixin, +) + +from ...models import Page, QuestionSet, QuestionSetQuestion, QuestionSetQuestionSet +from ...validators import QuestionSetLockedValidator, QuestionSetQuestionSetValidator, QuestionSetUniqueURIValidator from .question import QuestionSerializer @@ -43,8 +42,10 @@ class QuestionSetSerializer(ThroughModelSerializerMixin, TranslationSerializerMi pages = serializers.PrimaryKeyRelatedField(queryset=Page.objects.all(), required=False, many=True) parents = serializers.PrimaryKeyRelatedField(queryset=QuestionSet.objects.all(), required=False, many=True) - questionsets = QuestionSetQuestionSetSerializer(source='questionset_questionsets', read_only=False, required=False, many=True) - questions = QuestionSetQuestionSerializer(source='questionset_questions', read_only=False, required=False, many=True) + questionsets = QuestionSetQuestionSetSerializer(source='questionset_questionsets', + read_only=False, required=False, many=True) + questions = QuestionSetQuestionSerializer(source='questionset_questions', + read_only=False, required=False, many=True) warning = serializers.SerializerMethodField() read_only = serializers.SerializerMethodField() @@ -111,8 +112,9 @@ class QuestionSetNestedSerializer(QuestionSetSerializer): elements = serializers.SerializerMethodField() class Meta(QuestionSetSerializer.Meta): - fields = QuestionSetSerializer.Meta.fields + ( - 'elements', + fields = ( + *QuestionSetSerializer.Meta.fields, + 'elements' ) def get_elements(self, obj): diff --git a/rdmo/questions/serializers/v1/section.py b/rdmo/questions/serializers/v1/section.py index 34d14fe6ac..fe5187084f 100644 --- a/rdmo/questions/serializers/v1/section.py +++ b/rdmo/questions/serializers/v1/section.py @@ -1,10 +1,12 @@ from rest_framework import serializers -from rdmo.core.serializers import (ElementModelSerializerMixin, - ElementWarningSerializerMixin, - ReadOnlyObjectPermissionSerializerMixin, - ThroughModelSerializerMixin, - TranslationSerializerMixin) +from rdmo.core.serializers import ( + ElementModelSerializerMixin, + ElementWarningSerializerMixin, + ReadOnlyObjectPermissionSerializerMixin, + ThroughModelSerializerMixin, + TranslationSerializerMixin, +) from ...models import Catalog, Section, SectionPage from ...validators import SectionLockedValidator, SectionUniqueURIValidator @@ -74,8 +76,9 @@ class SectionNestedSerializer(SectionSerializer): elements = serializers.SerializerMethodField() class Meta(SectionSerializer.Meta): - fields = SectionSerializer.Meta.fields + ( - 'elements', + fields = ( + *SectionSerializer.Meta.fields, + 'elements' ) def get_elements(self, obj): diff --git a/rdmo/questions/tests/test_models.py b/rdmo/questions/tests/test_models.py index a2fcb5f657..8fb1ab6b96 100644 --- a/rdmo/questions/tests/test_models.py +++ b/rdmo/questions/tests/test_models.py @@ -33,15 +33,19 @@ def test_catalog_descendants(db): page = section_page.page descendant_ids.append(page.id) - page_elements = list(page.page_questionsets.all()) + list(page.page_questions.all()) - page_elements = sorted(page_elements, key=lambda e: e.order) + page_elements = sorted([ + *page.page_questionsets.all(), + *page.page_questions.all() + ], key=lambda e: e.order) for page_element in page_elements: element = page_element.element descendant_ids.append(element.id) try: - element_elements = list(element.questionset_questionsets.all()) + list(element.questionset_questions.all()) - element_elements = sorted(element_elements, key=lambda e: e.order) + element_elements = sorted([ + *element.questionset_questionsets.all(), + *element.questionset_questions.all() + ], key=lambda e: e.order) except AttributeError: element_elements = [] @@ -50,8 +54,10 @@ def test_catalog_descendants(db): descendant_ids.append(element2.id) try: - element_elements2 = list(element2.questionset_questionsets.all()) + list(element2.questionset_questions.all()) - element_elements2 = sorted(element_elements2, key=lambda e: e.order) + element_elements2 = sorted([ + *element2.questionset_questionsets.all(), + *element2.questionset_questions.all() + ], key=lambda e: e.order) except AttributeError: element_elements2 = [] @@ -89,15 +95,19 @@ def test_section_descendants(db): page = section_page.page descendant_ids.append(page.id) - page_elements = list(page.page_questionsets.all()) + list(page.page_questions.all()) - page_elements = sorted(page_elements, key=lambda e: e.order) + page_elements = sorted([ + *page.page_questionsets.all(), + *page.page_questions.all() + ], key=lambda e: e.order) for page_element in page_elements: element = page_element.element descendant_ids.append(element.id) try: - element_elements = list(element.questionset_questionsets.all()) + list(element.questionset_questions.all()) - element_elements = sorted(element_elements, key=lambda e: e.order) + element_elements = sorted([ + *element.questionset_questionsets.all(), + *element.questionset_questions.all() + ], key=lambda e: e.order) except AttributeError: element_elements = [] @@ -106,8 +116,10 @@ def test_section_descendants(db): descendant_ids.append(element2.id) try: - element_elements2 = list(element2.questionset_questionsets.all()) + list(element2.questionset_questions.all()) - element_elements2 = sorted(element_elements2, key=lambda e: e.order) + element_elements2 = sorted([ + *element2.questionset_questionsets.all(), + *element2.questionset_questions.all() + ], key=lambda e: e.order) except AttributeError: element_elements2 = [] @@ -134,15 +146,19 @@ def test_page_descendants(db): for instance in instances: descendant_ids = [] - page_elements = list(instance.page_questionsets.all()) + list(instance.page_questions.all()) - page_elements = sorted(page_elements, key=lambda e: e.order) + page_elements = sorted([ + *instance.page_questionsets.all(), + *instance.page_questions.all() + ], key=lambda e: e.order) for page_element in page_elements: element = page_element.element descendant_ids.append(element.id) try: - element_elements = list(element.questionset_questionsets.all()) + list(element.questionset_questions.all()) - element_elements = sorted(element_elements, key=lambda e: e.order) + element_elements = sorted([ + *element.questionset_questionsets.all(), + *element.questionset_questions.all() + ], key=lambda e: e.order) except AttributeError: element_elements = [] @@ -151,8 +167,10 @@ def test_page_descendants(db): descendant_ids.append(element2.id) try: - element_elements2 = list(element2.questionset_questionsets.all()) + list(element2.questionset_questions.all()) - element_elements2 = sorted(element_elements2, key=lambda e: e.order) + element_elements2 = sorted([ + *element2.questionset_questionsets.all(), + *element2.questionset_questions.all() + ], key=lambda e: e.order) except AttributeError: element_elements2 = [] @@ -187,8 +205,10 @@ def test_questionset_descendants(db): descendant_ids = [] try: - element_elements = list(instance.questionset_questionsets.all()) + list(instance.questionset_questions.all()) - element_elements = sorted(element_elements, key=lambda e: e.order) + element_elements = sorted([ + *instance.questionset_questionsets.all(), + *instance.questionset_questions.all() + ], key=lambda e: e.order) except AttributeError: element_elements = [] @@ -197,8 +217,10 @@ def test_questionset_descendants(db): descendant_ids.append(element2.id) try: - element_elements2 = list(element2.questionset_questionsets.all()) + list(element2.questionset_questions.all()) - element_elements2 = sorted(element_elements2, key=lambda e: e.order) + element_elements2 = sorted([ + *element2.questionset_questionsets.all(), + *element2.questionset_questions.all() + ], key=lambda e: e.order) except AttributeError: element_elements2 = [] diff --git a/rdmo/questions/tests/test_validator_locked_catalogs.py b/rdmo/questions/tests/test_validator_locked_catalogs.py index 4345b65f72..3951e32e81 100644 --- a/rdmo/questions/tests/test_validator_locked_catalogs.py +++ b/rdmo/questions/tests/test_validator_locked_catalogs.py @@ -1,7 +1,8 @@ import pytest + from django.core.exceptions import ValidationError -from rest_framework.exceptions import \ - ValidationError as RestFameworkValidationError + +from rest_framework.exceptions import ValidationError as RestFameworkValidationError from ..models import Catalog from ..serializers.v1 import CatalogSerializer diff --git a/rdmo/questions/tests/test_validator_locked_pages.py b/rdmo/questions/tests/test_validator_locked_pages.py index ed117b66dd..9ebc011e54 100644 --- a/rdmo/questions/tests/test_validator_locked_pages.py +++ b/rdmo/questions/tests/test_validator_locked_pages.py @@ -1,7 +1,8 @@ import pytest + from django.core.exceptions import ValidationError -from rest_framework.exceptions import \ - ValidationError as RestFameworkValidationError + +from rest_framework.exceptions import ValidationError as RestFameworkValidationError from ..models import Page, Section from ..serializers.v1 import PageSerializer diff --git a/rdmo/questions/tests/test_validator_locked_questions.py b/rdmo/questions/tests/test_validator_locked_questions.py index 4668a6e999..5b82e13359 100644 --- a/rdmo/questions/tests/test_validator_locked_questions.py +++ b/rdmo/questions/tests/test_validator_locked_questions.py @@ -1,7 +1,8 @@ import pytest + from django.core.exceptions import ValidationError -from rest_framework.exceptions import \ - ValidationError as RestFameworkValidationError + +from rest_framework.exceptions import ValidationError as RestFameworkValidationError from ..models import Page, Question, QuestionSet from ..serializers.v1 import QuestionSerializer diff --git a/rdmo/questions/tests/test_validator_locked_questionsets.py b/rdmo/questions/tests/test_validator_locked_questionsets.py index 56183101a4..d4e5f6fc59 100644 --- a/rdmo/questions/tests/test_validator_locked_questionsets.py +++ b/rdmo/questions/tests/test_validator_locked_questionsets.py @@ -1,7 +1,8 @@ import pytest + from django.core.exceptions import ValidationError -from rest_framework.exceptions import \ - ValidationError as RestFameworkValidationError + +from rest_framework.exceptions import ValidationError as RestFameworkValidationError from ..models import Page, QuestionSet from ..serializers.v1 import QuestionSetSerializer diff --git a/rdmo/questions/tests/test_validator_locked_sections.py b/rdmo/questions/tests/test_validator_locked_sections.py index de59406b78..80f50bbcd2 100644 --- a/rdmo/questions/tests/test_validator_locked_sections.py +++ b/rdmo/questions/tests/test_validator_locked_sections.py @@ -1,7 +1,8 @@ import pytest + from django.core.exceptions import ValidationError -from rest_framework.exceptions import \ - ValidationError as RestFameworkValidationError + +from rest_framework.exceptions import ValidationError as RestFameworkValidationError from ..models import Catalog, Section from ..serializers.v1 import SectionSerializer diff --git a/rdmo/questions/tests/test_validator_unique_uri_catalogs.py b/rdmo/questions/tests/test_validator_unique_uri_catalogs.py index 411fae4311..5e418d47de 100644 --- a/rdmo/questions/tests/test_validator_unique_uri_catalogs.py +++ b/rdmo/questions/tests/test_validator_unique_uri_catalogs.py @@ -1,10 +1,11 @@ import pytest + from django.conf import settings from django.core.exceptions import ValidationError -from rest_framework.exceptions import \ - ValidationError as RestFameworkValidationError -from ..models import Question, QuestionSet, Page, Catalog, Section +from rest_framework.exceptions import ValidationError as RestFameworkValidationError + +from ..models import Catalog, Page, Question, QuestionSet, Section from ..serializers.v1 import SectionSerializer from ..validators import CatalogUniqueURIValidator diff --git a/rdmo/questions/tests/test_validator_unique_uri_pages.py b/rdmo/questions/tests/test_validator_unique_uri_pages.py index 133d3722ad..a2878451e7 100644 --- a/rdmo/questions/tests/test_validator_unique_uri_pages.py +++ b/rdmo/questions/tests/test_validator_unique_uri_pages.py @@ -1,10 +1,11 @@ import pytest + from django.conf import settings from django.core.exceptions import ValidationError -from rest_framework.exceptions import \ - ValidationError as RestFameworkValidationError -from ..models import Question, QuestionSet, Page, Catalog, Section +from rest_framework.exceptions import ValidationError as RestFameworkValidationError + +from ..models import Catalog, Page, Question, QuestionSet, Section from ..serializers.v1 import PageSerializer from ..validators import PageUniqueURIValidator diff --git a/rdmo/questions/tests/test_validator_unique_uri_questions.py b/rdmo/questions/tests/test_validator_unique_uri_questions.py index 0412afa49a..bcd53d8fec 100644 --- a/rdmo/questions/tests/test_validator_unique_uri_questions.py +++ b/rdmo/questions/tests/test_validator_unique_uri_questions.py @@ -1,10 +1,11 @@ import pytest + from django.conf import settings from django.core.exceptions import ValidationError -from rest_framework.exceptions import \ - ValidationError as RestFameworkValidationError -from ..models import Question, QuestionSet, Page, Catalog, Section +from rest_framework.exceptions import ValidationError as RestFameworkValidationError + +from ..models import Catalog, Page, Question, QuestionSet, Section from ..serializers.v1 import QuestionSerializer from ..validators import QuestionUniqueURIValidator diff --git a/rdmo/questions/tests/test_validator_unique_uri_questionsets.py b/rdmo/questions/tests/test_validator_unique_uri_questionsets.py index 1d93d0787d..5b4c9a7341 100644 --- a/rdmo/questions/tests/test_validator_unique_uri_questionsets.py +++ b/rdmo/questions/tests/test_validator_unique_uri_questionsets.py @@ -1,10 +1,11 @@ import pytest + from django.conf import settings from django.core.exceptions import ValidationError -from rest_framework.exceptions import \ - ValidationError as RestFameworkValidationError -from ..models import Question, QuestionSet, Page, Catalog, Section +from rest_framework.exceptions import ValidationError as RestFameworkValidationError + +from ..models import Catalog, Page, Question, QuestionSet, Section from ..serializers.v1 import QuestionSetSerializer from ..validators import QuestionSetUniqueURIValidator diff --git a/rdmo/questions/tests/test_validator_unique_uri_sections.py b/rdmo/questions/tests/test_validator_unique_uri_sections.py index 57c85f2d65..3af509afa9 100644 --- a/rdmo/questions/tests/test_validator_unique_uri_sections.py +++ b/rdmo/questions/tests/test_validator_unique_uri_sections.py @@ -1,10 +1,11 @@ import pytest + from django.conf import settings from django.core.exceptions import ValidationError -from rest_framework.exceptions import \ - ValidationError as RestFameworkValidationError -from ..models import Question, QuestionSet, Page, Catalog, Section +from rest_framework.exceptions import ValidationError as RestFameworkValidationError + +from ..models import Catalog, Page, Question, QuestionSet, Section from ..serializers.v1 import SectionSerializer from ..validators import SectionUniqueURIValidator diff --git a/rdmo/questions/tests/test_viewset_catalog.py b/rdmo/questions/tests/test_viewset_catalog.py index c84c6204b8..cddf9291a5 100644 --- a/rdmo/questions/tests/test_viewset_catalog.py +++ b/rdmo/questions/tests/test_viewset_catalog.py @@ -1,6 +1,7 @@ import xml.etree.ElementTree as et import pytest + from django.urls import reverse from ..models import Catalog @@ -112,7 +113,7 @@ def test_create(db, client, username, password): url = reverse(urlnames['list']) data = { 'uri_prefix': instance.uri_prefix, - 'uri_path': '%s_new_%s' % (instance.uri_path, username), + 'uri_path': f'{instance.uri_path}_new_{username}', 'comment': instance.comment, 'order': instance.order, 'title_en': instance.title_lang1, @@ -136,7 +137,7 @@ def test_create_m2m(db, client, username, password): url = reverse(urlnames['list']) data = { 'uri_prefix': instance.uri_prefix, - 'uri_path': '%s_new_%s' % (instance.uri_path, username), + 'uri_path': f'{instance.uri_path}_new_{username}', 'comment': instance.comment, 'order': instance.order, 'title_en': instance.title_lang1, diff --git a/rdmo/questions/tests/test_viewset_catalog_multisite.py b/rdmo/questions/tests/test_viewset_catalog_multisite.py index d8b14db237..1a69aff5af 100644 --- a/rdmo/questions/tests/test_viewset_catalog_multisite.py +++ b/rdmo/questions/tests/test_viewset_catalog_multisite.py @@ -1,15 +1,14 @@ import xml.etree.ElementTree as et import pytest + from django.urls import reverse +from ...core.tests import get_obj_perms_status_code from ...core.tests import multisite_status_map as status_map from ...core.tests import multisite_users as users -from ...core.tests import get_obj_perms_status_code - from ..models import Catalog - -from .test_viewset_catalog import urlnames, export_formats +from .test_viewset_catalog import export_formats, urlnames @pytest.mark.parametrize('username,password', users) @@ -77,7 +76,7 @@ def test_create(db, client, username, password): url = reverse(urlnames['list']) data = { 'uri_prefix': instance.uri_prefix, - 'uri_path': '%s_new_%s' % (instance.uri_path, username), + 'uri_path': f'{instance.uri_path}_new_{username}', 'comment': instance.comment, 'order': instance.order, 'title_en': instance.title_lang1, @@ -101,7 +100,7 @@ def test_create_m2m(db, client, username, password): url = reverse(urlnames['list']) data = { 'uri_prefix': instance.uri_prefix, - 'uri_path': '%s_new_%s' % (instance.uri_path, username), + 'uri_path': f'{instance.uri_path}_new_{username}', 'comment': instance.comment, 'order': instance.order, 'title_en': instance.title_lang1, diff --git a/rdmo/questions/tests/test_viewset_page.py b/rdmo/questions/tests/test_viewset_page.py index 7b3ec2d748..7d292e1b95 100644 --- a/rdmo/questions/tests/test_viewset_page.py +++ b/rdmo/questions/tests/test_viewset_page.py @@ -1,6 +1,7 @@ import xml.etree.ElementTree as et import pytest + from django.db.models import Max from django.urls import reverse @@ -113,7 +114,7 @@ def test_create(db, client, username, password): url = reverse(urlnames['list']) data = { 'uri_prefix': instance.uri_prefix, - 'uri_path': '%s_new_%s' % (instance.uri_path, username), + 'uri_path': f'{instance.uri_path}_new_{username}', 'comment': instance.comment, 'attribute': instance.attribute.pk if instance.attribute else '', 'is_collection': instance.is_collection, @@ -144,7 +145,7 @@ def test_create_section(db, client, username, password): url = reverse(urlnames['list']) data = { 'uri_prefix': instance.uri_prefix, - 'uri_path': '%s_new_%s' % (instance.uri_path, username), + 'uri_path': f'{instance.uri_path}_new_{username}', 'comment': instance.comment, 'attribute': instance.attribute.pk if instance.attribute else '', 'is_collection': instance.is_collection, @@ -164,7 +165,7 @@ def test_create_section(db, client, username, password): if response.status_code == 201: new_instance = Page.objects.get(id=response.json().get('id')) section.refresh_from_db() - assert section_pages + [(new_instance.id, order)] == \ + assert [*section_pages, (new_instance.id, order)] == \ list(section.section_pages.values_list('page', 'order')) @@ -187,7 +188,7 @@ def test_create_m2m(db, client, username, password): url = reverse(urlnames['list']) data = { 'uri_prefix': instance.uri_prefix, - 'uri_path': '%s_new_%s' % (instance.uri_path, username), + 'uri_path': f'{instance.uri_path}_new_{username}', 'comment': instance.comment, 'attribute': instance.attribute.pk if instance.attribute else '', 'is_collection': instance.is_collection, diff --git a/rdmo/questions/tests/test_viewset_page_multisite.py b/rdmo/questions/tests/test_viewset_page_multisite.py index 16f001af38..20abffb078 100644 --- a/rdmo/questions/tests/test_viewset_page_multisite.py +++ b/rdmo/questions/tests/test_viewset_page_multisite.py @@ -1,16 +1,15 @@ import xml.etree.ElementTree as et import pytest + from django.db.models import Max from django.urls import reverse +from ...core.tests import get_obj_perms_status_code from ...core.tests import multisite_status_map as status_map from ...core.tests import multisite_users as users -from ...core.tests import get_obj_perms_status_code - from ..models import Page - -from .test_viewset_page import urlnames, export_formats +from .test_viewset_page import export_formats, urlnames @pytest.mark.parametrize('username,password', users) @@ -78,7 +77,7 @@ def test_create(db, client, username, password): url = reverse(urlnames['list']) data = { 'uri_prefix': instance.uri_prefix, - 'uri_path': '%s_new_%s' % (instance.uri_path, username), + 'uri_path': f'{instance.uri_path}_new_{username}', 'comment': instance.comment, 'attribute': instance.attribute.pk if instance.attribute else '', 'is_collection': instance.is_collection, @@ -109,7 +108,7 @@ def test_create_section(db, client, username, password): url = reverse(urlnames['list']) data = { 'uri_prefix': instance.uri_prefix, - 'uri_path': '%s_new_%s' % (instance.uri_path, username), + 'uri_path': f'{instance.uri_path}_new_{username}', 'comment': instance.comment, 'attribute': instance.attribute.pk if instance.attribute else '', 'is_collection': instance.is_collection, @@ -129,7 +128,7 @@ def test_create_section(db, client, username, password): if response.status_code == 201: new_instance = Page.objects.get(id=response.json().get('id')) section.refresh_from_db() - assert section_pages + [(new_instance.id, order)] == \ + assert [*section_pages, (new_instance.id, order)] == \ list(section.section_pages.values_list('page', 'order')) @@ -152,7 +151,7 @@ def test_create_m2m(db, client, username, password): url = reverse(urlnames['list']) data = { 'uri_prefix': instance.uri_prefix, - 'uri_path': '%s_new_%s' % (instance.uri_path, username), + 'uri_path': f'{instance.uri_path}_new_{username}', 'comment': instance.comment, 'attribute': instance.attribute.pk if instance.attribute else '', 'is_collection': instance.is_collection, diff --git a/rdmo/questions/tests/test_viewset_question.py b/rdmo/questions/tests/test_viewset_question.py index 8cc192b2bd..929a7a97e6 100644 --- a/rdmo/questions/tests/test_viewset_question.py +++ b/rdmo/questions/tests/test_viewset_question.py @@ -1,6 +1,7 @@ import xml.etree.ElementTree as et import pytest + from django.db.models import Max from django.urls import reverse @@ -102,7 +103,7 @@ def test_create(db, client, username, password): url = reverse(urlnames['list']) data = { 'uri_prefix': instance.uri_prefix, - 'uri_path': '%s_new_%s' % (instance.uri_path, username), + 'uri_path': f'{instance.uri_path}_new_{username}', 'comment': instance.comment or '', 'attribute': instance.attribute.pk if instance.attribute else '', 'is_collection': instance.is_collection, @@ -139,7 +140,7 @@ def test_create_page(db, client, username, password): url = reverse(urlnames['list']) data = { 'uri_prefix': instance.uri_prefix, - 'uri_path': '%s_new_%s' % (instance.uri_path, username), + 'uri_path': f'{instance.uri_path}_new_{username}', 'comment': instance.comment or '', 'attribute': instance.attribute.pk if instance.attribute else '', 'is_collection': instance.is_collection, @@ -165,7 +166,7 @@ def test_create_page(db, client, username, password): if response.status_code == 201: new_instance = Question.objects.get(id=response.json().get('id')) page.refresh_from_db() - assert page_questions + [(new_instance.id, order)] == \ + assert [*page_questions, (new_instance.id, order)] == \ list(page.page_questions.values_list('question', 'order')) @@ -183,7 +184,7 @@ def test_create_questionset(db, client, username, password): url = reverse(urlnames['list']) data = { 'uri_prefix': instance.uri_prefix, - 'uri_path': '%s_new_%s' % (instance.uri_path, username), + 'uri_path': f'{instance.uri_path}_new_{username}', 'comment': instance.comment or '', 'attribute': instance.attribute.pk if instance.attribute else '', 'is_collection': instance.is_collection, @@ -209,7 +210,7 @@ def test_create_questionset(db, client, username, password): if response.status_code == 201: new_instance = Question.objects.get(id=response.json().get('id')) questionset.refresh_from_db() - assert questionset_questions + [(new_instance.id, order)] == \ + assert [*questionset_questions, (new_instance.id, order)] == \ list(questionset.questionset_questions.values_list('question', 'order')) @@ -225,7 +226,7 @@ def test_create_m2m(db, client, username, password): url = reverse(urlnames['list']) data = { 'uri_prefix': instance.uri_prefix, - 'uri_path': '%s_new_%s' % (instance.uri_path, username), + 'uri_path': f'{instance.uri_path}_new_{username}', 'comment': instance.comment or '', 'attribute': instance.attribute.pk if instance.attribute else '', 'is_collection': instance.is_collection, diff --git a/rdmo/questions/tests/test_viewset_question_multisite.py b/rdmo/questions/tests/test_viewset_question_multisite.py index 495bade005..230c611633 100644 --- a/rdmo/questions/tests/test_viewset_question_multisite.py +++ b/rdmo/questions/tests/test_viewset_question_multisite.py @@ -1,16 +1,15 @@ import xml.etree.ElementTree as et import pytest + from django.db.models import Max from django.urls import reverse +from ...core.tests import get_obj_perms_status_code from ...core.tests import multisite_status_map as status_map from ...core.tests import multisite_users as users -from ...core.tests import get_obj_perms_status_code - from ..models import Question - -from .test_viewset_question import urlnames, export_formats +from .test_viewset_question import export_formats, urlnames @pytest.mark.parametrize('username,password', users) @@ -67,7 +66,7 @@ def test_create(db, client, username, password): url = reverse(urlnames['list']) data = { 'uri_prefix': instance.uri_prefix, - 'uri_path': '%s_new_%s' % (instance.uri_path, username), + 'uri_path': f'{instance.uri_path}_new_{username}', 'comment': instance.comment or '', 'attribute': instance.attribute.pk if instance.attribute else '', 'is_collection': instance.is_collection, @@ -104,7 +103,7 @@ def test_create_page(db, client, username, password): url = reverse(urlnames['list']) data = { 'uri_prefix': instance.uri_prefix, - 'uri_path': '%s_new_%s' % (instance.uri_path, username), + 'uri_path': f'{instance.uri_path}_new_{username}', 'comment': instance.comment or '', 'attribute': instance.attribute.pk if instance.attribute else '', 'is_collection': instance.is_collection, @@ -130,7 +129,7 @@ def test_create_page(db, client, username, password): if response.status_code == 201: new_instance = Question.objects.get(id=response.json().get('id')) page.refresh_from_db() - assert page_questions + [(new_instance.id, order)] == \ + assert [*page_questions, (new_instance.id, order)] == \ list(page.page_questions.values_list('question', 'order')) @@ -148,7 +147,7 @@ def test_create_questionset(db, client, username, password): url = reverse(urlnames['list']) data = { 'uri_prefix': instance.uri_prefix, - 'uri_path': '%s_new_%s' % (instance.uri_path, username), + 'uri_path': f'{instance.uri_path}_new_{username}', 'comment': instance.comment or '', 'attribute': instance.attribute.pk if instance.attribute else '', 'is_collection': instance.is_collection, @@ -174,7 +173,7 @@ def test_create_questionset(db, client, username, password): if response.status_code == 201: new_instance = Question.objects.get(id=response.json().get('id')) questionset.refresh_from_db() - assert questionset_questions + [(new_instance.id, order)] == \ + assert [*questionset_questions, (new_instance.id, order)] == \ list(questionset.questionset_questions.values_list('question', 'order')) @@ -190,7 +189,7 @@ def test_create_m2m(db, client, username, password): url = reverse(urlnames['list']) data = { 'uri_prefix': instance.uri_prefix, - 'uri_path': '%s_new_%s' % (instance.uri_path, username), + 'uri_path': f'{instance.uri_path}_new_{username}', 'comment': instance.comment or '', 'attribute': instance.attribute.pk if instance.attribute else '', 'is_collection': instance.is_collection, diff --git a/rdmo/questions/tests/test_viewset_questionset.py b/rdmo/questions/tests/test_viewset_questionset.py index 1462562b5f..0ab31c35cd 100644 --- a/rdmo/questions/tests/test_viewset_questionset.py +++ b/rdmo/questions/tests/test_viewset_questionset.py @@ -1,6 +1,7 @@ import xml.etree.ElementTree as et import pytest + from django.db.models import Max from django.urls import reverse @@ -113,7 +114,7 @@ def test_create(db, client, username, password): url = reverse(urlnames['list']) data = { 'uri_prefix': instance.uri_prefix, - 'uri_path': '%s_new_%s' % (instance.uri_path, username), + 'uri_path': f'{instance.uri_path}_new_{username}', 'comment': instance.comment, 'attribute': instance.attribute.pk if instance.attribute else '', 'is_collection': instance.is_collection, @@ -144,7 +145,7 @@ def test_create_page(db, client, username, password): url = reverse(urlnames['list']) data = { 'uri_prefix': instance.uri_prefix, - 'uri_path': '%s_new_%s' % (instance.uri_path, username), + 'uri_path': f'{instance.uri_path}_new_{username}', 'comment': instance.comment, 'attribute': instance.attribute.pk if instance.attribute else '', 'is_collection': instance.is_collection, @@ -164,7 +165,7 @@ def test_create_page(db, client, username, password): if response.status_code == 201: new_instance = QuestionSet.objects.get(id=response.json().get('id')) page.refresh_from_db() - assert page_questionsets + [(new_instance.id, order)] == \ + assert [*page_questionsets, (new_instance.id, order)] == \ list(page.page_questionsets.values_list('questionset', 'order')) @@ -182,7 +183,7 @@ def test_create_parent(db, client, username, password): url = reverse(urlnames['list']) data = { 'uri_prefix': instance.uri_prefix, - 'uri_path': '%s_new_%s' % (instance.uri_path, username), + 'uri_path': f'{instance.uri_path}_new_{username}', 'comment': instance.comment, 'attribute': instance.attribute.pk if instance.attribute else '', 'is_collection': instance.is_collection, @@ -202,7 +203,7 @@ def test_create_parent(db, client, username, password): if response.status_code == 201: new_instance = QuestionSet.objects.get(id=response.json().get('id')) parent.refresh_from_db() - assert parent_questionsets + [(new_instance.id, order)] == \ + assert [*parent_questionsets, (new_instance.id, order)] == \ list(parent.questionset_questionsets.values_list('questionset', 'order')) @@ -225,7 +226,7 @@ def test_create_m2m(db, client, username, password): url = reverse(urlnames['list']) data = { 'uri_prefix': instance.uri_prefix, - 'uri_path': '%s_new_%s' % (instance.uri_path, username), + 'uri_path': f'{instance.uri_path}_new_{username}', 'comment': instance.comment, 'attribute': instance.attribute.pk if instance.attribute else '', 'is_collection': instance.is_collection, diff --git a/rdmo/questions/tests/test_viewset_questionset_multisite.py b/rdmo/questions/tests/test_viewset_questionset_multisite.py index f7789c869b..1d5f42003f 100644 --- a/rdmo/questions/tests/test_viewset_questionset_multisite.py +++ b/rdmo/questions/tests/test_viewset_questionset_multisite.py @@ -1,16 +1,16 @@ import xml.etree.ElementTree as et import pytest + from django.db.models import Max from django.urls import reverse +from ...core.tests import get_obj_perms_status_code from ...core.tests import multisite_status_map as status_map from ...core.tests import multisite_users as users -from ...core.tests import get_obj_perms_status_code - from ..models import QuestionSet +from .test_viewset_questionset import export_formats, urlnames -from .test_viewset_questionset import urlnames, export_formats @pytest.mark.parametrize('username,password', users) def test_list(db, client, username, password): @@ -77,7 +77,7 @@ def test_create(db, client, username, password): url = reverse(urlnames['list']) data = { 'uri_prefix': instance.uri_prefix, - 'uri_path': '%s_new_%s' % (instance.uri_path, username), + 'uri_path': f'{instance.uri_path}_new_{username}', 'comment': instance.comment, 'attribute': instance.attribute.pk if instance.attribute else '', 'is_collection': instance.is_collection, @@ -108,7 +108,7 @@ def test_create_page(db, client, username, password): url = reverse(urlnames['list']) data = { 'uri_prefix': instance.uri_prefix, - 'uri_path': '%s_new_%s' % (instance.uri_path, username), + 'uri_path': f'{instance.uri_path}_new_{username}', 'comment': instance.comment, 'attribute': instance.attribute.pk if instance.attribute else '', 'is_collection': instance.is_collection, @@ -128,7 +128,7 @@ def test_create_page(db, client, username, password): if response.status_code == 201: new_instance = QuestionSet.objects.get(id=response.json().get('id')) page.refresh_from_db() - assert page_questionsets + [(new_instance.id, order)] == \ + assert [*page_questionsets, (new_instance.id, order)] == \ list(page.page_questionsets.values_list('questionset', 'order')) @@ -146,7 +146,7 @@ def test_create_parent(db, client, username, password): url = reverse(urlnames['list']) data = { 'uri_prefix': instance.uri_prefix, - 'uri_path': '%s_new_%s' % (instance.uri_path, username), + 'uri_path': f'{instance.uri_path}_new_{username}', 'comment': instance.comment, 'attribute': instance.attribute.pk if instance.attribute else '', 'is_collection': instance.is_collection, @@ -166,7 +166,7 @@ def test_create_parent(db, client, username, password): if response.status_code == 201: new_instance = QuestionSet.objects.get(id=response.json().get('id')) parent.refresh_from_db() - assert parent_questionsets + [(new_instance.id, order)] == \ + assert [*parent_questionsets, (new_instance.id, order)] == \ list(parent.questionset_questionsets.values_list('questionset', 'order')) @@ -189,7 +189,7 @@ def test_create_m2m(db, client, username, password): url = reverse(urlnames['list']) data = { 'uri_prefix': instance.uri_prefix, - 'uri_path': '%s_new_%s' % (instance.uri_path, username), + 'uri_path': f'{instance.uri_path}_new_{username}', 'comment': instance.comment, 'attribute': instance.attribute.pk if instance.attribute else '', 'is_collection': instance.is_collection, diff --git a/rdmo/questions/tests/test_viewset_section.py b/rdmo/questions/tests/test_viewset_section.py index a60d068720..425c202f43 100644 --- a/rdmo/questions/tests/test_viewset_section.py +++ b/rdmo/questions/tests/test_viewset_section.py @@ -1,6 +1,7 @@ import xml.etree.ElementTree as et import pytest + from django.db.models import Max from django.urls import reverse @@ -113,7 +114,7 @@ def test_create(db, client, username, password): url = reverse(urlnames['list']) data = { 'uri_prefix': instance.uri_prefix, - 'uri_path': '%s_new_%s' % (instance.uri_path, username), + 'uri_path': f'{instance.uri_path}_new_{username}', 'comment': instance.comment, 'title_en': instance.title_lang1, 'title_de': instance.title_lang2 @@ -136,7 +137,7 @@ def test_create_catalog(db, client, username, password): url = reverse(urlnames['list']) data = { 'uri_prefix': instance.uri_prefix, - 'uri_path': '%s_new_%s' % (instance.uri_path, username), + 'uri_path': f'{instance.uri_path}_new_{username}', 'comment': instance.comment, 'title_en': instance.title_lang1, 'title_de': instance.title_lang2, @@ -148,7 +149,7 @@ def test_create_catalog(db, client, username, password): if response.status_code == 201: new_instance = Section.objects.get(id=response.json().get('id')) catalog.refresh_from_db() - assert catalog_sections + [(new_instance.id, order)] == \ + assert [*catalog_sections, (new_instance.id, order)] == \ list(catalog.catalog_sections.values_list('section', 'order')) @@ -166,7 +167,7 @@ def test_create_m2m(db, client, username, password): url = reverse(urlnames['list']) data = { 'uri_prefix': instance.uri_prefix, - 'uri_path': '%s_new_%s' % (instance.uri_path, username), + 'uri_path': f'{instance.uri_path}_new_{username}', 'comment': instance.comment, 'pages': section_pages, 'title_en': instance.title_lang1, diff --git a/rdmo/questions/tests/test_viewset_section_multisite.py b/rdmo/questions/tests/test_viewset_section_multisite.py index 85cc44dfac..fcb166fd49 100644 --- a/rdmo/questions/tests/test_viewset_section_multisite.py +++ b/rdmo/questions/tests/test_viewset_section_multisite.py @@ -1,16 +1,15 @@ import xml.etree.ElementTree as et import pytest + from django.db.models import Max from django.urls import reverse +from ...core.tests import get_obj_perms_status_code from ...core.tests import multisite_status_map as status_map from ...core.tests import multisite_users as users -from ...core.tests import get_obj_perms_status_code - from ..models import Section - -from .test_viewset_section import urlnames, export_formats +from .test_viewset_section import export_formats, urlnames @pytest.mark.parametrize('username,password', users) @@ -78,7 +77,7 @@ def test_create(db, client, username, password): url = reverse(urlnames['list']) data = { 'uri_prefix': instance.uri_prefix, - 'uri_path': '%s_new_%s' % (instance.uri_path, username), + 'uri_path': f'{instance.uri_path}_new_{username}', 'comment': instance.comment, 'title_en': instance.title_lang1, 'title_de': instance.title_lang2 @@ -101,7 +100,7 @@ def test_create_catalog(db, client, username, password): url = reverse(urlnames['list']) data = { 'uri_prefix': instance.uri_prefix, - 'uri_path': '%s_new_%s' % (instance.uri_path, username), + 'uri_path': f'{instance.uri_path}_new_{username}', 'comment': instance.comment, 'title_en': instance.title_lang1, 'title_de': instance.title_lang2, @@ -113,7 +112,7 @@ def test_create_catalog(db, client, username, password): if response.status_code == 201: new_instance = Section.objects.get(id=response.json().get('id')) catalog.refresh_from_db() - assert catalog_sections + [(new_instance.id, order)] == \ + assert [*catalog_sections, (new_instance.id, order)] == \ list(catalog.catalog_sections.values_list('section', 'order')) @@ -131,7 +130,7 @@ def test_create_m2m(db, client, username, password): url = reverse(urlnames['list']) data = { 'uri_prefix': instance.uri_prefix, - 'uri_path': '%s_new_%s' % (instance.uri_path, username), + 'uri_path': f'{instance.uri_path}_new_{username}', 'comment': instance.comment, 'pages': section_pages, 'title_en': instance.title_lang1, diff --git a/rdmo/questions/tests/test_viewset_widgettype.py b/rdmo/questions/tests/test_viewset_widgettype.py index 9ccd419a5c..df845783f4 100644 --- a/rdmo/questions/tests/test_viewset_widgettype.py +++ b/rdmo/questions/tests/test_viewset_widgettype.py @@ -1,4 +1,5 @@ import pytest + from django.urls import reverse users = ( diff --git a/rdmo/questions/urls/v1.py b/rdmo/questions/urls/v1.py index 7eaad0d6bc..d200a4fcc7 100644 --- a/rdmo/questions/urls/v1.py +++ b/rdmo/questions/urls/v1.py @@ -2,8 +2,15 @@ from rest_framework import routers -from ..viewsets import (CatalogViewSet, PageViewSet, QuestionSetViewSet, QuestionViewSet, - SectionViewSet, ValueTypeViewSet, WidgetTypeViewSet) +from ..viewsets import ( + CatalogViewSet, + PageViewSet, + QuestionSetViewSet, + QuestionViewSet, + SectionViewSet, + ValueTypeViewSet, + WidgetTypeViewSet, +) app_name = 'v1-questions' diff --git a/rdmo/questions/utils.py b/rdmo/questions/utils.py index 95f793d884..84bed4874f 100644 --- a/rdmo/questions/utils.py +++ b/rdmo/questions/utils.py @@ -1,4 +1,4 @@ -from rdmo.core.plugins import get_plugins, get_plugin +from rdmo.core.plugins import get_plugin, get_plugins def get_widgets(): diff --git a/rdmo/questions/validators.py b/rdmo/questions/validators.py index 6f6d78e339..961d205f23 100644 --- a/rdmo/questions/validators.py +++ b/rdmo/questions/validators.py @@ -1,7 +1,6 @@ from django.utils.translation import gettext_lazy as _ -from rdmo.core.validators import (InstanceValidator, LockedValidator, - UniqueURIValidator) +from rdmo.core.validators import InstanceValidator, LockedValidator, UniqueURIValidator from .models import Catalog, Page, Question, QuestionSet, Section @@ -55,9 +54,10 @@ def __call__(self, data, serializer=None): # get the original from the view when cloning an attribute obj = view.get_object() for questionset in questionsets: - if obj in [questionset] + questionset.descendants: + if obj in [questionset, *questionset.descendants]: self.raise_validation_error({ - 'questionset': [_('A question set may not be cloned to be a child of itself or one of its descendants.')] + 'questionset': [_('A question set may not be cloned to be a child of itself or one of ' + 'its descendants.')] }) if not self.instance: return @@ -67,7 +67,7 @@ def __call__(self, data, serializer=None): return for questionset in questionsets: - if self.instance in [questionset] + questionset.descendants: + if self.instance in [questionset, *questionset.descendants]: self.raise_validation_error({ 'questionsets': [_('A question set may not be a child of itself or one of its descendants.')] }) diff --git a/rdmo/questions/viewsets.py b/rdmo/questions/viewsets.py index 4c18aa608d..4184669d42 100644 --- a/rdmo/questions/viewsets.py +++ b/rdmo/questions/viewsets.py @@ -1,11 +1,13 @@ from django.db import models -from django_filters.rest_framework import DjangoFilterBackend + from rest_framework.decorators import action from rest_framework.filters import SearchFilter from rest_framework.permissions import IsAuthenticated from rest_framework.response import Response from rest_framework.viewsets import ModelViewSet +from django_filters.rest_framework import DjangoFilterBackend + from rdmo.core.constants import VALUE_TYPE_CHOICES from rdmo.core.exports import XMLResponse from rdmo.core.permissions import HasModelPermission, HasObjectPermission @@ -13,20 +15,30 @@ from rdmo.core.views import ChoicesViewSet from .models import Catalog, Page, Question, QuestionSet, Section -from .renderers import (CatalogRenderer, PageRenderer, QuestionRenderer, - QuestionSetRenderer, SectionRenderer) -from .serializers.export import (CatalogExportSerializer, PageExportSerializer, - QuestionExportSerializer, - QuestionSetExportSerializer, - SectionExportSerializer) -from .serializers.v1 import (CatalogIndexSerializer, CatalogNestedSerializer, - CatalogSerializer, PageIndexSerializer, - PageNestedSerializer, PageSerializer, - QuestionIndexSerializer, QuestionSerializer, - QuestionSetIndexSerializer, - QuestionSetNestedSerializer, - QuestionSetSerializer, SectionIndexSerializer, - SectionNestedSerializer, SectionSerializer) +from .renderers import CatalogRenderer, PageRenderer, QuestionRenderer, QuestionSetRenderer, SectionRenderer +from .serializers.export import ( + CatalogExportSerializer, + PageExportSerializer, + QuestionExportSerializer, + QuestionSetExportSerializer, + SectionExportSerializer, +) +from .serializers.v1 import ( + CatalogIndexSerializer, + CatalogNestedSerializer, + CatalogSerializer, + PageIndexSerializer, + PageNestedSerializer, + PageSerializer, + QuestionIndexSerializer, + QuestionSerializer, + QuestionSetIndexSerializer, + QuestionSetNestedSerializer, + QuestionSetSerializer, + SectionIndexSerializer, + SectionNestedSerializer, + SectionSerializer, +) from .utils import get_widget_type_choices @@ -70,9 +82,11 @@ def export(self, request, export_format='xml'): xml = CatalogRenderer().render(serializer.data, context=self.get_export_renderer_context(request)) return XMLResponse(xml, name='catalogs') else: - return render_to_format(self.request, export_format, 'questions', 'questions/export/catalogs.html', { - 'catalogs': queryset - }) + return render_to_format( + self.request, export_format, 'questions', 'questions/export/catalogs.html', { + 'catalogs': queryset + } + ) @action(detail=True, url_path='export(/(?P[a-z]+))?') def detail_export(self, request, pk=None, export_format='xml'): @@ -81,9 +95,11 @@ def detail_export(self, request, pk=None, export_format='xml'): xml = CatalogRenderer().render([serializer.data], context=self.get_export_renderer_context(request)) return XMLResponse(xml, name=self.get_object().uri_path) else: - return render_to_format(self.request, export_format, self.get_object().uri_path, 'questions/export/catalogs.html', { - 'catalogs': [self.get_object()] - }) + return render_to_format( + self.request, export_format, self.get_object().uri_path, 'questions/export/catalogs.html', { + 'catalogs': [self.get_object()] + } + ) def get_export_renderer_context(self, request): full = is_truthy(request.GET.get('full')) @@ -138,9 +154,11 @@ def export(self, request, export_format='xml'): xml = SectionRenderer().render(serializer.data, context=self.get_export_renderer_context(request)) return XMLResponse(xml, name='sections') else: - return render_to_format(self.request, export_format, 'questions', 'questions/export/sections.html', { - 'sections': queryset - }) + return render_to_format( + self.request, export_format, 'questions', 'questions/export/sections.html', { + 'sections': queryset + } + ) @action(detail=True, url_path='export(/(?P[a-z]+))?') def detail_export(self, request, pk=None, export_format='xml'): @@ -149,9 +167,11 @@ def detail_export(self, request, pk=None, export_format='xml'): xml = SectionRenderer().render([serializer.data], context=self.get_export_renderer_context(request)) return XMLResponse(xml, name=self.get_object().uri_path) else: - return render_to_format(self.request, export_format, self.get_object().uri_path, 'questions/export/sections.html', { - 'sections': [self.get_object()] - }) + return render_to_format( + self.request, export_format, self.get_object().uri_path, 'questions/export/sections.html', { + 'sections': [self.get_object()] + } + ) def get_export_renderer_context(self, request): full = is_truthy(request.GET.get('full')) @@ -213,9 +233,11 @@ def export(self, request, export_format='xml'): xml = PageRenderer().render(serializer.data, context=self.get_export_renderer_context(request)) return XMLResponse(xml, name='pages') else: - return render_to_format(self.request, export_format, 'questions', 'questions/export/pages.html', { - 'pages': queryset - }) + return render_to_format( + self.request, export_format, 'questions', 'questions/export/pages.html', { + 'pages': queryset + } + ) @action(detail=True, url_path='export(/(?P[a-z]+))?') def detail_export(self, request, pk=None, export_format='xml'): @@ -224,9 +246,11 @@ def detail_export(self, request, pk=None, export_format='xml'): xml = PageRenderer().render([serializer.data], context=self.get_export_renderer_context(request)) return XMLResponse(xml, name=self.get_object().uri_path) else: - return render_to_format(self.request, export_format, self.get_object().uri_path, 'questions/export/pages.html', { - 'pages': [self.get_object()] - }) + return render_to_format( + self.request, export_format, self.get_object().uri_path, 'questions/export/pages.html', { + 'pages': [self.get_object()] + } + ) def get_export_renderer_context(self, request): full = is_truthy(request.GET.get('full')) @@ -288,9 +312,11 @@ def export(self, request, export_format='xml'): xml = QuestionSetRenderer().render(serializer.data, context=self.get_export_renderer_context(request)) return XMLResponse(xml, name='questionsets') else: - return render_to_format(self.request, export_format, 'questionsets', 'questions/export/questionsets.html', { - 'questionsets': queryset - }) + return render_to_format( + self.request, export_format, 'questionsets', 'questions/export/questionsets.html', { + 'questionsets': queryset + } + ) @action(detail=True, url_path='export(/(?P[a-z]+))?') def detail_export(self, request, pk=None, export_format='xml'): @@ -299,9 +325,11 @@ def detail_export(self, request, pk=None, export_format='xml'): xml = QuestionSetRenderer().render([serializer.data], context=self.get_export_renderer_context(request)) return XMLResponse(xml, name=self.get_object().uri_path) else: - return render_to_format(self.request, export_format, self.get_object().uri_path, 'questions/export/questionsets.html', { - 'questionsets': [self.get_object()] - }) + return render_to_format( + self.request, export_format, self.get_object().uri_path, 'questions/export/questionsets.html', { + 'questionsets': [self.get_object()] + } + ) def get_export_renderer_context(self, request): full = is_truthy(request.GET.get('full')) @@ -360,9 +388,11 @@ def export(self, request, export_format='xml'): xml = QuestionRenderer().render(serializer.data, context=self.get_export_renderer_context(request)) return XMLResponse(xml, name='questions') else: - return render_to_format(self.request, export_format, 'questions', 'questions/export/questions.html', { - 'questions': queryset - }) + return render_to_format( + self.request, export_format, 'questions', 'questions/export/questions.html', { + 'questions': queryset + } + ) @action(detail=True, url_path='export(/(?P[a-z]+))?') def detail_export(self, request, pk=None, export_format='xml'): @@ -371,9 +401,11 @@ def detail_export(self, request, pk=None, export_format='xml'): xml = QuestionRenderer().render([serializer.data], context=self.get_export_renderer_context(request)) return XMLResponse(xml, name=self.get_object().uri_path) else: - return render_to_format(self.request, export_format, self.get_object().uri_path, 'questions/export/questions.html', { - 'questions': [self.get_object()] - }) + return render_to_format( + self.request, export_format, self.get_object().uri_path, 'questions/export/questions.html', { + 'questions': [self.get_object()] + } + ) def get_export_renderer_context(self, request): full = is_truthy(request.GET.get('full')) diff --git a/rdmo/services/providers.py b/rdmo/services/providers.py index 1edb70e360..41b6530224 100644 --- a/rdmo/services/providers.py +++ b/rdmo/services/providers.py @@ -1,22 +1,19 @@ -import hmac -import json import logging -from urllib.parse import quote, urlencode +from urllib.parse import urlencode -import requests from django.conf import settings -from django.core.exceptions import ObjectDoesNotExist -from django.http import Http404, HttpResponse, HttpResponseRedirect +from django.http import HttpResponseRedirect from django.shortcuts import render from django.urls import reverse from django.utils.crypto import get_random_string from django.utils.translation import gettext_lazy as _ -from rdmo.core.plugins import Plugin + +import requests logger = logging.getLogger(__name__) -class OauthProviderMixin(object): +class OauthProviderMixin: def get(self, request, url): # get access token from the session @@ -128,7 +125,7 @@ def post_success(self, request, response): raise NotImplementedError def get_session_key(self, key): - return '{}.{}'.format(self.class_name, key) + return f'{self.class_name}.{key}' def store_in_session(self, request, key, data): session_key = self.get_session_key(key) @@ -143,7 +140,7 @@ def pop_from_session(self, request, key): return request.session.pop(session_key, None) def get_authorization_headers(self, access_token): - return {'Authorization': 'Bearer {}'.format(access_token)} + return {'Authorization': f'Bearer {access_token}'} def get_authorize_params(self, request, state): raise NotImplementedError @@ -183,7 +180,7 @@ def redirect_path(self): def get_authorization_headers(self, access_token): return { - 'Authorization': 'token {}'.format(access_token), + 'Authorization': f'token {access_token}', 'Accept': 'application/vnd.github.v3+json' } @@ -213,15 +210,15 @@ def gitlab_url(self): @property def authorize_url(self): - return '{}/oauth/authorize'.format(self.gitlab_url) + return f'{self.gitlab_url}/oauth/authorize' @property def token_url(self): - return '{}/oauth/token'.format(self.gitlab_url) + return f'{self.gitlab_url}/oauth/token' @property def api_url(self): - return '{}/api/v4'.format(self.gitlab_url) + return f'{self.gitlab_url}/api/v4' @property def client_id(self): diff --git a/rdmo/services/validators.py b/rdmo/services/validators.py index d161e3506e..b6f02f68f0 100644 --- a/rdmo/services/validators.py +++ b/rdmo/services/validators.py @@ -3,7 +3,7 @@ from rdmo.core.plugins import get_plugin -class ProviderValidator(): +class ProviderValidator: def __call__(self, data): provider_key = data.get('provider_key') @@ -15,15 +15,15 @@ def __call__(self, data): try: options = {option.get('key'): option.get('value') for option in data.get('options', [])} - except KeyError: + except KeyError as e: raise ValidationError({ 'options': 'Options need to be of the form "{"key": "": "value": ""}".' - }) + }) from e for key in options: if key not in [field.get('key') for field in provider.fields]: raise ValidationError({ - 'options': 'Key "{}" is not valid.'.format(key) + 'options': f'Key "{key}" is not valid.' }) for field in provider.fields: diff --git a/rdmo/tasks/admin.py b/rdmo/tasks/admin.py index b241070485..bb2c85c429 100644 --- a/rdmo/tasks/admin.py +++ b/rdmo/tasks/admin.py @@ -22,7 +22,7 @@ def clean(self): class TaskAdmin(admin.ModelAdmin): form = TaskAdminForm - search_fields = ['uri'] + get_language_fields('title') + get_language_fields('text') + search_fields = ['uri', *get_language_fields('title'), *get_language_fields('text')] list_display = ('uri', 'title', 'text', 'available') readonly_fields = ('uri', ) list_filter = ('available', ) diff --git a/rdmo/tasks/imports.py b/rdmo/tasks/imports.py index a6a14fdee4..7631d1be44 100644 --- a/rdmo/tasks/imports.py +++ b/rdmo/tasks/imports.py @@ -2,9 +2,14 @@ from django.contrib.sites.models import Site -from rdmo.core.imports import (check_permissions, set_common_fields, - set_foreign_field, set_lang_field, - set_m2m_instances, validate_instance) +from rdmo.core.imports import ( + check_permissions, + set_common_fields, + set_foreign_field, + set_lang_field, + set_m2m_instances, + validate_instance, +) from .models import Task from .validators import TaskLockedValidator, TaskUniqueURIValidator diff --git a/rdmo/tasks/managers.py b/rdmo/tasks/managers.py index ee446e401f..0251eab9f0 100644 --- a/rdmo/tasks/managers.py +++ b/rdmo/tasks/managers.py @@ -1,10 +1,13 @@ from django.db import models -from rdmo.core.managers import (AvailabilityManagerMixin, - AvailabilityQuerySetMixin, - CurrentSiteManagerMixin, - CurrentSiteQuerySetMixin, GroupsManagerMixin, - GroupsQuerySetMixin) +from rdmo.core.managers import ( + AvailabilityManagerMixin, + AvailabilityQuerySetMixin, + CurrentSiteManagerMixin, + CurrentSiteQuerySetMixin, + GroupsManagerMixin, + GroupsQuerySetMixin, +) class TaskQuestionSet(CurrentSiteQuerySetMixin, GroupsQuerySetMixin, AvailabilityQuerySetMixin, models.QuerySet): diff --git a/rdmo/tasks/models.py b/rdmo/tasks/models.py index 179badfb17..6edc0c16b9 100644 --- a/rdmo/tasks/models.py +++ b/rdmo/tasks/models.py @@ -3,9 +3,10 @@ from django.contrib.sites.models import Site from django.db import models from django.utils.translation import gettext_lazy as _ + from rdmo.conditions.models import Condition from rdmo.core.models import TranslationMixin -from rdmo.core.utils import copy_model, join_url +from rdmo.core.utils import join_url from rdmo.domain.models import Attribute from rdmo.questions.models import Catalog @@ -44,7 +45,8 @@ class Task(TranslationMixin, models.Model): catalogs = models.ManyToManyField( Catalog, blank=True, verbose_name=_('Catalogs'), - help_text=_('The catalogs this task can be used with. An empty list implies that this task can be used with every catalog.') + help_text=_('The catalogs this task can be used with. ' + 'An empty list implies that this task can be used with every catalog.') ) sites = models.ManyToManyField( Site, blank=True, @@ -119,7 +121,8 @@ class Task(TranslationMixin, models.Model): end_attribute = models.ForeignKey( Attribute, blank=True, null=True, on_delete=models.SET_NULL, related_name='tasks_as_end', verbose_name=_('End date attribute'), - help_text=_('The attribute that is setting the end date for this task (optional, if no end date attribute is given, the start date attribute sets also the end date).') + help_text=_('The attribute that is setting the end date for this task ' + '(optional, if no end date attribute is given, the start date attribute sets also the end date).') ) days_before = models.IntegerField( blank=True, null=True, diff --git a/rdmo/tasks/serializers/v1.py b/rdmo/tasks/serializers/v1.py index 8940268c2b..cebd38a415 100644 --- a/rdmo/tasks/serializers/v1.py +++ b/rdmo/tasks/serializers/v1.py @@ -1,9 +1,11 @@ from rest_framework import serializers -from rdmo.core.serializers import (ElementModelSerializerMixin, - ElementWarningSerializerMixin, - ReadOnlyObjectPermissionSerializerMixin, - TranslationSerializerMixin) +from rdmo.core.serializers import ( + ElementModelSerializerMixin, + ElementWarningSerializerMixin, + ReadOnlyObjectPermissionSerializerMixin, + TranslationSerializerMixin, +) from ..models import Task from ..validators import TaskLockedValidator, TaskUniqueURIValidator diff --git a/rdmo/tasks/tests/test_validator_locked.py b/rdmo/tasks/tests/test_validator_locked.py index f6722589b5..45e1c16039 100644 --- a/rdmo/tasks/tests/test_validator_locked.py +++ b/rdmo/tasks/tests/test_validator_locked.py @@ -1,7 +1,8 @@ import pytest + from django.core.exceptions import ValidationError -from rest_framework.exceptions import \ - ValidationError as RestFameworkValidationError + +from rest_framework.exceptions import ValidationError as RestFameworkValidationError from ..models import Task from ..serializers.v1 import TaskSerializer diff --git a/rdmo/tasks/tests/test_validator_unique_uri.py b/rdmo/tasks/tests/test_validator_unique_uri.py index 0efd84da3b..14db7d394c 100644 --- a/rdmo/tasks/tests/test_validator_unique_uri.py +++ b/rdmo/tasks/tests/test_validator_unique_uri.py @@ -1,8 +1,9 @@ import pytest + from django.conf import settings from django.core.exceptions import ValidationError -from rest_framework.exceptions import \ - ValidationError as RestFameworkValidationError + +from rest_framework.exceptions import ValidationError as RestFameworkValidationError from ..models import Task from ..serializers.v1 import TaskSerializer diff --git a/rdmo/tasks/tests/test_viewset_task.py b/rdmo/tasks/tests/test_viewset_task.py index 2e3a35db97..92f31dc9c0 100644 --- a/rdmo/tasks/tests/test_viewset_task.py +++ b/rdmo/tasks/tests/test_viewset_task.py @@ -1,6 +1,7 @@ import xml.etree.ElementTree as et import pytest + from django.urls import reverse from ..models import Task @@ -97,7 +98,7 @@ def test_create(db, client, username, password): url = reverse(urlnames['list']) data = { 'uri_prefix': instance.uri_prefix, - 'uri_path': '%s_new_%s' % (instance.uri_path, username), + 'uri_path': f'{instance.uri_path}_new_{username}', 'comment': instance.comment, 'title_en': instance.title_lang1, 'title_de': instance.title_lang2, diff --git a/rdmo/tasks/tests/test_viewset_task_multisite.py b/rdmo/tasks/tests/test_viewset_task_multisite.py index 98ecb8a823..2a1ee40f60 100644 --- a/rdmo/tasks/tests/test_viewset_task_multisite.py +++ b/rdmo/tasks/tests/test_viewset_task_multisite.py @@ -1,16 +1,14 @@ import xml.etree.ElementTree as et import pytest + from django.urls import reverse +from ...core.tests import get_obj_perms_status_code from ...core.tests import multisite_status_map as status_map from ...core.tests import multisite_users as users -from ...core.tests import get_obj_perms_status_code - from ..models import Task - -from .test_viewset_task import export_formats -from .test_viewset_task import urlnames +from .test_viewset_task import export_formats, urlnames @pytest.mark.parametrize('username,password', users) @@ -67,7 +65,7 @@ def test_create(db, client, username, password): url = reverse(urlnames['list']) data = { 'uri_prefix': instance.uri_prefix, - 'uri_path': '%s_new_%s' % (instance.uri_path, username), + 'uri_path': f'{instance.uri_path}_new_{username}', 'comment': instance.comment, 'title_en': instance.title_lang1, 'title_de': instance.title_lang2, diff --git a/rdmo/tasks/viewsets.py b/rdmo/tasks/viewsets.py index 2d99061913..c07f90d1b0 100644 --- a/rdmo/tasks/viewsets.py +++ b/rdmo/tasks/viewsets.py @@ -1,10 +1,12 @@ from django.db import models -from django_filters.rest_framework import DjangoFilterBackend + from rest_framework.decorators import action from rest_framework.filters import SearchFilter from rest_framework.response import Response from rest_framework.viewsets import ModelViewSet +from django_filters.rest_framework import DjangoFilterBackend + from rdmo.core.exports import XMLResponse from rdmo.core.permissions import HasModelPermission, HasObjectPermission from rdmo.core.utils import is_truthy, render_to_format @@ -59,9 +61,11 @@ def detail_export(self, request, pk=None, export_format='xml'): xml = TaskRenderer().render([serializer.data], context=self.get_export_renderer_context(request)) return XMLResponse(xml, name=self.get_object().uri_path) else: - return render_to_format(self.request, export_format, self.get_object().uri_path, 'tasks/export/tasks.html', { - 'tasks': [self.get_object()] - }) + return render_to_format( + self.request, export_format, self.get_object().uri_path, 'tasks/export/tasks.html', { + 'tasks': [self.get_object()] + } + ) def get_export_renderer_context(self, request): full = is_truthy(request.GET.get('full')) diff --git a/rdmo/views/admin.py b/rdmo/views/admin.py index ba4c17711c..1cfebec9af 100644 --- a/rdmo/views/admin.py +++ b/rdmo/views/admin.py @@ -22,7 +22,7 @@ def clean(self): class ViewAdmin(admin.ModelAdmin): form = ViewAdminForm - search_fields = ['uri'] + get_language_fields('title') + get_language_fields('help') + search_fields = ['uri', *get_language_fields('title'), *get_language_fields('help')] list_display = ('uri', 'title', 'help', 'available') readonly_fields = ('uri', ) list_filter = ('available', ) diff --git a/rdmo/views/imports.py b/rdmo/views/imports.py index e811625086..7c09061035 100644 --- a/rdmo/views/imports.py +++ b/rdmo/views/imports.py @@ -2,9 +2,7 @@ from django.contrib.sites.models import Site -from rdmo.core.imports import (check_permissions, set_common_fields, - set_lang_field, set_m2m_instances, - validate_instance) +from rdmo.core.imports import check_permissions, set_common_fields, set_lang_field, set_m2m_instances, validate_instance from .models import View from .validators import ViewLockedValidator, ViewUniqueURIValidator diff --git a/rdmo/views/managers.py b/rdmo/views/managers.py index 73f0604c44..4c584e4839 100644 --- a/rdmo/views/managers.py +++ b/rdmo/views/managers.py @@ -1,10 +1,13 @@ from django.db import models -from rdmo.core.managers import (AvailabilityManagerMixin, - AvailabilityQuerySetMixin, - CurrentSiteManagerMixin, - CurrentSiteQuerySetMixin, GroupsManagerMixin, - GroupsQuerySetMixin) +from rdmo.core.managers import ( + AvailabilityManagerMixin, + AvailabilityQuerySetMixin, + CurrentSiteManagerMixin, + CurrentSiteQuerySetMixin, + GroupsManagerMixin, + GroupsQuerySetMixin, +) class ViewQuestionSet(CurrentSiteQuerySetMixin, GroupsQuerySetMixin, AvailabilityQuerySetMixin, models.QuerySet): diff --git a/rdmo/views/models.py b/rdmo/views/models.py index 224c756b3c..510f502659 100644 --- a/rdmo/views/models.py +++ b/rdmo/views/models.py @@ -7,7 +7,7 @@ from rdmo import __version__ from rdmo.core.models import TranslationMixin -from rdmo.core.utils import copy_model, get_pandoc_main_version, join_url +from rdmo.core.utils import get_pandoc_main_version, join_url from rdmo.questions.models import Catalog from .managers import ViewManager @@ -46,7 +46,8 @@ class View(models.Model, TranslationMixin): catalogs = models.ManyToManyField( Catalog, blank=True, verbose_name=_('Catalogs'), - help_text=_('The catalogs this view can be used with. An empty list implies that this view can be used with every catalog.') + help_text=_('The catalogs this view can be used with. ' + 'An empty list implies that this view can be used with every catalog.') ) sites = models.ManyToManyField( Site, blank=True, diff --git a/rdmo/views/serializers/v1.py b/rdmo/views/serializers/v1.py index a7e27a3d1b..5862537a29 100644 --- a/rdmo/views/serializers/v1.py +++ b/rdmo/views/serializers/v1.py @@ -1,10 +1,13 @@ from django.template import Context, Template, TemplateSyntaxError + from rest_framework import exceptions, serializers -from rdmo.core.serializers import (ElementModelSerializerMixin, - ElementWarningSerializerMixin, - ReadOnlyObjectPermissionSerializerMixin, - TranslationSerializerMixin) +from rdmo.core.serializers import ( + ElementModelSerializerMixin, + ElementWarningSerializerMixin, + ReadOnlyObjectPermissionSerializerMixin, + TranslationSerializerMixin, +) from ..models import View from ..validators import ViewLockedValidator, ViewUniqueURIValidator @@ -29,7 +32,7 @@ def validate(self, data): except (KeyError, IndexError): pass except (TemplateSyntaxError, TypeError) as e: - raise exceptions.ValidationError({'template': '\n'.join(e.args)}) + raise exceptions.ValidationError({'template': '\n'.join(e.args)}) from e return super().validate(data) diff --git a/rdmo/views/templatetags/view_tags.py b/rdmo/views/templatetags/view_tags.py index 9373cf0a0b..44f7b13319 100644 --- a/rdmo/views/templatetags/view_tags.py +++ b/rdmo/views/templatetags/view_tags.py @@ -1,9 +1,9 @@ from django import template from django.utils.translation import gettext_lazy as _ -from rdmo.core.constants import (VALUE_TYPE_DATETIME, VALUE_TYPE_INTEGER, - VALUE_TYPE_TEXT) +from rdmo.core.constants import VALUE_TYPE_DATETIME, VALUE_TYPE_INTEGER, VALUE_TYPE_TEXT from rdmo.projects.models import Value + register = template.Library() @@ -45,7 +45,8 @@ def get_numbers(context, attribute, set_prefix='*', set_index='*', index='*', pr @register.simple_tag(takes_context=True) def get_value(context, attribute, set_prefix='', set_index=0, index=0, project=None): try: - return get_values(context, attribute, set_prefix=set_prefix, set_index=set_index, index=index, project=project)[0] + return get_values(context, attribute, set_prefix=set_prefix, set_index=set_index, + index=index, project=project)[0] except IndexError: return None @@ -69,7 +70,8 @@ def get_set_values(context, set, attribute, set_prefix='', project=None): def get_set_value(context, set, attribute, set_prefix='', index=0, project=None): try: set_index = set.get('set_index') - return get_values(context, attribute, set_prefix=set_prefix, set_index=set_index, index=index, project=project)[0] + return get_values(context, attribute, set_prefix=set_prefix, set_index=set_index, + index=index, project=project)[0] except IndexError: return None @@ -77,7 +79,7 @@ def get_set_value(context, set, attribute, set_prefix='', index=0, project=None) @register.simple_tag(takes_context=True) def get_set_prefixes(context, attribute, project=None): try: - return sorted(set(map(lambda value: value['set_prefix'], get_values(context, attribute, project=project)))) + return sorted({value['set_prefix'] for value in get_values(context, attribute, project=project)}) except IndexError: return None @@ -85,7 +87,9 @@ def get_set_prefixes(context, attribute, project=None): @register.simple_tag(takes_context=True) def get_set_indexes(context, attribute, set_prefix='', project=None): try: - return sorted(set(map(lambda value: value['set_index'], get_values(context, attribute, set_prefix=set_prefix, project=project)))) + return sorted({ + value['set_index'] for value in get_values(context, attribute, set_prefix=set_prefix, project=project) + }) except IndexError: return None @@ -109,7 +113,8 @@ def get_set(context, attribute, set_prefix='', project=None): @register.inclusion_tag('views/tags/value.html', takes_context=True) def render_value(context, attribute, set_prefix='', set_index=0, index=0, project=None): - context['value'] = get_value(context, attribute, set_prefix=set_prefix, set_index=set_index, index=index, project=project) + context['value'] = get_value(context, attribute, set_prefix=set_prefix, set_index=set_index, + index=index, project=project) return context @@ -167,11 +172,12 @@ def get_labels(context, element, set_prefix='', set_index=0, project=None): set_labels = [] for ancestor in element['ancestors']: if ancestor['is_collection']: - set_label = '#{}'.format(set_index + 1) + set_label = f'#{set_index + 1}' if ancestor['attribute']: # get attribute value - value = get_value(context, ancestor['attribute'], set_prefix=set_prefix, set_index=set_index, index=0, project=project) + value = get_value(context, ancestor['attribute'], set_prefix=set_prefix, set_index=set_index, + index=0, project=project) if value: set_label = '"{}"'.format(value['value']) diff --git a/rdmo/views/tests/test_validator_locked.py b/rdmo/views/tests/test_validator_locked.py index 51d1612136..d3091ed12d 100644 --- a/rdmo/views/tests/test_validator_locked.py +++ b/rdmo/views/tests/test_validator_locked.py @@ -1,7 +1,8 @@ import pytest + from django.core.exceptions import ValidationError -from rest_framework.exceptions import \ - ValidationError as RestFameworkValidationError + +from rest_framework.exceptions import ValidationError as RestFameworkValidationError from ..models import View from ..serializers.v1 import ViewSerializer diff --git a/rdmo/views/tests/test_validator_unique_uri.py b/rdmo/views/tests/test_validator_unique_uri.py index aa4ce39225..fbf44d9346 100644 --- a/rdmo/views/tests/test_validator_unique_uri.py +++ b/rdmo/views/tests/test_validator_unique_uri.py @@ -1,8 +1,9 @@ import pytest + from django.conf import settings from django.core.exceptions import ValidationError -from rest_framework.exceptions import \ - ValidationError as RestFameworkValidationError + +from rest_framework.exceptions import ValidationError as RestFameworkValidationError from ..models import View from ..serializers.v1 import ViewSerializer diff --git a/rdmo/views/tests/test_view_tags.py b/rdmo/views/tests/test_view_tags.py index 96ef380af0..6ff168b370 100644 --- a/rdmo/views/tests/test_view_tags.py +++ b/rdmo/views/tests/test_view_tags.py @@ -1,9 +1,9 @@ import pytest + from django.template import Context from rdmo.projects.models import Project -from rdmo.views.templatetags.view_tags import (get_set_value, get_set_values, - get_sets, get_value, get_values) +from rdmo.views.templatetags.view_tags import get_set_value, get_set_values, get_sets, get_value, get_values from rdmo.views.utils import ProjectWrapper project_pk = 1 @@ -22,7 +22,9 @@ def context(db): @pytest.fixture def values(db): - return Project.objects.get(pk=project_pk).values.filter(snapshot=None).order_by('set_index').order_by('collection_index') + return Project.objects.get(pk=project_pk).values.filter(snapshot=None) \ + .order_by('set_index') \ + .order_by('collection_index') def test_get_value_project_title(context): @@ -81,10 +83,12 @@ def test_get_value(context, values): def test_get_set_values(context, values): path = 'set/single/text' for value_set in get_sets(context, 'set'): - assertListEqual(get_set_values(context, value_set, path), values.filter(attribute__path=path, set_index=value_set['set_index'])) + assertListEqual(get_set_values(context, value_set, path), + values.filter(attribute__path=path, set_index=value_set['set_index'])) def test_get_set_value(context, values): path = 'set/single/text' for value_set in get_sets(context, 'set'): - assert get_set_value(context, value_set, path)['id'] == values.filter(attribute__path=path, set_index=value_set['set_index']).first().id + assert get_set_value(context, value_set, path)['id'] == \ + values.filter(attribute__path=path, set_index=value_set['set_index']).first().id diff --git a/rdmo/views/tests/test_viewset_view.py b/rdmo/views/tests/test_viewset_view.py index 310afb4662..64079c9a3e 100644 --- a/rdmo/views/tests/test_viewset_view.py +++ b/rdmo/views/tests/test_viewset_view.py @@ -1,6 +1,7 @@ import xml.etree.ElementTree as et import pytest + from django.urls import reverse from ..models import View @@ -97,7 +98,7 @@ def test_create(db, client, username, password): url = reverse(urlnames['list']) data = { 'uri_prefix': instance.uri_prefix, - 'uri_path': '%s_new_%s' % (instance.uri_path, username), + 'uri_path': f'{instance.uri_path}_new_{username}', 'comment': instance.comment, 'template': instance.template, 'title_en': instance.title_lang1, diff --git a/rdmo/views/tests/test_viewset_view_multisite.py b/rdmo/views/tests/test_viewset_view_multisite.py index fa84517ec9..31be9c2fbe 100644 --- a/rdmo/views/tests/test_viewset_view_multisite.py +++ b/rdmo/views/tests/test_viewset_view_multisite.py @@ -1,16 +1,14 @@ import xml.etree.ElementTree as et import pytest + from django.urls import reverse +from ...core.tests import get_obj_perms_status_code from ...core.tests import multisite_status_map as status_map from ...core.tests import multisite_users as users -from ...core.tests import get_obj_perms_status_code - from ..models import View - -from .test_viewset_view import export_formats -from .test_viewset_view import urlnames +from .test_viewset_view import export_formats, urlnames @pytest.mark.parametrize('username,password', users) @@ -67,7 +65,7 @@ def test_create(db, client, username, password): url = reverse(urlnames['list']) data = { 'uri_prefix': instance.uri_prefix, - 'uri_path': '%s_new_%s' % (instance.uri_path, username), + 'uri_path': f'{instance.uri_path}_new_{username}', 'comment': instance.comment, 'template': instance.template, 'title_en': instance.title_lang1, diff --git a/rdmo/views/utils.py b/rdmo/views/utils.py index cda712d1fb..76b710de7c 100644 --- a/rdmo/views/utils.py +++ b/rdmo/views/utils.py @@ -6,7 +6,7 @@ from mptt.utils import get_cached_trees -class ProjectWrapper(object): +class ProjectWrapper: def __init__(self, project, snapshot=None): self._project = project @@ -113,7 +113,7 @@ def _get_values(self, attribute, set_prefix='*', set_index='*', index='*'): if index != '*': values = filter(lambda value: value.collection_index == index, values) - return list(map(lambda value: value.as_dict, values)) + return [value.as_dict for value in values] def _check_element(self, element, set_prefix=None, set_index=None): conditions = set(filter(lambda condition: condition.uri in element['conditions'], self._conditions)) diff --git a/rdmo/views/viewsets.py b/rdmo/views/viewsets.py index 0e6ba73288..7d26002e0e 100644 --- a/rdmo/views/viewsets.py +++ b/rdmo/views/viewsets.py @@ -1,10 +1,12 @@ from django.db import models -from django_filters.rest_framework import DjangoFilterBackend + from rest_framework.decorators import action from rest_framework.filters import SearchFilter from rest_framework.response import Response from rest_framework.viewsets import ModelViewSet +from django_filters.rest_framework import DjangoFilterBackend + from rdmo.core.exports import XMLResponse from rdmo.core.permissions import HasModelPermission, HasObjectPermission from rdmo.core.utils import render_to_format @@ -58,6 +60,8 @@ def detail_export(self, request, pk=None, export_format='xml'): xml = ViewRenderer().render([serializer.data]) return XMLResponse(xml, name=self.get_object().uri_path) else: - return render_to_format(self.request, export_format, self.get_object().uri_path, 'views/export/views.html', { - 'views': [self.get_object()] - }) + return render_to_format( + self.request, export_format, self.get_object().uri_path, 'views/export/views.html', { + 'views': [self.get_object()] + } + ) diff --git a/testing/config/settings/__init__.py b/testing/config/settings/__init__.py index f52a1cc4f5..b0e6094d54 100644 --- a/testing/config/settings/__init__.py +++ b/testing/config/settings/__init__.py @@ -2,27 +2,3 @@ from .base import * from .local import * - -# add static and templates from local.THEME_DIR to STATICFILES_DIRS and TEMPLATES -try: - STATICFILES_DIRS = [ - os.path.join(THEME_DIR, 'static/') - ] - TEMPLATES[0]['DIRS'].append(os.path.join(THEME_DIR, 'templates/')) -except NameError: - pass - -# prepend the local.BASE_URL to the different URL settings -try: - LOGIN_URL = BASE_URL + LOGIN_URL - LOGIN_REDIRECT_URL = BASE_URL + LOGIN_REDIRECT_URL - LOGOUT_URL = BASE_URL + LOGOUT_URL - ACCOUNT_LOGOUT_REDIRECT_URL = BASE_URL - MEDIA_URL = BASE_URL + MEDIA_URL - STATIC_URL = BASE_URL + STATIC_URL - - CSRF_COOKIE_PATH = BASE_URL + '/' - LANGUAGE_COOKIE_PATH = BASE_URL + '/' - SESSION_COOKIE_PATH = BASE_URL + '/' -except NameError: - pass diff --git a/testing/config/urls.py b/testing/config/urls.py index e7a4a453bb..2b8747a97a 100644 --- a/testing/config/urls.py +++ b/testing/config/urls.py @@ -1,5 +1,6 @@ from django.contrib import admin from django.urls import include, path + from rdmo.core.views import about, home urlpatterns = [ diff --git a/testing/fixtures/groups.json b/testing/fixtures/groups.json index 81187dcba4..66df260b5d 100644 --- a/testing/fixtures/groups.json +++ b/testing/fixtures/groups.json @@ -31,4 +31,4 @@ "permissions": [] } } -] \ No newline at end of file +] diff --git a/testing/fixtures/overlays.json b/testing/fixtures/overlays.json index c8e843e1a3..6a64b39281 100644 --- a/testing/fixtures/overlays.json +++ b/testing/fixtures/overlays.json @@ -19,4 +19,4 @@ "current": "create-project" } } -] \ No newline at end of file +] diff --git a/testing/fixtures/projects.json b/testing/fixtures/projects.json index 7ac5ec8ae5..791f1a7493 100644 --- a/testing/fixtures/projects.json +++ b/testing/fixtures/projects.json @@ -5768,4 +5768,4 @@ "timestamp": "2023-07-10T14:06:55.112Z" } } -] \ No newline at end of file +] diff --git a/testing/fixtures/users.json b/testing/fixtures/users.json index 6776a47a2b..960be64813 100644 --- a/testing/fixtures/users.json +++ b/testing/fixtures/users.json @@ -413,4 +413,4 @@ "user_permissions": [] } } -] \ No newline at end of file +] diff --git a/testing/import/catalogs.json b/testing/import/catalogs.json index a018cd5adb..f5af5985c4 100644 --- a/testing/import/catalogs.json +++ b/testing/import/catalogs.json @@ -5393,4 +5393,4 @@ "help_de": null, "sections": null } -] \ No newline at end of file +]