diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml index c82796b381..4baf12caaf 100644 --- a/.github/workflows/pytest.yml +++ b/.github/workflows/pytest.yml @@ -120,6 +120,24 @@ jobs: - run: python -Im pip install -e .[dev] - run: python -Ic 'import rdmo; print(rdmo.__version__)' + optional-dependencies: + name: Test installation of optional-dependencies + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-python@v4 + with: + python-version: "3.11" + cache: pip + - name: Install os requirements for python-ldap + run: | + sudo apt update + sudo apt install --yes libldap2-dev libsasl2-dev + - run: python -m pip install --upgrade pip setuptools + - run: python -m pip install .[allauth,ci,dev,gunicorn,ldap,memcached,mysql,postgres,pytest] + - run: python -m pip freeze + - run: python -m pip list --outdated + required-checks-pass: if: always() needs: @@ -127,6 +145,7 @@ jobs: - test - coveralls - dev-setup + - optional-dependencies runs-on: ubuntu-22.04 steps: - uses: re-actors/alls-green@release/v1 diff --git a/pyproject.toml b/pyproject.toml index b93e051421..8f9fc846ec 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,13 +15,13 @@ keywords = [ ] license = {text = "Apache-2.0"} authors = [ - {name = "RDMO Arbeitsgemeinschaft", email = "rdmo-team@listserv.dfn.de"}, + {name = "RDMO Arbeitsgemeinschaft", email = "rdmo-team@listserv.dfn.de"}, ] requires-python = ">=3.8" classifiers = [ "Development Status :: 5 - Production/Stable", "Environment :: Web Environment", - "Framework :: Django :: 3.2", + "Framework :: Django :: 4.2", "Intended Audience :: Science/Research", "License :: OSI Approved :: Apache Software License", "Operating System :: OS Independent", @@ -37,60 +37,66 @@ dynamic = [ "version", ] dependencies = [ - "defusedcsv~=2.0.0", - "defusedxml~=0.7.1", - "Django~=3.2.20", - "django-allauth~=0.53.1", - "django-cleanup~=6.0.0", + "defusedcsv~=2.0", + "defusedxml~=0.7", + "django~=4.2", + "django-cleanup~=8.0", "django-compressor~=4.4", - "django-extensions~=3.2.0", - "django-filter~=21.1", - "django-libsass==0.9", - "django-mathfilters~=1.0.0", - "django-mptt~=0.13.4", - "django-rest-swagger~=2.2.0", - "django-settings-export~=1.2.1", - "django-widget-tweaks~=1.4.8", - "djangorestframework~=3.12.4", - "drf-extensions~=0.7.0", - "iso8601~=2.0.0", - "Markdown~=3.3.7", - "pypandoc~=1.10.0", - "rules~=3.3.0", + "django-extensions~=3.2", + "django-filter~=23.2", + "django-libsass~=0.9", + "django-mathfilters~=1.0", + "django-mptt~=0.14", + "django-rest-swagger~=2.2", + "django-settings-export~=1.2", + "django-widget-tweaks~=1.5", + "djangorestframework~=3.14", + "drf-extensions~=0.7", + "iso8601~=2.0", + "markdown~=3.4", + "pypandoc~=1.11", + "rules~=3.3", ] [project.optional-dependencies] +allauth = [ + "django-allauth~=0.56", +] ci = [ - "coveralls", + "coveralls~=3.3", "rdmo[dev]", ] dev = [ - "django-debug-toolbar", - "pre-commit", + "django-debug-toolbar~=4.2", + "pre-commit~=3.4", + "rdmo[allauth]", "rdmo[pytest]", ] gunicorn = [ - "gunicorn>=19.9", + "gunicorn~=21.2", ] ldap = [ - "django-auth-ldap>=2", + "django-auth-ldap~=4.5", ] memcached = [ - "python-memcached>=1.58", + "python-memcached~=1.5", ] mysql = [ - "mysqlclient~=2.2.0", + "mysqlclient~=2.2", ] postgres = [ - "psycopg2-binary~=2.9.6", + # once python 3.12 is officially released, the psycopg team will release a psycopg-binary for it. + # replace these two lines with "psycopg[binary]~=3.1" + "psycopg[binary]~=3.1; python_version<'3.12'", + "psycopg2-binary~=2.9; python_version>='3.12'", ] pytest = [ - "pytest~=7.4.0", - "pytest-cov~=4.0.0", - "pytest-django~=4.5.0", - "pytest-mock~=3.11.0", - "pytest-randomly~=3.15.0", - "pytest-xdist~=3.3.1", + "pytest~=7.4", + "pytest-cov~=4.1", + "pytest-django~=4.5", + "pytest-mock~=3.11", + "pytest-randomly~=3.15", + "pytest-xdist~=3.3", ] [project.urls] @@ -181,6 +187,18 @@ testpaths = ["rdmo"] python_files = "test_*[!.txt].py" pythonpath = [".", "testing"] addopts = "-p no:randomly" +filterwarnings = [ + # fail on RemovedInDjango50Warning exception + "error::django.utils.deprecation.RemovedInDjango50Warning", + + # ignore warnings raised from within coreapi 2.3.3 + "ignore:'cgi' is deprecated and slated for removal in Python 3.13:DeprecationWarning", + "ignore:pkg_resources is deprecated as an API:DeprecationWarning", + + # ignore warnings raised from within django itself + # django/core/files/storage/__init__.py + "ignore:django.core.files.storage.get_storage_class is deprecated:django.utils.deprecation.RemovedInDjango51Warning", +] [tool.coverage.run] source = ["rdmo"] diff --git a/rdmo/accounts/forms.py b/rdmo/accounts/forms.py index e8e5f3df61..e82f7f4c38 100644 --- a/rdmo/accounts/forms.py +++ b/rdmo/accounts/forms.py @@ -28,7 +28,6 @@ def __init__(self, *args, **kwargs): self.fields['last_name'].widget = forms.TextInput(attrs={'placeholder': _('Last name')}) self.additional_fields = AdditionalField.objects.all() - self.additional_values = self.instance.additional_values.all() # add fields and init values for the Profile model for additional_field in self.additional_fields: @@ -46,8 +45,10 @@ def __init__(self, *args, **kwargs): self.fields[additional_field.key] = field - for additional_field_value in self.additional_values: - self.fields[additional_field.key].initial = additional_field_value.value + # existing user is going to be updated + if self.instance.pk is not None: + for additional_field_value in AdditionalFieldValue.objects.filter(user=self.instance): + self.fields[additional_field.key].initial = additional_field_value.value def save(self, *args, **kwargs): super().save(*args, **kwargs) diff --git a/rdmo/conditions/migrations/0025_alter_condition_editors.py b/rdmo/conditions/migrations/0025_alter_condition_editors.py new file mode 100644 index 0000000000..1eee5e7ede --- /dev/null +++ b/rdmo/conditions/migrations/0025_alter_condition_editors.py @@ -0,0 +1,24 @@ +# Generated by Django 4.2.5 on 2023-09-07 07:27 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ('sites', '0002_alter_domain_unique'), + ('conditions', '0024_uri_path'), + ] + + operations = [ + migrations.AlterField( + model_name='condition', + name='editors', + field=models.ManyToManyField( + blank=True, + help_text='The sites that can edit this condition (in a multi site setup).', + related_name='%(class)s_editors', + to='sites.site', + verbose_name='Editors', + ), + ), + ] diff --git a/rdmo/core/settings.py b/rdmo/core/settings.py index 411bac5a79..2a00454d85 100644 --- a/rdmo/core/settings.py +++ b/rdmo/core/settings.py @@ -133,8 +133,6 @@ USE_I18N = True -USE_L10N = True - USE_TZ = True LOGIN_URL = '/account/login/' diff --git a/rdmo/domain/migrations/0050_alter_attribute_editors.py b/rdmo/domain/migrations/0050_alter_attribute_editors.py new file mode 100644 index 0000000000..b85d4b3a8b --- /dev/null +++ b/rdmo/domain/migrations/0050_alter_attribute_editors.py @@ -0,0 +1,24 @@ +# Generated by Django 4.2.5 on 2023-09-07 07:27 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ('sites', '0002_alter_domain_unique'), + ('domain', '0049_attribute_add_editors'), + ] + + operations = [ + migrations.AlterField( + model_name='attribute', + name='editors', + field=models.ManyToManyField( + blank=True, + help_text='The sites that can edit this attribute (in a multi site setup).', + related_name='%(class)s_editors', + to='sites.site', + verbose_name='Editors', + ), + ), + ] diff --git a/rdmo/options/migrations/0032_alter_option_editors_alter_optionset_editors.py b/rdmo/options/migrations/0032_alter_option_editors_alter_optionset_editors.py new file mode 100644 index 0000000000..c00678692b --- /dev/null +++ b/rdmo/options/migrations/0032_alter_option_editors_alter_optionset_editors.py @@ -0,0 +1,35 @@ +# Generated by Django 4.2.5 on 2023-09-07 07:27 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ('sites', '0002_alter_domain_unique'), + ('options', '0031_add_editors'), + ] + + operations = [ + migrations.AlterField( + model_name='option', + name='editors', + field=models.ManyToManyField( + blank=True, + help_text='The sites that can edit this option (in a multi site setup).', + related_name='%(class)s_editors', + to='sites.site', + verbose_name='Editors', + ), + ), + migrations.AlterField( + model_name='optionset', + name='editors', + field=models.ManyToManyField( + blank=True, + help_text='The sites that can edit this option set (in a multi site setup).', + related_name='%(class)s_editors', + to='sites.site', + verbose_name='Editors', + ), + ), + ] diff --git a/rdmo/projects/forms.py b/rdmo/projects/forms.py index a6e32f7195..4ac7d42ab2 100644 --- a/rdmo/projects/forms.py +++ b/rdmo/projects/forms.py @@ -266,10 +266,15 @@ def __init__(self, *args, **kwargs): # add fields for the integration options for field in self.provider.fields: - try: - initial = IntegrationOption.objects.get(integration=self.instance, key=field.get('key')).value - except IntegrationOption.DoesNotExist: + # new integration instance is going to be created + if self.instance.pk is None: initial = None + # existing integration is going to be updated + else: + try: + initial = IntegrationOption.objects.get(integration=self.instance, key=field.get('key')).value + except IntegrationOption.DoesNotExist: + initial = None if field.get('placeholder'): attrs = {'placeholder': field.get('placeholder')} diff --git a/rdmo/projects/views/membership.py b/rdmo/projects/views/membership.py index 8392ff2d18..54242b57e9 100644 --- a/rdmo/projects/views/membership.py +++ b/rdmo/projects/views/membership.py @@ -66,7 +66,7 @@ class MembershipDeleteView(ObjectPermissionMixin, RedirectViewMixin, DeleteView) def get_queryset(self): return Membership.objects.filter(project_id=self.kwargs.get('project_id')) - def delete(self, *args, **kwargs): + def form_valid(self, form): self.obj = self.get_object() if (self.request.user in self.obj.project.owners) or is_site_manager(self.request.user): diff --git a/rdmo/questions/migrations/0091_alter_catalog_editors_alter_page_editors_and_more.py b/rdmo/questions/migrations/0091_alter_catalog_editors_alter_page_editors_and_more.py new file mode 100644 index 0000000000..e10a402832 --- /dev/null +++ b/rdmo/questions/migrations/0091_alter_catalog_editors_alter_page_editors_and_more.py @@ -0,0 +1,68 @@ +# Generated by Django 4.2.5 on 2023-09-07 07:27 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ('sites', '0002_alter_domain_unique'), + ('questions', '0090_add_editors'), + ] + + operations = [ + migrations.AlterField( + model_name='catalog', + name='editors', + field=models.ManyToManyField( + blank=True, + help_text='The sites that can edit this catalog (in a multi site setup).', + related_name='%(class)s_editors', + to='sites.site', + verbose_name='Editors', + ), + ), + migrations.AlterField( + model_name='page', + name='editors', + field=models.ManyToManyField( + blank=True, + help_text='The sites that can edit this page (in a multi site setup).', + related_name='%(class)s_editors', + to='sites.site', + verbose_name='Editors', + ), + ), + migrations.AlterField( + model_name='question', + name='editors', + field=models.ManyToManyField( + blank=True, + help_text='The sites that can edit this question (in a multi site setup).', + related_name='%(class)s_editors', + to='sites.site', + verbose_name='Editors', + ), + ), + migrations.AlterField( + model_name='questionset', + name='editors', + field=models.ManyToManyField( + blank=True, + help_text='The sites that can edit this questionset (in a multi site setup).', + related_name='%(class)s_editors', + to='sites.site', + verbose_name='Editors', + ), + ), + migrations.AlterField( + model_name='section', + name='editors', + field=models.ManyToManyField( + blank=True, + help_text='The sites that can edit this section (in a multi site setup).', + related_name='%(class)s_editors', + to='sites.site', + verbose_name='Editors', + ), + ), + ] diff --git a/rdmo/tasks/migrations/0036_alter_task_editors.py b/rdmo/tasks/migrations/0036_alter_task_editors.py new file mode 100644 index 0000000000..29f322a893 --- /dev/null +++ b/rdmo/tasks/migrations/0036_alter_task_editors.py @@ -0,0 +1,24 @@ +# Generated by Django 4.2.5 on 2023-09-07 07:27 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ('sites', '0002_alter_domain_unique'), + ('tasks', '0035_uri_path'), + ] + + operations = [ + migrations.AlterField( + model_name='task', + name='editors', + field=models.ManyToManyField( + blank=True, + help_text='The sites that can edit this task (in a multi site setup).', + related_name='%(class)s_editors', + to='sites.site', + verbose_name='Editors', + ), + ), + ] diff --git a/rdmo/views/migrations/0029_alter_view_editors.py b/rdmo/views/migrations/0029_alter_view_editors.py new file mode 100644 index 0000000000..1e2acb01bb --- /dev/null +++ b/rdmo/views/migrations/0029_alter_view_editors.py @@ -0,0 +1,24 @@ +# Generated by Django 4.2.5 on 2023-09-07 07:27 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ('sites', '0002_alter_domain_unique'), + ('views', '0028_uri_path'), + ] + + operations = [ + migrations.AlterField( + model_name='view', + name='editors', + field=models.ManyToManyField( + blank=True, + help_text='The sites that can edit this view (in a multi site setup).', + related_name='%(class)s_editors', + to='sites.site', + verbose_name='Editors', + ), + ), + ] diff --git a/testing/config/settings/base.py b/testing/config/settings/base.py index b954ad435b..4ee136eb4a 100644 --- a/testing/config/settings/base.py +++ b/testing/config/settings/base.py @@ -2,7 +2,7 @@ from django.utils.translation import gettext_lazy as _ -from . import INSTALLED_APPS +from . import INSTALLED_APPS, MIDDLEWARE DEBUG = False TEMPLATE_DEBUG = False @@ -29,6 +29,10 @@ 'allauth.account' ] +MIDDLEWARE += [ + 'allauth.account.middleware.AccountMiddleware' +] + ACCOUNT = True ACCOUNT_SIGNUP = True SOCIALACCOUNT = False diff --git a/testing/config/settings/mysql.py b/testing/config/settings/mysql.py index 2abee3e7af..9009619412 100644 --- a/testing/config/settings/mysql.py +++ b/testing/config/settings/mysql.py @@ -8,7 +8,6 @@ 'TEST': { 'CHARSET': 'utf8', 'COLLATION': 'utf8_general_ci', - 'SERIALIZE': False, }, 'OPTIONS': { 'init_command': "SET GLOBAL sql_mode=(SELECT REPLACE(@@sql_mode,'ONLY_FULL_GROUP_BY',''));" diff --git a/testing/config/settings/postgres.py b/testing/config/settings/postgres.py index d74efcdabd..a3e61d847f 100644 --- a/testing/config/settings/postgres.py +++ b/testing/config/settings/postgres.py @@ -5,6 +5,5 @@ 'USER': 'postgres_user', 'PASSWORD': 'postgres_password', 'HOST': '127.0.0.1', - 'TEST': {'SERIALIZE': False}, } } diff --git a/testing/config/settings/sqlite3.py b/testing/config/settings/sqlite3.py index 9f0a0ba8cf..8441c57a23 100644 --- a/testing/config/settings/sqlite3.py +++ b/testing/config/settings/sqlite3.py @@ -2,6 +2,5 @@ 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': 'db.sqlite3', - 'TEST': {'SERIALIZE': False}, } }