From ba41b27665efe34c17822d35c42166be0e38f4e5 Mon Sep 17 00:00:00 2001 From: Stefan Heinemann Date: Fri, 22 Aug 2025 17:31:33 +0200 Subject: [PATCH 1/2] Fix linting according to Ruff --- app/config/logging.py | 13 ++++++++----- app/config/settings_dev.py | 6 +++--- app/config/settings_prod.py | 4 ++-- app/config/settings_test.py | 4 ++-- app/tests/config/test_exception_handler.py | 2 +- app/tests/config/test_request_logging.py | 6 +++--- app/tests/distributions/test_dataset_model.py | 5 +++-- .../test_package_distribution_model.py | 5 +++-- app/wsgi.py | 3 ++- 9 files changed, 27 insertions(+), 21 deletions(-) diff --git a/app/config/logging.py b/app/config/logging.py index 55c28280..9168cc65 100644 --- a/app/config/logging.py +++ b/app/config/logging.py @@ -17,15 +17,18 @@ 'LogExtra', { 'http': { - 'request': { - 'method': str, 'header': dict[str, str] + 'request': { # noqa: F821 + 'method': str, # noqa: F821 + 'header': dict[str, str] # noqa: F821 }, - 'response': { - 'status_code': int, 'header': dict[str, str] + 'response': { # noqa: F821 + 'status_code': int, # noqa: F821 + 'header': dict[str, str] # noqa: F821 } }, 'url': { - 'path': str, 'scheme': str + 'path': str, # noqa: F821 + 'scheme': str # noqa: F821 } } ) diff --git a/app/config/settings_dev.py b/app/config/settings_dev.py index 436c652f..2b7f4449 100644 --- a/app/config/settings_dev.py +++ b/app/config/settings_dev.py @@ -1,6 +1,6 @@ import environ -from .settings_base import * # pylint: disable=wildcard-import, unused-wildcard-import +from .settings_base import * # noqa: F403 env = environ.Env() @@ -9,9 +9,9 @@ DEBUG = env.bool('DEBUG') if DEBUG: - INSTALLED_APPS += ['django_extensions', 'debug_toolbar'] + INSTALLED_APPS += ['django_extensions', 'debug_toolbar'] # noqa: F405 if DEBUG: MIDDLEWARE = [ 'debug_toolbar.middleware.DebugToolbarMiddleware', - ] + MIDDLEWARE + ] + MIDDLEWARE # noqa: F405 diff --git a/app/config/settings_prod.py b/app/config/settings_prod.py index 9d5352ba..c34ac3ec 100644 --- a/app/config/settings_prod.py +++ b/app/config/settings_prod.py @@ -1,11 +1,11 @@ -from .settings_base import * # pylint: disable=wildcard-import, unused-wildcard-import +from .settings_base import * # noqa: F403 DEBUG = False # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/5.0/howto/static-files/ -STATIC_ROOT = BASE_DIR / 'var' / 'www' / 'service_control' / 'static_files' +STATIC_ROOT = BASE_DIR / 'var' / 'www' / 'service_control' / 'static_files' # noqa: F405 STORAGES = { "staticfiles": { "BACKEND": "whitenoise.storage.CompressedManifestStaticFilesStorage", diff --git a/app/config/settings_test.py b/app/config/settings_test.py index 5a1c04a7..ef10c339 100644 --- a/app/config/settings_test.py +++ b/app/config/settings_test.py @@ -1,11 +1,11 @@ import os -from .settings_dev import * # pylint: disable=wildcard-import, unused-wildcard-import +from .settings_dev import * # noqa: F403 TESTING = True SECRET_KEY = 'django-insecure-6-72r#zx=sv6v@-4k@uf1gv32me@%yr*oqa*fu8&5l&a!ws)5#' # nosec B105 # The tests use the default database, remove the BOD so that django doesn't try to create it -del DATABASES['bod'] +del DATABASES['bod'] # noqa: F405 os.environ["NINJA_SKIP_REGISTRY"] = "yes" diff --git a/app/tests/config/test_exception_handler.py b/app/tests/config/test_exception_handler.py index 96b95514..5415a5ca 100644 --- a/app/tests/config/test_exception_handler.py +++ b/app/tests/config/test_exception_handler.py @@ -1,4 +1,4 @@ -import mock_api # pylint: disable=unused-import +import mock_api # noqa: F401 def test_handle_404_not_found(client): diff --git a/app/tests/config/test_request_logging.py b/app/tests/config/test_request_logging.py index 148031f5..65eea12a 100644 --- a/app/tests/config/test_request_logging.py +++ b/app/tests/config/test_request_logging.py @@ -5,7 +5,7 @@ import json -import mock_api # pylint: disable=unused-import +import mock_api # noqa: F401 import pytest from config.logging import generate_log_extra from ecs_logging import StdlibFormatter @@ -120,7 +120,7 @@ def test_500_server_error_logging(client, caplog, configure_logger): def test_http_error_logging(client, caplog, configure_logger): path = '/api/v1/trigger-http-error' - response = client.get(path) + client.get(path) # we need to split the caplog, since I can't get rid of the bloody # django.log which also logs the request @@ -134,7 +134,7 @@ def test_http_error_logging(client, caplog, configure_logger): def test_positive_request_log(client, caplog, configure_logger): path = '/api/v1/trigger-200-response' - response = client.get(path) + client.get(path) # we need to split the caplog, since I can't get rid of the bloody # django.log which also logs the request diff --git a/app/tests/distributions/test_dataset_model.py b/app/tests/distributions/test_dataset_model.py index 1b8ac9b0..0dc8b264 100644 --- a/app/tests/distributions/test_dataset_model.py +++ b/app/tests/distributions/test_dataset_model.py @@ -105,6 +105,7 @@ def test_field_updated_matches_update_time(provider, attribution): assert dataset.updated == time_updated -def test_raises_exception_when_creating_db_object_with_mandatory_field_null(provider, attribution): - with raises(ValidationError) as e: +def test_raises_exception_when_creating_db_object_with_mandatory_field_null( + provider, attribution): + with raises(ValidationError): Dataset.objects.create(provider=provider, attribution=attribution) diff --git a/app/tests/distributions/test_package_distribution_model.py b/app/tests/distributions/test_package_distribution_model.py index 00078e2f..650a2428 100644 --- a/app/tests/distributions/test_package_distribution_model.py +++ b/app/tests/distributions/test_package_distribution_model.py @@ -41,6 +41,7 @@ def test_create_package_distribution_in_database(dataset): assert distribution.dataset == dataset -def test_raises_exception_when_creating_db_object_with_mandatory_field_null(dataset): - with raises(ValidationError) as e: +def test_raises_exception_when_creating_db_object_with_mandatory_field_null( + dataset): + with raises(ValidationError): PackageDistribution.objects.create(dataset=dataset) diff --git a/app/wsgi.py b/app/wsgi.py index 998e71ac..cc9105ef 100755 --- a/app/wsgi.py +++ b/app/wsgi.py @@ -1,4 +1,5 @@ #!/usr/bin/env python +# ruff: noqa: E402 """ The gevent monkey import and patch suppress a warning, and a potential problem. Gunicorn would call it anyway, but if it tries to call it after the ssl module @@ -27,7 +28,7 @@ For more information on this file, see https://docs.djangoproject.com/en/5.0/howto/deployment/wsgi/ """ -import os +import os # noqa from gunicorn.app.base import BaseApplication from gunicorn.config import Config From 56577fd5ed41bc7d48b947c029d3dce00929dfd3 Mon Sep 17 00:00:00 2001 From: Stefan Heinemann Date: Fri, 22 Aug 2025 17:59:07 +0200 Subject: [PATCH 2/2] Format the code with ruff --- app/access/admin.py | 21 +- app/access/api.py | 14 +- app/access/apps.py | 4 +- app/access/migrations/0001_initial.py | 32 +- app/access/migrations/0002_user_deleted_at.py | 11 +- app/access/migrations/0003_user_user_id.py | 21 +- .../migrations/0004_alter_user_username.py | 9 +- .../0005_user_created_user_updated.py | 17 +- app/access/models.py | 38 +- app/access/schemas.py | 1 - app/bod/apps.py | 4 +- app/bod/management/commands/bod_sync.py | 229 ++++++----- app/bod/migrations/0001_initial.py | 155 +++++--- app/bod/models.py | 8 +- app/cognito/apps.py | 4 +- .../management/commands/cognito_sync.py | 111 +++--- app/cognito/utils/client.py | 93 +++-- app/config/api.py | 32 +- app/config/asgi.py | 2 +- app/config/logging.py | 61 +-- app/config/settings_base.py | 164 ++++---- app/config/settings_dev.py | 8 +- app/config/settings_prod.py | 2 +- app/config/settings_test.py | 4 +- app/config/urls.py | 7 +- app/distributions/admin.py | 28 +- app/distributions/api.py | 26 +- app/distributions/apps.py | 4 +- .../management/commands/stac_sync.py | 132 ++++--- app/distributions/migrations/0001_initial.py | 67 ++-- app/distributions/migrations/0002_dataset.py | 49 ++- ...tribution_options_alter_dataset_options.py | 7 +- ...ter_attribution_description_it_and_more.py | 35 +- ...__legacy_id_alter_dataset_slug_and_more.py | 25 +- .../migrations/0006_alter_dataset_slug.py | 9 +- .../migrations/0007_packagedistribution.py | 30 +- .../migrations/0008_attribution_slug.py | 19 +- ...set_slug_alter_packagedistribution_slug.py | 19 +- ...ibution_attribution_id_and_more_updated.py | 39 +- ...tion_de_dataset_description_en_and_more.py | 80 ++-- .../migrations/0012_dataset_geocat_id.py | 26 +- ...edistribution__legacy_imported_and_more.py | 17 +- ...on_created_attribution_updated_and_more.py | 31 +- app/distributions/models.py | 45 ++- app/manage.py | 5 +- app/provider/admin.py | 6 +- app/provider/api.py | 10 +- app/provider/apps.py | 4 +- app/provider/migrations/0001_initial.py | 23 +- ...er_name_remove_provider_prefix_and_more.py | 59 ++- ...03_provider_acronym_rm_provider_name_rm.py | 15 +- ...m_de_alter_provider_acronym_en_and_more.py | 39 +- .../migrations/0005_alter_provider_options.py | 8 +- ...m_it_alter_provider_acronym_rm_and_more.py | 35 +- .../migrations/0007_provider__legacy_id.py | 14 +- app/provider/migrations/0008_provider_slug.py | 19 +- ...name_slug_provider_provider_id_and_more.py | 15 +- .../0010_provider_created_provider_updated.py | 17 +- app/provider/models.py | 20 +- app/support/apps.py | 4 +- .../management/commands/manage_superuser.py | 14 +- app/tests/access/test_access_admin.py | 69 ++-- app/tests/access/test_access_api.py | 274 +++++++------ app/tests/access/test_access_models.py | 54 +-- app/tests/bod/test_bod_sync_command.py | 240 +++++++---- app/tests/cognito/test_cognito_client.py | 331 +++++++++------- .../cognito/test_cognito_sync_command.py | 185 +++++---- app/tests/config/mock_api.py | 2 +- app/tests/config/test_checker.py | 10 +- app/tests/config/test_exception_handler.py | 32 +- app/tests/config/test_request_logging.py | 126 +++--- app/tests/conftest.py | 36 +- .../distributions/test_attribution_model.py | 8 +- app/tests/distributions/test_dataset_model.py | 23 +- .../distributions/test_distributions_api.py | 371 ++++++++++-------- .../test_package_distribution_model.py | 13 +- .../distributions/test_stac_sync_command.py | 333 +++++++++------- app/tests/provider/test_provider_api.py | 205 +++++----- app/tests/provider/test_provider_model.py | 4 - app/tests/support/test_superuser_command.py | 63 +-- app/tests/utils/test_command.py | 26 +- app/tests/utils/test_database_router.py | 8 +- app/tests/utils/test_exceptions.py | 113 +++--- app/tests/utils/test_fields.py | 4 +- app/tests/utils/test_language.py | 52 +-- app/tests/utils/test_short_id.py | 4 +- app/utils/authentication.py | 2 +- app/utils/command.py | 49 ++- app/utils/database_router.py | 16 +- app/utils/language.py | 3 +- app/utils/short_id.py | 2 +- app/utils/testing.py | 4 +- app/wsgi.py | 30 +- 93 files changed, 2663 insertions(+), 2076 deletions(-) diff --git a/app/access/admin.py b/app/access/admin.py index 443e7bd5..014c9427 100644 --- a/app/access/admin.py +++ b/app/access/admin.py @@ -7,12 +7,19 @@ @admin.register(User) class UserAdmin(admin.ModelAdmin): # type:ignore[type-arg] - '''Admin View for User''' + """Admin View for User""" - list_display = ('user_id', 'username', 'last_name', 'first_name', 'deleted_at', 'provider') - list_filter = ('deleted_at', ('provider', admin.RelatedOnlyFieldListFilter)) - readonly_fields = ('user_id', 'deleted_at', 'created', 'updated') - actions = ('disable',) + list_display = ( + "user_id", + "username", + "last_name", + "first_name", + "deleted_at", + "provider", + ) + list_filter = ("deleted_at", ("provider", admin.RelatedOnlyFieldListFilter)) + readonly_fields = ("user_id", "deleted_at", "created", "updated") + actions = ("disable",) @admin.action(description="Disable selected users") def disable(self, request: HttpRequest, queryset: models.QuerySet[User]) -> None: @@ -22,6 +29,8 @@ def disable(self, request: HttpRequest, queryset: models.QuerySet[User]) -> None def get_queryset(self, request: HttpRequest) -> models.QuerySet[User]: return User.all_objects.get_queryset() - def delete_queryset(self, request: HttpRequest, queryset: models.QuerySet[User]) -> None: + def delete_queryset( + self, request: HttpRequest, queryset: models.QuerySet[User] + ) -> None: for user in queryset: user.delete() diff --git a/app/access/api.py b/app/access/api.py index 7aefb63d..dc2f2d61 100644 --- a/app/access/api.py +++ b/app/access/api.py @@ -34,7 +34,7 @@ def user_to_response(model: User) -> UserSchema: "users/{username}", response={200: UserSchema}, exclude_none=True, - auth=PermissionAuth('access.view_user') + auth=PermissionAuth("access.view_user"), ) def user(request: HttpRequest, username: str) -> UserSchema: """ @@ -49,7 +49,7 @@ def user(request: HttpRequest, username: str) -> UserSchema: "users", response={200: UserListSchema}, exclude_none=True, - auth=PermissionAuth('access.view_user') + auth=PermissionAuth("access.view_user"), ) def users(request: HttpRequest) -> dict[str, list[UserSchema]]: """ @@ -60,7 +60,9 @@ def users(request: HttpRequest) -> dict[str, list[UserSchema]]: return {"items": responses} -@router.post("users", response={201: UserSchema}, auth=PermissionAuth('access.add_user')) +@router.post( + "users", response={201: UserSchema}, auth=PermissionAuth("access.add_user") +) def create(request: HttpRequest, user_in: UserSchema) -> UserSchema: """Create the given user and return it. @@ -80,12 +82,12 @@ def create(request: HttpRequest, user_in: UserSchema) -> UserSchema: first_name=user_in.first_name, last_name=user_in.last_name, email=user_in.email, - provider=provider + provider=provider, ) return user_to_response(user_out) -@router.delete("users/{username}", auth=PermissionAuth('access.delete_user')) +@router.delete("users/{username}", auth=PermissionAuth("access.delete_user")) def delete(request: HttpRequest, username: str) -> HttpResponse: """ Delete the user with the given username. @@ -101,7 +103,7 @@ def delete(request: HttpRequest, username: str) -> HttpResponse: return HttpResponse(status=204) -@router.put("users/{username}", auth=PermissionAuth('access.change_user')) +@router.put("users/{username}", auth=PermissionAuth("access.change_user")) def update_user( request: HttpRequest, username: str, user_in: UserSchema ) -> HttpResponse | UserSchema: diff --git a/app/access/apps.py b/app/access/apps.py index 056377f1..86688bb5 100644 --- a/app/access/apps.py +++ b/app/access/apps.py @@ -2,5 +2,5 @@ class AccessConfig(AppConfig): - default_auto_field = 'django.db.models.BigAutoField' - name = 'access' + default_auto_field = "django.db.models.BigAutoField" + name = "access" diff --git a/app/access/migrations/0001_initial.py b/app/access/migrations/0001_initial.py index 2aa455de..c203a686 100644 --- a/app/access/migrations/0001_initial.py +++ b/app/access/migrations/0001_initial.py @@ -6,32 +6,38 @@ class Migration(migrations.Migration): - initial = True dependencies = [ - ('provider', '0006_alter_provider_acronym_it_alter_provider_acronym_rm_and_more'), + ( + "provider", + "0006_alter_provider_acronym_it_alter_provider_acronym_rm_and_more", + ), ] operations = [ migrations.CreateModel( - name='User', + name="User", fields=[ ( - 'id', + "id", models.BigAutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name='ID' - ) + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), ), - ('username', models.CharField(unique=True, verbose_name='User name')), - ('first_name', models.CharField(verbose_name='First name')), - ('last_name', models.CharField(verbose_name='Last name')), - ('email', models.EmailField(max_length=254, verbose_name='Email')), + ("username", models.CharField(unique=True, verbose_name="User name")), + ("first_name", models.CharField(verbose_name="First name")), + ("last_name", models.CharField(verbose_name="Last name")), + ("email", models.EmailField(max_length=254, verbose_name="Email")), ( - 'provider', + "provider", models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, to='provider.provider' - ) + on_delete=django.db.models.deletion.CASCADE, + to="provider.provider", + ), ), ], ), diff --git a/app/access/migrations/0002_user_deleted_at.py b/app/access/migrations/0002_user_deleted_at.py index 46afb814..cff777b5 100644 --- a/app/access/migrations/0002_user_deleted_at.py +++ b/app/access/migrations/0002_user_deleted_at.py @@ -5,15 +5,16 @@ class Migration(migrations.Migration): - dependencies = [ - ('access', '0001_initial'), + ("access", "0001_initial"), ] operations = [ migrations.AddField( - model_name='user', - name='deleted_at', - field=models.DateTimeField(blank=True, null=True, verbose_name='deleted at'), + model_name="user", + name="deleted_at", + field=models.DateTimeField( + blank=True, null=True, verbose_name="deleted at" + ), ), ] diff --git a/app/access/migrations/0003_user_user_id.py b/app/access/migrations/0003_user_user_id.py index abcac271..4d3eee31 100644 --- a/app/access/migrations/0003_user_user_id.py +++ b/app/access/migrations/0003_user_user_id.py @@ -9,28 +9,31 @@ def populate_user_id(apps: Apps, schema_editor: BaseDatabaseSchemaEditor) -> None: - User = apps.get_model('access', 'User') + User = apps.get_model("access", "User") for obj in User.objects.all(): obj.user_id = generate_short_id() obj.save() class Migration(migrations.Migration): - dependencies = [ - ('access', '0002_user_deleted_at'), + ("access", "0002_user_deleted_at"), ] operations = [ migrations.AddField( - model_name='user', - name='user_id', - field=models.CharField(default=generate_short_id, null=True, verbose_name='User ID'), + model_name="user", + name="user_id", + field=models.CharField( + default=generate_short_id, null=True, verbose_name="User ID" + ), ), migrations.RunPython(populate_user_id, migrations.RunPython.noop), migrations.AlterField( - model_name='user', - name='user_id', - field=models.CharField(default=generate_short_id, unique=True, verbose_name='User ID'), + model_name="user", + name="user_id", + field=models.CharField( + default=generate_short_id, unique=True, verbose_name="User ID" + ), ), ] diff --git a/app/access/migrations/0004_alter_user_username.py b/app/access/migrations/0004_alter_user_username.py index 7d093825..d24d0fbd 100644 --- a/app/access/migrations/0004_alter_user_username.py +++ b/app/access/migrations/0004_alter_user_username.py @@ -6,17 +6,16 @@ class Migration(migrations.Migration): - dependencies = [ - ('access', '0003_user_user_id'), + ("access", "0003_user_user_id"), ] operations = [ migrations.AlterField( - model_name='user', - name='username', + model_name="user", + name="username", field=utils.fields.CustomSlugField( - max_length=100, unique=True, verbose_name='User name' + max_length=100, unique=True, verbose_name="User name" ), ), ] diff --git a/app/access/migrations/0005_user_created_user_updated.py b/app/access/migrations/0005_user_created_user_updated.py index 3ead9077..bc506641 100644 --- a/app/access/migrations/0005_user_created_user_updated.py +++ b/app/access/migrations/0005_user_created_user_updated.py @@ -6,23 +6,24 @@ class Migration(migrations.Migration): - dependencies = [ - ('access', '0004_alter_user_username'), + ("access", "0004_alter_user_username"), ] operations = [ migrations.AddField( - model_name='user', - name='created', + model_name="user", + name="created", field=models.DateTimeField( - auto_now_add=True, default=django.utils.timezone.now, verbose_name='Created' + auto_now_add=True, + default=django.utils.timezone.now, + verbose_name="Created", ), preserve_default=False, ), migrations.AddField( - model_name='user', - name='updated', - field=models.DateTimeField(auto_now=True, verbose_name='Updated'), + model_name="user", + name="updated", + field=models.DateTimeField(auto_now=True, verbose_name="Updated"), ), ] diff --git a/app/access/models.py b/app/access/models.py index 85fc27f3..894cf090 100644 --- a/app/access/models.py +++ b/app/access/models.py @@ -16,7 +16,7 @@ class CognitoInconsistencyError(Exception): - """ An exception indicating that the state in the database and state in cognito have + """An exception indicating that the state in the database and state in cognito have diverged. """ @@ -46,7 +46,9 @@ def __str__(self) -> str: return str(self.username) username = CustomSlugField(_(_context, "User name"), unique=True, max_length=100) - user_id = models.CharField(_(_context, "User ID"), unique=True, default=generate_short_id) + user_id = models.CharField( + _(_context, "User ID"), unique=True, default=generate_short_id + ) created = models.DateTimeField(_(_context, "Created"), auto_now_add=True) updated = models.DateTimeField(_(_context, "Updated"), auto_now=True) first_name = models.CharField(_(_context, "First name")) @@ -70,7 +72,7 @@ def save( force_insert: bool | tuple[ModelBase, ...] = False, force_update: bool = False, using: str | None = None, - update_fields: Iterable[str] | None = None + update_fields: Iterable[str] | None = None, ) -> None: """Validates the model before writing it to the database and syncs with cognito.""" @@ -78,20 +80,28 @@ def save( client = Client() with transaction.atomic(): if self._state.adding: - super().save(force_insert=True, using=using, update_fields=update_fields) + super().save( + force_insert=True, using=using, update_fields=update_fields + ) if not client.create_user(self.user_id, self.username, self.email): - logger.critical("User %s already exists in cognito, not created", self.user_id) + logger.critical( + "User %s already exists in cognito, not created", self.user_id + ) raise CognitoInconsistencyError() else: User.all_objects.select_for_update().filter(pk=self.pk).get() - super().save(force_update=True, using=using, update_fields=update_fields) + super().save( + force_update=True, using=using, update_fields=update_fields + ) if not client.update_user(self.user_id, self.username, self.email): - logger.critical("User %s does not exist in cognito, not updated", self.user_id) + logger.critical( + "User %s does not exist in cognito, not updated", self.user_id + ) raise CognitoInconsistencyError() - def delete(self, - using: str | None = None, - keep_parents: bool = False) -> tuple[int, dict[str, int]]: + def delete( + self, using: str | None = None, keep_parents: bool = False + ) -> tuple[int, dict[str, int]]: """Deletes the user from the database and cognito.""" client = Client() @@ -99,7 +109,9 @@ def delete(self, User.all_objects.select_for_update().filter(pk=self.pk).get() result = super().delete(using=using, keep_parents=keep_parents) if not client.delete_user(self.user_id): - logger.critical("User %s does not exist in cognito, not deleted", self.user_id) + logger.critical( + "User %s does not exist in cognito, not deleted", self.user_id + ) raise CognitoInconsistencyError() return result @@ -113,5 +125,7 @@ def disable(self) -> None: self.deleted_at = timezone.now() super().save(force_update=True) if not client.disable_user(self.user_id): - logger.critical("User %s does not exist in cognito, not disabled", self.user_id) + logger.critical( + "User %s does not exist in cognito, not disabled", self.user_id + ) raise CognitoInconsistencyError() diff --git a/app/access/schemas.py b/app/access/schemas.py index ee245aa6..992097e5 100644 --- a/app/access/schemas.py +++ b/app/access/schemas.py @@ -2,7 +2,6 @@ class UserSchema(Schema): - username: str first_name: str last_name: str diff --git a/app/bod/apps.py b/app/bod/apps.py index 7b417c44..cbe2cc42 100644 --- a/app/bod/apps.py +++ b/app/bod/apps.py @@ -2,5 +2,5 @@ class BodConfig(AppConfig): - default_auto_field = 'django.db.models.BigAutoField' - name = 'bod' + default_auto_field = "django.db.models.BigAutoField" + name = "bod" diff --git a/app/bod/management/commands/bod_sync.py b/app/bod/management/commands/bod_sync.py index bea940fc..5f152504 100644 --- a/app/bod/management/commands/bod_sync.py +++ b/app/bod/management/commands/bod_sync.py @@ -16,13 +16,14 @@ from django.core.management.base import CommandParser from django.db import transaction -Counter = TypedDict('Counter', {'added': int, 'cleared': int, 'removed': int, 'updated': int}) -Operation = Literal['added', 'cleared', 'removed', 'updated'] +Counter = TypedDict( + "Counter", {"added": int, "cleared": int, "removed": int, "updated": int} +) +Operation = Literal["added", "cleared", "removed", "updated"] class Handler(CommandHandler): - - def __init__(self, command: CustomBaseCommand, options: dict['str', Any]): + def __init__(self, command: CustomBaseCommand, options: dict["str", Any]): super().__init__(command, options) self.clear = options["clear"] self.dry_run = options["dry_run"] @@ -31,10 +32,14 @@ def __init__(self, command: CustomBaseCommand, options: dict['str', Any]): self.include_datasets = options["datasets"] self.counts: dict[str, Counter] = {} - def increment_counter(self, model_name: str, operation: Operation, value: int = 1) -> None: - """ Updates internal counters of operations on models. """ + def increment_counter( + self, model_name: str, operation: Operation, value: int = 1 + ) -> None: + """Updates internal counters of operations on models.""" - self.counts.setdefault(model_name, {'added': 0, 'cleared': 0, 'removed': 0, 'updated': 0}) + self.counts.setdefault( + model_name, {"added": 0, "cleared": 0, "removed": 0, "updated": 0} + ) self.counts[model_name][operation] += value def update_model( @@ -42,9 +47,9 @@ def update_model( model: Provider | Attribution | Dataset, attribute: str, new_value: str, - is_new_model: bool + is_new_model: bool, ) -> bool: - """ Update the attributes of the given model and print the changed ones. """ + """Update the attributes of the given model and print the changed ones.""" changed = False old_value = getattr(model, attribute) @@ -59,15 +64,15 @@ def update_model( return changed def clear_providers(self) -> None: - """ Remove existing providers previously imported from BOD. """ + """Remove existing providers previously imported from BOD.""" _, cleared = Provider.objects.filter(_legacy_id__isnull=False).delete() for model_class, count in cleared.items(): - model_name = model_class.split('.')[-1].lower() - self.increment_counter(model_name, 'cleared', count) + model_name = model_class.split(".")[-1].lower() + self.increment_counter(model_name, "cleared", count) def import_providers(self) -> None: - """ Import providers from the old contact organizations table. + """Import providers from the old contact organizations table. This function adds new providers, updates existing ones (with a matching legacy ID) and removes orphans (with a legacy id not found in the BOD). @@ -80,7 +85,10 @@ def import_providers(self) -> None: processed = set() for organization in BodContactOrganisation.objects.all(): - if not organization.attribution or len(organization.attribution.split('.')) != 2: + if ( + not organization.attribution + or len(organization.attribution.split(".")) != 2 + ): # Skip entries that are not a provider. # BodContactOrganisation (table 'contactorganisation') contains providers and # attributions. Providers are of the format "ch." and only include a @@ -104,53 +112,57 @@ def import_providers(self) -> None: acronym_en="undefined", name_de="undefined", name_fr="undefined", - name_en="undefined" + name_en="undefined", ) - self.increment_counter('provider', 'added') + self.increment_counter("provider", "added") self.print(f"Added provider '{organization.name_en}'") self.update_provider(provider, organization, is_new_model) provider.save() # Remove orphaned providers - orphans = Provider.objects.filter(_legacy_id__isnull=False - ).exclude(_legacy_id__in=processed) + orphans = Provider.objects.filter(_legacy_id__isnull=False).exclude( + _legacy_id__in=processed + ) _, removed = orphans.delete() for model_class, count in removed.items(): - model_name = model_class.split('.')[-1].lower() - self.increment_counter(model_name, 'removed', count) + model_name = model_class.split(".")[-1].lower() + self.increment_counter(model_name, "removed", count) def update_provider( - self, provider: Provider, organization: BodContactOrganisation, is_new_model: bool + self, + provider: Provider, + organization: BodContactOrganisation, + is_new_model: bool, ) -> None: - """ Update the attributes of a provider. """ + """Update the attributes of a provider.""" any_changed = False for provider_attribute, organization_attribute in ( - ('name_de', 'name_de'), - ('name_en', 'name_en'), - ('name_fr', 'name_fr'), - ('name_it', 'name_it'), - ('name_rm', 'name_rm'), - ('acronym_de', 'abkuerzung_de'), - ('acronym_fr', 'abkuerzung_fr'), - ('acronym_en', 'abkuerzung_en'), - ('acronym_it', 'abkuerzung_it'), - ('acronym_rm', 'abkuerzung_rm'), - ('provider_id', 'attribution'), + ("name_de", "name_de"), + ("name_en", "name_en"), + ("name_fr", "name_fr"), + ("name_it", "name_it"), + ("name_rm", "name_rm"), + ("acronym_de", "abkuerzung_de"), + ("acronym_fr", "abkuerzung_fr"), + ("acronym_en", "abkuerzung_en"), + ("acronym_it", "abkuerzung_it"), + ("acronym_rm", "abkuerzung_rm"), + ("provider_id", "attribution"), ): changed = self.update_model( provider, provider_attribute, getattr(organization, organization_attribute), - is_new_model + is_new_model, ) any_changed = any_changed or changed if any_changed and not is_new_model: - self.increment_counter('provider', 'updated') + self.increment_counter("provider", "updated") def import_attribution(self) -> None: - """ Import the attributions from the old contact organizations table. + """Import the attributions from the old contact organizations table. The BOD stores one attribution per organization in a column of the organizations table with translations in the translations table. Here, we create one attribution entry for each @@ -164,7 +176,6 @@ def import_attribution(self) -> None: processed = set() for organization in BodContactOrganisation.objects.all(): - # Keep track of processed organizations for orphan removal legacy_id = organization.pk_contactorganisation_id processed.add(legacy_id) @@ -172,13 +183,17 @@ def import_attribution(self) -> None: # Get related provider provider = None if organization.attribution: - provider_of_attribution = ".".join(organization.attribution.split(".", 2)[:2]) - provider = Provider.objects.filter(provider_id=provider_of_attribution).first() + provider_of_attribution = ".".join( + organization.attribution.split(".", 2)[:2] + ) + provider = Provider.objects.filter( + provider_id=provider_of_attribution + ).first() if not provider: # Skip as no matching provider self.print( - f"skipping attribution '{organization.attribution}' " + - "as no matching provider was found" + f"skipping attribution '{organization.attribution}' " + + "as no matching provider was found" ) continue @@ -195,9 +210,9 @@ def import_attribution(self) -> None: name_en="undefined", description_de="undefined", description_fr="undefined", - description_en="undefined" + description_en="undefined", ) - self.increment_counter('attribution', 'added') + self.increment_counter("attribution", "added") self.print(f"Added attribution '{organization.attribution}'") self.update_attribution(attribution, organization, is_new_model) @@ -205,46 +220,53 @@ def import_attribution(self) -> None: attribution.save() # Remove orphaned attributions - orphans = Attribution.objects.filter(_legacy_id__isnull=False - ).exclude(_legacy_id__in=processed) + orphans = Attribution.objects.filter(_legacy_id__isnull=False).exclude( + _legacy_id__in=processed + ) _, removed = orphans.delete() for model, count in removed.items(): - model_class = model.split('.')[-1].lower() - self.increment_counter(model_class, 'removed', count) + model_class = model.split(".")[-1].lower() + self.increment_counter(model_class, "removed", count) def update_attribution( - self, attribution: Attribution, organization: BodContactOrganisation, is_new_model: bool + self, + attribution: Attribution, + organization: BodContactOrganisation, + is_new_model: bool, ) -> None: - """ Update the attributes of an attribution. """ + """Update the attributes of an attribution.""" any_changed = False - translation = BodTranslations.objects.filter(msg_id=organization.attribution).first() + translation = BodTranslations.objects.filter( + msg_id=organization.attribution + ).first() for attribution_attribute, translation_attribute in ( - ('name_de', 'de'), - ('name_fr', 'fr'), - ('name_en', 'en'), - ('name_it', 'it'), - ('name_rm', 'rm'), - ('description_de', 'de'), - ('description_fr', 'fr'), - ('description_en', 'en'), - ('description_it', 'it'), - ('description_rm', 'rm'), - ('attribution_id', '') # will be updated to organization.attribution + ("name_de", "de"), + ("name_fr", "fr"), + ("name_en", "en"), + ("name_it", "it"), + ("name_rm", "rm"), + ("description_de", "de"), + ("description_fr", "fr"), + ("description_en", "en"), + ("description_it", "it"), + ("description_rm", "rm"), + ("attribution_id", ""), # will be updated to organization.attribution ): changed = self.update_model( attribution, attribution_attribute, - getattr(translation, translation_attribute, '') or organization.attribution or - 'undefined', - is_new_model + getattr(translation, translation_attribute, "") + or organization.attribution + or "undefined", + is_new_model, ) any_changed = any_changed or changed if any_changed and not is_new_model: - self.increment_counter('attribution', 'updated') + self.increment_counter("attribution", "updated") def import_datasets(self) -> None: - """ Import all datasets of legacy providers. + """Import all datasets of legacy providers. This function adds new datasets, updates existing ones (with a matching legacy ID) and removes orphans (with a legacy id not found in the BOD). @@ -255,7 +277,7 @@ def import_datasets(self) -> None: """ processed = set() - for bod_dataset in BodDataset.objects.filter(staging='prod').all(): + for bod_dataset in BodDataset.objects.filter(staging="prod").all(): # Keep track of processed BOD datasets for orphan removal legacy_id = bod_dataset.id processed.add(legacy_id) @@ -267,8 +289,8 @@ def import_datasets(self) -> None: if not attribution: # Skip as no matching attribution self.print( - f"skipping dataset '{bod_dataset.id_dataset}' " + - "as no matching attribution was found" + f"skipping dataset '{bod_dataset.id_dataset}' " + + "as no matching attribution was found" ) continue @@ -284,7 +306,9 @@ def import_datasets(self) -> None: continue # Get meta information title and description - bod_meta = BodGeocatPublish.objects.filter(fk_id_dataset=bod_dataset.id_dataset).first() + bod_meta = BodGeocatPublish.objects.filter( + fk_id_dataset=bod_dataset.id_dataset + ).first() if not bod_meta: bod_meta = BodGeocatPublish() @@ -305,7 +329,9 @@ def import_datasets(self) -> None: # Get or create dataset is_new_model = False dataset = Dataset.objects.filter( - provider=attribution.provider, attribution=attribution, _legacy_id=legacy_id + provider=attribution.provider, + attribution=attribution, + _legacy_id=legacy_id, ).first() if not dataset: is_new_model = True @@ -324,9 +350,9 @@ def import_datasets(self) -> None: description_it=bod_meta.abstract_it, description_rm=bod_meta.abstract_rm, _legacy_id=legacy_id, - dataset_id='undefined' + dataset_id="undefined", ) - self.increment_counter('dataset', 'added') + self.increment_counter("dataset", "added") self.print(f"Added dataset '{bod_dataset.id_dataset}'") self.update_dataset(dataset, bod_dataset, bod_meta, is_new_model) @@ -334,50 +360,59 @@ def import_datasets(self) -> None: dataset.save() # Remove orphaned datasets - orphans = Dataset.objects.filter(_legacy_id__isnull=False).exclude(_legacy_id__in=processed) + orphans = Dataset.objects.filter(_legacy_id__isnull=False).exclude( + _legacy_id__in=processed + ) _, removed = orphans.delete() for model, count in removed.items(): - model_class = model.split('.')[-1].lower() - self.increment_counter(model_class, 'removed', count) + model_class = model.split(".")[-1].lower() + self.increment_counter(model_class, "removed", count) def update_dataset( self, dataset: Dataset, bod_dataset: BodDataset, bod_meta: BodGeocatPublish, - is_new_model: bool + is_new_model: bool, ) -> None: - """ Update the attributes of a dataset. """ + """Update the attributes of a dataset.""" any_changed = False - for dataset_attribute, bod_dataset_attribute in (('dataset_id', 'id_dataset'), - ('geocat_id', 'fk_geocat'),): + for dataset_attribute, bod_dataset_attribute in ( + ("dataset_id", "id_dataset"), + ("geocat_id", "fk_geocat"), + ): changed = self.update_model( dataset, dataset_attribute, getattr(bod_dataset, bod_dataset_attribute), - is_new_model + is_new_model, ) any_changed = any_changed or changed - for dataset_attribute, bod_dataset_attribute in (('title_de', 'bezeichnung_de'), - ('title_fr', 'bezeichnung_fr'), - ('title_en', 'bezeichnung_en'), - ('title_it', 'bezeichnung_it'), - ('title_rm', 'bezeichnung_rm'), - ('description_de', 'abstract_de'), - ('description_fr', 'abstract_fr'), - ('description_en', 'abstract_en'), - ('description_it', 'abstract_it'), - ('description_rm', 'abstract_rm'),): + for dataset_attribute, bod_dataset_attribute in ( + ("title_de", "bezeichnung_de"), + ("title_fr", "bezeichnung_fr"), + ("title_en", "bezeichnung_en"), + ("title_it", "bezeichnung_it"), + ("title_rm", "bezeichnung_rm"), + ("description_de", "abstract_de"), + ("description_fr", "abstract_fr"), + ("description_en", "abstract_en"), + ("description_it", "abstract_it"), + ("description_rm", "abstract_rm"), + ): changed = self.update_model( - dataset, dataset_attribute, getattr(bod_meta, bod_dataset_attribute), is_new_model + dataset, + dataset_attribute, + getattr(bod_meta, bod_dataset_attribute), + is_new_model, ) any_changed = any_changed or changed if any_changed and not is_new_model: - self.increment_counter('dataset', 'updated') + self.increment_counter("dataset", "updated") def run(self) -> None: - """ Main entry point of command. """ + """Main entry point of command.""" with transaction.atomic(): # Clear data @@ -394,8 +429,10 @@ def run(self) -> None: # Print counts printed = False if ( - not self.clear and not self.include_providers and not self.include_attributions and - not self.include_datasets + not self.clear + and not self.include_providers + and not self.include_attributions + and not self.include_datasets ): printed = True self.print_warning("no option provided, nothing changed") diff --git a/app/bod/migrations/0001_initial.py b/app/bod/migrations/0001_initial.py index 6e15ea67..fab83aa6 100644 --- a/app/bod/migrations/0001_initial.py +++ b/app/bod/migrations/0001_initial.py @@ -6,81 +6,140 @@ class Migration(migrations.Migration): - initial = True dependencies = [] operations = [ migrations.CreateModel( - name='BodContactOrganisation', + name="BodContactOrganisation", fields=[ - ('pk_contactorganisation_id', models.AutoField(primary_key=True, serialize=False)), - ('abkuerzung_de', models.CharField(blank=True, max_length=255, null=True)), - ('abkuerzung_fr', models.CharField(blank=True, max_length=255, null=True)), - ('abkuerzung_en', models.CharField(blank=True, max_length=255, null=True)), - ('abkuerzung_it', models.CharField(blank=True, max_length=255, null=True)), - ('abkuerzung_rm', models.CharField(blank=True, max_length=255, null=True)), - ('name_de', models.CharField(blank=True, max_length=255, null=True)), - ('name_fr', models.CharField(blank=True, max_length=255, null=True)), - ('name_en', models.CharField(blank=True, max_length=255, null=True)), - ('name_it', models.CharField(blank=True, max_length=255, null=True)), - ('name_rm', models.CharField(blank=True, max_length=255, null=True)), - ('attribution', models.TextField(blank=True, null=True)), + ( + "pk_contactorganisation_id", + models.AutoField(primary_key=True, serialize=False), + ), + ( + "abkuerzung_de", + models.CharField(blank=True, max_length=255, null=True), + ), + ( + "abkuerzung_fr", + models.CharField(blank=True, max_length=255, null=True), + ), + ( + "abkuerzung_en", + models.CharField(blank=True, max_length=255, null=True), + ), + ( + "abkuerzung_it", + models.CharField(blank=True, max_length=255, null=True), + ), + ( + "abkuerzung_rm", + models.CharField(blank=True, max_length=255, null=True), + ), + ("name_de", models.CharField(blank=True, max_length=255, null=True)), + ("name_fr", models.CharField(blank=True, max_length=255, null=True)), + ("name_en", models.CharField(blank=True, max_length=255, null=True)), + ("name_it", models.CharField(blank=True, max_length=255, null=True)), + ("name_rm", models.CharField(blank=True, max_length=255, null=True)), + ("attribution", models.TextField(blank=True, null=True)), ], options={ - 'db_table': 'contactorganisation', - 'managed': settings.TESTING, + "db_table": "contactorganisation", + "managed": settings.TESTING, }, ), migrations.CreateModel( - name='BodDataset', + name="BodDataset", fields=[ - ('id', models.AutoField(primary_key=True, serialize=False, unique=True)), - ('id_dataset', models.TextField()), - ('fk_geocat', models.TextField()), - ('fk_contactorganisation_id', models.IntegerField(blank=True, null=True)), - ('staging', models.TextField(blank=True, null=True)), + ( + "id", + models.AutoField(primary_key=True, serialize=False, unique=True), + ), + ("id_dataset", models.TextField()), + ("fk_geocat", models.TextField()), + ( + "fk_contactorganisation_id", + models.IntegerField(blank=True, null=True), + ), + ("staging", models.TextField(blank=True, null=True)), ], options={ - 'db_table': 'dataset', - 'managed': settings.TESTING, + "db_table": "dataset", + "managed": settings.TESTING, }, ), migrations.CreateModel( - name='BodGeocatPublish', + name="BodGeocatPublish", fields=[ - ('bgdi_id', models.AutoField(primary_key=True, serialize=False, unique=True)), - ('fk_id_dataset', models.TextField()), - ('bezeichnung_de', models.CharField(blank=True, max_length=5000, null=True)), - ('bezeichnung_fr', models.CharField(blank=True, max_length=5000, null=True)), - ('bezeichnung_it', models.CharField(blank=True, max_length=5000, null=True)), - ('bezeichnung_rm', models.CharField(blank=True, max_length=5000, null=True)), - ('bezeichnung_en', models.CharField(blank=True, max_length=5000, null=True)), - ('abstract_de', models.CharField(blank=True, max_length=5000, null=True)), - ('abstract_fr', models.CharField(blank=True, max_length=5000, null=True)), - ('abstract_it', models.CharField(blank=True, max_length=5000, null=True)), - ('abstract_rm', models.CharField(blank=True, max_length=5000, null=True)), - ('abstract_en', models.CharField(blank=True, max_length=5000, null=True)), + ( + "bgdi_id", + models.AutoField(primary_key=True, serialize=False, unique=True), + ), + ("fk_id_dataset", models.TextField()), + ( + "bezeichnung_de", + models.CharField(blank=True, max_length=5000, null=True), + ), + ( + "bezeichnung_fr", + models.CharField(blank=True, max_length=5000, null=True), + ), + ( + "bezeichnung_it", + models.CharField(blank=True, max_length=5000, null=True), + ), + ( + "bezeichnung_rm", + models.CharField(blank=True, max_length=5000, null=True), + ), + ( + "bezeichnung_en", + models.CharField(blank=True, max_length=5000, null=True), + ), + ( + "abstract_de", + models.CharField(blank=True, max_length=5000, null=True), + ), + ( + "abstract_fr", + models.CharField(blank=True, max_length=5000, null=True), + ), + ( + "abstract_it", + models.CharField(blank=True, max_length=5000, null=True), + ), + ( + "abstract_rm", + models.CharField(blank=True, max_length=5000, null=True), + ), + ( + "abstract_en", + models.CharField(blank=True, max_length=5000, null=True), + ), ], options={ - 'db_table': 'geocat_publish', - 'managed': settings.TESTING, + "db_table": "geocat_publish", + "managed": settings.TESTING, }, ), migrations.CreateModel( - name='BodTranslations', + name="BodTranslations", fields=[ - ('msg_id', models.CharField(max_length=255, primary_key=True, serialize=False)), - ('de', models.CharField(blank=True, max_length=5000, null=True)), - ('fr', models.CharField(blank=True, max_length=5000, null=True)), - ('it', models.CharField(blank=True, max_length=5000, null=True)), - ('rm', models.CharField(blank=True, max_length=5000, null=True)), - ('en', models.CharField(blank=True, max_length=5000, null=True)), + ( + "msg_id", + models.CharField(max_length=255, primary_key=True, serialize=False), + ), + ("de", models.CharField(blank=True, max_length=5000, null=True)), + ("fr", models.CharField(blank=True, max_length=5000, null=True)), + ("it", models.CharField(blank=True, max_length=5000, null=True)), + ("rm", models.CharField(blank=True, max_length=5000, null=True)), + ("en", models.CharField(blank=True, max_length=5000, null=True)), ], options={ - 'db_table': 'translations', - 'managed': settings.TESTING, + "db_table": "translations", + "managed": settings.TESTING, }, ), ] diff --git a/app/bod/models.py b/app/bod/models.py index 9d075091..31c1367c 100644 --- a/app/bod/models.py +++ b/app/bod/models.py @@ -21,7 +21,7 @@ class BodContactOrganisation(models.Model): class Meta: managed = settings.TESTING - db_table = 'contactorganisation' + db_table = "contactorganisation" class BodDataset(models.Model): @@ -33,7 +33,7 @@ class BodDataset(models.Model): class Meta: managed = settings.TESTING - db_table = 'dataset' + db_table = "dataset" class BodTranslations(models.Model): @@ -47,7 +47,7 @@ class BodTranslations(models.Model): class Meta: managed = settings.TESTING - db_table = 'translations' + db_table = "translations" class BodGeocatPublish(models.Model): @@ -68,4 +68,4 @@ class BodGeocatPublish(models.Model): class Meta: managed = settings.TESTING - db_table = 'geocat_publish' + db_table = "geocat_publish" diff --git a/app/cognito/apps.py b/app/cognito/apps.py index 1ce5ed7f..572bdfc6 100644 --- a/app/cognito/apps.py +++ b/app/cognito/apps.py @@ -2,5 +2,5 @@ class CognitoConfig(AppConfig): - default_auto_field = 'django.db.models.BigAutoField' - name = 'cognito' + default_auto_field = "django.db.models.BigAutoField" + name = "cognito" diff --git a/app/cognito/management/commands/cognito_sync.py b/app/cognito/management/commands/cognito_sync.py index 6e38eeb0..1b3aba78 100644 --- a/app/cognito/management/commands/cognito_sync.py +++ b/app/cognito/management/commands/cognito_sync.py @@ -14,96 +14,103 @@ class Handler(CommandHandler): - def __init__(self, command: CustomBaseCommand, options: dict[str, Any]) -> None: super().__init__(command, options) - self.clear = options['clear'] - self.dry_run = options['dry_run'] + self.clear = options["clear"] + self.dry_run = options["dry_run"] self.client = Client() - self.counts = {'added': 0, 'deleted': 0, 'updated': 0, 'enabled': 0, 'disabled': 0} + self.counts = { + "added": 0, + "deleted": 0, + "updated": 0, + "enabled": 0, + "disabled": 0, + } def clear_users(self) -> None: - """ Remove all existing cognito users. """ + """Remove all existing cognito users.""" for user in self.client.list_users(): - self.counts['deleted'] += 1 - user_id = user['Username'] - self.print(f'deleting user {user_id}') + self.counts["deleted"] += 1 + user_id = user["Username"] + self.print(f"deleting user {user_id}") if not self.dry_run: deleted = self.client.delete_user(user_id) if not deleted: self.print_error( - 'Could not delete %s, might not exist or might be unmanged', user_id + "Could not delete %s, might not exist or might be unmanged", + user_id, ) def add_user(self, user: User) -> None: - """ Add a local user to cognito. """ + """Add a local user to cognito.""" - self.counts['added'] += 1 - self.print(f'adding user {user.user_id}') + self.counts["added"] += 1 + self.print(f"adding user {user.user_id}") if not self.dry_run: created = self.client.create_user(user.user_id, user.username, user.email) if not created: self.print_error( - 'Could not create %s, might already exist as unmanaged user', user.user_id + "Could not create %s, might already exist as unmanaged user", + user.user_id, ) def delete_user(self, user_id: str) -> None: - """ Delete a remote user from cognito. """ + """Delete a remote user from cognito.""" - self.counts['deleted'] += 1 - self.print(f'deleting user {user_id}') + self.counts["deleted"] += 1 + self.print(f"deleting user {user_id}") if not self.dry_run: deleted = self.client.delete_user(user_id) if not deleted: self.print_error( - 'Could not delete %s, might not exist or might be unmanaged', user_id + "Could not delete %s, might not exist or might be unmanaged", + user_id, ) - def update_user(self, local_user: User, remote_user: 'UserTypeTypeDef') -> None: - """ Update a remote user in cognito. """ + def update_user(self, local_user: User, remote_user: "UserTypeTypeDef") -> None: + """Update a remote user in cognito.""" - remote_attributes = user_attributes_to_dict(remote_user['Attributes']) - changed = ( - local_user.email != remote_attributes.get('email') or - local_user.username != remote_attributes.get('preferred_username') - ) + remote_attributes = user_attributes_to_dict(remote_user["Attributes"]) + changed = local_user.email != remote_attributes.get( + "email" + ) or local_user.username != remote_attributes.get("preferred_username") if changed: - self.counts['updated'] += 1 - self.print(f'updating user {local_user.user_id}') + self.counts["updated"] += 1 + self.print(f"updating user {local_user.user_id}") if not self.dry_run: updated = self.client.update_user( local_user.user_id, local_user.username, local_user.email ) if not updated: self.print_error( - 'Could not update %s, might not exist or might be unmanaged', - local_user.user_id + "Could not update %s, might not exist or might be unmanaged", + local_user.user_id, ) - if local_user.is_active != remote_user['Enabled']: + if local_user.is_active != remote_user["Enabled"]: if local_user.is_active: - self.counts['enabled'] += 1 - self.print(f'enabling user {local_user.user_id}') + self.counts["enabled"] += 1 + self.print(f"enabling user {local_user.user_id}") if not self.dry_run: enabled = self.client.enable_user(local_user.user_id) if not enabled: - self.print_error('Could not enable %s', local_user.user_id) + self.print_error("Could not enable %s", local_user.user_id) else: - self.counts['disabled'] += 1 - self.print(f'disabling user {local_user.user_id}') + self.counts["disabled"] += 1 + self.print(f"disabling user {local_user.user_id}") if not self.dry_run: disabled = self.client.disable_user(local_user.user_id) if not disabled: - self.print_error('Could not disable %s', local_user.user_id) + self.print_error("Could not disable %s", local_user.user_id) def sync_users(self) -> None: - """ Synchronizes local and cognito users. """ + """Synchronizes local and cognito users.""" # Get all remote and local users local_users = {user.user_id: user for user in User.all_objects.all()} local_user_ids = set(local_users.keys()) - remote_users = {user['Username']: user for user in self.client.list_users()} + remote_users = {user["Username"]: user for user in self.client.list_users()} remote_user_ids = set(remote_users.keys()) for user_id in local_user_ids.difference(remote_user_ids): @@ -116,14 +123,16 @@ def sync_users(self) -> None: self.update_user(local_users[user_id], remote_users[user_id]) def run(self) -> None: - """ Main entry point of command. """ + """Main entry point of command.""" # Clear data if self.clear: - self.print_warning('This action will delete all managed users from cognito', level=0) - confirm = input('are you sure you want to proceed? [yes/no]: ') - if confirm.lower() != 'yes': - self.print_warning('operation cancelled', level=0) + self.print_warning( + "This action will delete all managed users from cognito", level=0 + ) + confirm = input("are you sure you want to proceed? [yes/no]: ") + if confirm.lower() != "yes": + self.print_warning("operation cancelled", level=0) return self.clear_users() @@ -135,12 +144,12 @@ def run(self) -> None: for operation, count in self.counts.items(): if count: printed = True - self.print_success(f'{count} user(s) {operation}') + self.print_success(f"{count} user(s) {operation}") if not printed: - self.print_success('nothing to be done') + self.print_success("nothing to be done") if self.dry_run: - self.print_warning('dry run, nothing has been done') + self.print_warning("dry run, nothing has been done") class Command(CustomBaseCommand): @@ -149,14 +158,14 @@ class Command(CustomBaseCommand): def add_arguments(self, parser: CommandParser) -> None: super().add_arguments(parser) parser.add_argument( - '--clear', - action='store_true', - help='Delete existing users in cognito before synchronizing', + "--clear", + action="store_true", + help="Delete existing users in cognito before synchronizing", ) parser.add_argument( - '--dry-run', - action='store_true', - help='Dry run, abort transaction in the end', + "--dry-run", + action="store_true", + help="Dry run, abort transaction in the end", ) def handle(self, *args: Any, **options: Any) -> None: diff --git a/app/cognito/utils/client.py b/app/cognito/utils/client.py index a3ebef2d..b7330873 100644 --- a/app/cognito/utils/client.py +++ b/app/cognito/utils/client.py @@ -10,14 +10,14 @@ from mypy_boto3_cognito_idp.type_defs import UserTypeTypeDef -def user_attributes_to_dict(attributes: list['AttributeTypeTypeDef']) -> dict[str, str]: - """ Converts the attributes from a cognito user to a dict. """ +def user_attributes_to_dict(attributes: list["AttributeTypeTypeDef"]) -> dict[str, str]: + """Converts the attributes from a cognito user to a dict.""" - return {attr['Name']: attr['Value'] for attr in attributes} + return {attr["Name"]: attr["Value"] for attr in attributes} class Client: - """ A low level client for managing cognito users. + """A low level client for managing cognito users. Only manages users which have the managed flag defined. """ @@ -28,29 +28,31 @@ def __init__(self) -> None: self.managed_flag_name = settings.COGNITO_MANAGED_FLAG_NAME self.client = client("cognito-idp", endpoint_url=self.endpoint_url) - def list_users(self) -> list['UserTypeTypeDef']: - """ Get a list of managed users. """ + def list_users(self) -> list["UserTypeTypeDef"]: + """Get a list of managed users.""" response = self.client.list_users(UserPoolId=self.user_pool_id, Limit=60) - users = response['Users'] + users = response["Users"] - while response.get('PaginationToken'): + while response.get("PaginationToken"): response = self.client.list_users( - UserPoolId=self.user_pool_id, Limit=60, PaginationToken=response['PaginationToken'] + UserPoolId=self.user_pool_id, + Limit=60, + PaginationToken=response["PaginationToken"], ) - users.extend(response['Users']) + users.extend(response["Users"]) return [ - user for user in users - if user_attributes_to_dict(user['Attributes']).get(self.managed_flag_name) == 'true' + user + for user in users + if user_attributes_to_dict(user["Attributes"]).get(self.managed_flag_name) + == "true" ] def get_user( - self, - username: str, - return_unmanaged: bool = False - ) -> 'AdminGetUserResponseTypeDef | None': - """ Get the user with the given cognito username. + self, username: str, return_unmanaged: bool = False + ) -> "AdminGetUserResponseTypeDef | None": + """Get the user with the given cognito username. Returns None if the user does not exist or doesn't have the managed flag and return_unamanged is False. @@ -58,18 +60,20 @@ def get_user( """ try: - response = self.client.admin_get_user(UserPoolId=self.user_pool_id, Username=username) + response = self.client.admin_get_user( + UserPoolId=self.user_pool_id, Username=username + ) except self.client.exceptions.UserNotFoundException: return None if not return_unmanaged: - attributes = user_attributes_to_dict(response['UserAttributes']) - if attributes.get(self.managed_flag_name) != 'true': + attributes = user_attributes_to_dict(response["UserAttributes"]) + if attributes.get(self.managed_flag_name) != "true": return None return response def create_user(self, username: str, preferred_username: str, email: str) -> bool: - """ Create a new user. + """Create a new user. Returns False, if a (managed or unmanaged) user already exist. @@ -85,22 +89,19 @@ def create_user(self, username: str, preferred_username: str, email: str) -> boo self.client.admin_create_user( UserPoolId=self.user_pool_id, Username=username, - UserAttributes=[{ - "Name": "email", "Value": email - }, { - "Name": "email_verified", "Value": "true" - }, { - "Name": "preferred_username", "Value": preferred_username - }, { - "Name": self.managed_flag_name, "Value": "true" - }], - DesiredDeliveryMediums=['EMAIL'] + UserAttributes=[ + {"Name": "email", "Value": email}, + {"Name": "email_verified", "Value": "true"}, + {"Name": "preferred_username", "Value": preferred_username}, + {"Name": self.managed_flag_name, "Value": "true"}, + ], + DesiredDeliveryMediums=["EMAIL"], ) return True return False def delete_user(self, username: str) -> bool: - """ Delete the user with the given cognito username. + """Delete the user with the given cognito username. Returns False, if the user does not exist or doesn't have the managed flag. @@ -108,12 +109,14 @@ def delete_user(self, username: str) -> bool: user = self.get_user(username) if user is not None: - self.client.admin_delete_user(UserPoolId=self.user_pool_id, Username=username) + self.client.admin_delete_user( + UserPoolId=self.user_pool_id, Username=username + ) return True return False def update_user(self, username: str, preferred_username: str, email: str) -> bool: - """ Update the user with the given cognito username. + """Update the user with the given cognito username. Only updates changed attributes. @@ -129,22 +132,28 @@ def update_user(self, username: str, preferred_username: str, email: str) -> boo if user is None: return False - old_attributes = user_attributes_to_dict(user['UserAttributes']) + old_attributes = user_attributes_to_dict(user["UserAttributes"]) new_attributes: list[AttributeTypeTypeDef] = [] reset_password = False - if old_attributes.get('email') != email: - new_attributes.append({'Name': 'email', 'Value': email}) - new_attributes.append({'Name': 'email_verified', 'Value': 'true'}) + if old_attributes.get("email") != email: + new_attributes.append({"Name": "email", "Value": email}) + new_attributes.append({"Name": "email_verified", "Value": "true"}) reset_password = True - if old_attributes.get('preferred_username') != preferred_username: - new_attributes.append({'Name': 'preferred_username', 'Value': preferred_username}) + if old_attributes.get("preferred_username") != preferred_username: + new_attributes.append( + {"Name": "preferred_username", "Value": preferred_username} + ) if new_attributes: self.client.admin_update_user_attributes( - UserPoolId=self.user_pool_id, Username=username, UserAttributes=new_attributes + UserPoolId=self.user_pool_id, + Username=username, + UserAttributes=new_attributes, ) if reset_password: - self.client.admin_reset_user_password(UserPoolId=self.user_pool_id, Username=username) + self.client.admin_reset_user_password( + UserPoolId=self.user_pool_id, Username=username + ) return True diff --git a/app/config/api.py b/app/config/api.py index daf8f15f..8b5cc50b 100644 --- a/app/config/api.py +++ b/app/config/api.py @@ -38,9 +38,7 @@ def handle_django_validation_error( messages = extract_error_messages(exception) return api.create_response( request, - { - "code": status, "description": messages - }, + {"code": status, "description": messages}, status=status, ) @@ -50,9 +48,7 @@ def handle_django_validation_error( def handle_404_not_found(request: HttpRequest, exception: Http404) -> HttpResponse: return api.create_response( request, - { - "code": 404, "description": "Resource not found" - }, + {"code": 404, "description": "Resource not found"}, status=404, ) @@ -61,9 +57,7 @@ def handle_404_not_found(request: HttpRequest, exception: Http404) -> HttpRespon def handle_exception(request: HttpRequest, exception: Exception) -> HttpResponse: return api.create_response( request, - { - "code": 500, "description": "Internal Server Error" - }, + {"code": 500, "description": "Internal Server Error"}, status=500, ) @@ -72,20 +66,18 @@ def handle_exception(request: HttpRequest, exception: Exception) -> HttpResponse def handle_http_error(request: HttpRequest, exception: HttpError) -> HttpResponse: return api.create_response( request, - { - "code": exception.status_code, "description": exception.message - }, + {"code": exception.status_code, "description": exception.message}, status=exception.status_code, ) @api.exception_handler(AuthenticationError) -def handle_unauthorized(request: HttpRequest, exception: AuthenticationError) -> HttpResponse: +def handle_unauthorized( + request: HttpRequest, exception: AuthenticationError +) -> HttpResponse: return api.create_response( request, - { - "code": 401, "description": "Unauthorized" - }, + {"code": 401, "description": "Unauthorized"}, status=401, ) @@ -100,9 +92,7 @@ def handle_ninja_validation_error( return api.create_response( request, - { - "code": 422, "description": messages - }, + {"code": 422, "description": messages}, status=422, ) @@ -113,9 +103,7 @@ def handle_cognito_connection_error( ) -> HttpResponse: return api.create_response( request, - { - "code": 503, "description": "Service Unavailable" - }, + {"code": 503, "description": "Service Unavailable"}, status=503, ) diff --git a/app/config/asgi.py b/app/config/asgi.py index 3ce8f0fa..009b14cd 100644 --- a/app/config/asgi.py +++ b/app/config/asgi.py @@ -12,6 +12,6 @@ from django.core.asgi import get_asgi_application # default to the setting that's being created in DOCKERFILE -os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'app.settings') +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "app.settings") application = get_asgi_application() diff --git a/app/config/logging.py b/app/config/logging.py index 9168cc65..44426bcf 100644 --- a/app/config/logging.py +++ b/app/config/logging.py @@ -14,23 +14,23 @@ logger = getLogger(__name__) LogExtra = TypedDict( - 'LogExtra', + "LogExtra", { - 'http': { - 'request': { # noqa: F821 - 'method': str, # noqa: F821 - 'header': dict[str, str] # noqa: F821 + "http": { + "request": { # noqa: F821 + "method": str, # noqa: F821 + "header": dict[str, str], # noqa: F821 + }, + "response": { # noqa: F821 + "status_code": int, # noqa: F821 + "header": dict[str, str], # noqa: F821 }, - 'response': { # noqa: F821 - 'status_code': int, # noqa: F821 - 'header': dict[str, str] # noqa: F821 - } }, - 'url': { - 'path': str, # noqa: F821 - 'scheme': str # noqa: F821 - } - } + "url": { + "path": str, # noqa: F821 + "scheme": str, # noqa: F821 + }, + }, ) @@ -60,27 +60,28 @@ def generate_log_extra(request: HttpRequest, response: HttpResponse) -> LogExtra dict: dict of extras """ return { - 'http': { - 'request': { - 'method': request.method or 'UNKNOWN', - 'header': { + "http": { + "request": { + "method": request.method or "UNKNOWN", + "header": { k.lower(): v for k, v in request.headers.items() if k.lower() in settings.LOG_ALLOWED_HEADERS - } + }, }, - 'response': { - 'status_code': response.status_code, - 'header': { + "response": { + "status_code": response.status_code, + "header": { k.lower(): v for k, v in response.headers.items() if k.lower() in settings.LOG_ALLOWED_HEADERS }, - } + }, + }, + "url": { + "path": request.path or "UNKNOWN", + "scheme": request.scheme or "UNKNOWN", }, - 'url': { - 'path': request.path or 'UNKNOWN', 'scheme': request.scheme or 'UNKNOWN' - } } @@ -108,16 +109,18 @@ def create_response( "Response %s on %s", response.status_code, # parameter for %s request.path, # parameter for %s - extra=generate_log_extra(request, response) + extra=generate_log_extra(request, response), ) elif response.status_code >= 400 and response.status_code < 500: logger.warning( "Response %s on %s", response.status_code, # parameter for %s request.path, # parameter for %s - extra=generate_log_extra(request, response) + extra=generate_log_extra(request, response), ) else: - logger.exception(repr(sys.exc_info()[1]), extra=generate_log_extra(request, response)) + logger.exception( + repr(sys.exc_info()[1]), extra=generate_log_extra(request, response) + ) return response diff --git a/app/config/settings_base.py b/app/config/settings_base.py index 9147369e..5d72e278 100644 --- a/app/config/settings_base.py +++ b/app/config/settings_base.py @@ -25,119 +25,117 @@ # See https://docs.djangoproject.com/en/5.0/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = env.str('SECRET_KEY', default=None) +SECRET_KEY = env.str("SECRET_KEY", default=None) # SECURITY: # https://docs.djangoproject.com/en/dev/ref/settings/#secure-proxy-ssl-header -SECURE_PROXY_SSL_HEADER = ('HTTP_CLOUDFRONT_FORWARDED_PROTO', 'https') +SECURE_PROXY_SSL_HEADER = ("HTTP_CLOUDFRONT_FORWARDED_PROTO", "https") # SECURITY WARNING: don't run with debug turned on in production! DEBUG = False -ALLOWED_HOSTS = env.list('ALLOWED_HOSTS', default=[]) +ALLOWED_HOSTS = env.list("ALLOWED_HOSTS", default=[]) # Application definition INSTALLED_APPS = [ - 'django.contrib.admin', - 'django.contrib.auth', - 'django.contrib.contenttypes', - 'django.contrib.sessions', - 'django.contrib.staticfiles', - 'django.contrib.messages', - 'provider', - 'distributions', - 'access', - 'cognito', - 'bod', - 'support' + "django.contrib.admin", + "django.contrib.auth", + "django.contrib.contenttypes", + "django.contrib.sessions", + "django.contrib.staticfiles", + "django.contrib.messages", + "provider", + "distributions", + "access", + "cognito", + "bod", + "support", ] MIDDLEWARE = [ - 'django.middleware.security.SecurityMiddleware', - 'whitenoise.middleware.WhiteNoiseMiddleware', - 'django.contrib.sessions.middleware.SessionMiddleware', - 'django.middleware.common.CommonMiddleware', - 'django.middleware.csrf.CsrfViewMiddleware', - 'django.contrib.auth.middleware.AuthenticationMiddleware', - 'django.contrib.messages.middleware.MessageMiddleware', - 'django.middleware.clickjacking.XFrameOptionsMiddleware', + "django.middleware.security.SecurityMiddleware", + "whitenoise.middleware.WhiteNoiseMiddleware", + "django.contrib.sessions.middleware.SessionMiddleware", + "django.middleware.common.CommonMiddleware", + "django.middleware.csrf.CsrfViewMiddleware", + "django.contrib.auth.middleware.AuthenticationMiddleware", + "django.contrib.messages.middleware.MessageMiddleware", + "django.middleware.clickjacking.XFrameOptionsMiddleware", ] -ROOT_URLCONF = 'config.urls' +ROOT_URLCONF = "config.urls" TEMPLATES = [ { - 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'DIRS': [], - 'APP_DIRS': True, - 'OPTIONS': { - 'context_processors': [ - 'django.template.context_processors.debug', - 'django.template.context_processors.request', - 'django.contrib.auth.context_processors.auth', - 'django.contrib.messages.context_processors.messages', + "BACKEND": "django.template.backends.django.DjangoTemplates", + "DIRS": [], + "APP_DIRS": True, + "OPTIONS": { + "context_processors": [ + "django.template.context_processors.debug", + "django.template.context_processors.request", + "django.contrib.auth.context_processors.auth", + "django.contrib.messages.context_processors.messages", ], }, }, ] -WSGI_APPLICATION = 'wsgi.application' +WSGI_APPLICATION = "wsgi.application" # Database # https://docs.djangoproject.com/en/5.0/ref/settings/#databases DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.postgresql', - 'NAME': env.str('DB_NAME', 'service_control'), - 'USER': env.str('DB_USER', 'service_control'), - 'PASSWORD': env.str('DB_PW', 'service_control'), - 'HOST': env.str('DB_HOST', 'service_control'), - 'PORT': env.str('DB_PORT', "5432"), - 'TEST': { - 'NAME': env.str('DB_NAME_TEST', 'test_service_control'), - } - }, - 'bod': { - 'ENGINE': 'django.db.backends.postgresql', - 'NAME': env.str('BOD_NAME', 'service_control'), - 'USER': env.str('BOD_USER', 'service_control'), - 'PASSWORD': env.str('BOD_PW', 'service_control'), - 'HOST': env.str('BOD_HOST', 'service_control'), - 'PORT': env.str('BOD_PORT', "5432"), - 'OPTIONS': { - 'options': '-c search_path=public' + "default": { + "ENGINE": "django.db.backends.postgresql", + "NAME": env.str("DB_NAME", "service_control"), + "USER": env.str("DB_USER", "service_control"), + "PASSWORD": env.str("DB_PW", "service_control"), + "HOST": env.str("DB_HOST", "service_control"), + "PORT": env.str("DB_PORT", "5432"), + "TEST": { + "NAME": env.str("DB_NAME_TEST", "test_service_control"), }, - } + }, + "bod": { + "ENGINE": "django.db.backends.postgresql", + "NAME": env.str("BOD_NAME", "service_control"), + "USER": env.str("BOD_USER", "service_control"), + "PASSWORD": env.str("BOD_PW", "service_control"), + "HOST": env.str("BOD_HOST", "service_control"), + "PORT": env.str("BOD_PORT", "5432"), + "OPTIONS": {"options": "-c search_path=public"}, + }, } -DATABASE_ROUTERS = ['utils.database_router.CustomRouter'] +DATABASE_ROUTERS = ["utils.database_router.CustomRouter"] # Password validation # https://docs.djangoproject.com/en/5.0/ref/settings/#auth-password-validators AUTH_PASSWORD_VALIDATORS = [ { - 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator", }, { - 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', + "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator", }, { - 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', + "NAME": "django.contrib.auth.password_validation.CommonPasswordValidator", }, { - 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator", }, ] # Internationalization # https://docs.djangoproject.com/en/5.0/topics/i18n/ -LANGUAGE_CODE = 'en-us' +LANGUAGE_CODE = "en-us" -TIME_ZONE = 'UTC' +TIME_ZONE = "UTC" USE_I18N = True @@ -146,39 +144,41 @@ # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/5.0/howto/static-files/ -STATIC_HOST = env.str('DJANGO_STATIC_HOST', '') -STATIC_URL = f'{STATIC_HOST}/static/' +STATIC_HOST = env.str("DJANGO_STATIC_HOST", "") +STATIC_URL = f"{STATIC_HOST}/static/" # Default primary key field type # https://docs.djangoproject.com/en/5.0/ref/settings/#default-auto-field -DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' +DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" # Cognito -COGNITO_ENDPOINT_URL = env.str('COGNITO_ENDPOINT_URL', 'http://localhost:9229') -COGNITO_POOL_ID = env.str('COGNITO_POOL_ID', 'local') -COGNITO_MANAGED_FLAG_NAME = env.str('COGNITO_MANAGED_FLAG_NAME', 'dev:custom:managed_by_service') +COGNITO_ENDPOINT_URL = env.str("COGNITO_ENDPOINT_URL", "http://localhost:9229") +COGNITO_POOL_ID = env.str("COGNITO_POOL_ID", "local") +COGNITO_MANAGED_FLAG_NAME = env.str( + "COGNITO_MANAGED_FLAG_NAME", "dev:custom:managed_by_service" +) # Testing TESTING = False # nanoid -SHORT_ID_SIZE = env.int('SHORT_ID_SIZE', 12) -SHORT_ID_ALPHABET = env.str('SHORT_ID_ALPHABET', '0123456789abcdefghijklmnopqrstuvwxyz') +SHORT_ID_SIZE = env.int("SHORT_ID_SIZE", 12) +SHORT_ID_ALPHABET = env.str("SHORT_ID_ALPHABET", "0123456789abcdefghijklmnopqrstuvwxyz") # Read configuration from file def get_logging_config() -> dict[str, object]: - '''Read logging configuration + """Read logging configuration Read and parse the yaml logging configuration file passed in the environment variable LOGGING_CFG and return it as dictionary Note: LOGGING_CFG is relative to the root of the repo - ''' - log_config_file = env('LOGGING_CFG', default='config/logging-cfg-local.yaml') - if log_config_file.lower() in ['none', '0', '', 'false', 'no']: + """ + log_config_file = env("LOGGING_CFG", default="config/logging-cfg-local.yaml") + if log_config_file.lower() in ["none", "0", "", "false", "no"]: return {} log_config = {} - with open(BASE_DIR / log_config_file, 'rt', encoding="utf-8") as fd: + with open(BASE_DIR / log_config_file, "rt", encoding="utf-8") as fd: log_config = yaml.safe_load(os.path.expandvars(fd.read())) return log_config or {} @@ -187,7 +187,6 @@ def get_logging_config() -> dict[str, object]: # list of headers that are allowed to be logged _DEFAULT_LOG_ALLOWED_HEADERS = [ - # Standard headers "accept", "accept-encoding", @@ -213,23 +212,20 @@ def get_logging_config() -> dict[str, object]: "x-forwarded-host", "x-forwarded-port", "x-forwarded-proto", - - # Cloudfront headers + # Cloudfront headers "cloudfront-is-android-viewer", "cloudfront-is-desktop-viewer", "cloudfront-is-ios-viewer", "cloudfront-is-mobile-viewer", "cloudfront-is-smarttv-viewer", "cloudfront-is-tablet-viewer", - - # PPBGDI headers + # PPBGDI headers "x-e2e-testing", # API GW Headers - "geoadmin-authenticated" - "geoadmin-username", - "apigw-requestid" + "geoadmin-authenticatedgeoadmin-username", + "apigw-requestid", ] LOG_ALLOWED_HEADERS = [ str(header).lower() - for header in env.list('LOG_ALLOWED_HEADERS', default=_DEFAULT_LOG_ALLOWED_HEADERS) + for header in env.list("LOG_ALLOWED_HEADERS", default=_DEFAULT_LOG_ALLOWED_HEADERS) ] diff --git a/app/config/settings_dev.py b/app/config/settings_dev.py index 2b7f4449..268cdf31 100644 --- a/app/config/settings_dev.py +++ b/app/config/settings_dev.py @@ -5,13 +5,13 @@ env = environ.Env() # Override debug if given by the env -if env.bool('DEBUG', None): - DEBUG = env.bool('DEBUG') +if env.bool("DEBUG", None): + DEBUG = env.bool("DEBUG") if DEBUG: - INSTALLED_APPS += ['django_extensions', 'debug_toolbar'] # noqa: F405 + INSTALLED_APPS += ["django_extensions", "debug_toolbar"] # noqa: F405 if DEBUG: MIDDLEWARE = [ - 'debug_toolbar.middleware.DebugToolbarMiddleware', + "debug_toolbar.middleware.DebugToolbarMiddleware", ] + MIDDLEWARE # noqa: F405 diff --git a/app/config/settings_prod.py b/app/config/settings_prod.py index c34ac3ec..d27c6c6a 100644 --- a/app/config/settings_prod.py +++ b/app/config/settings_prod.py @@ -5,7 +5,7 @@ # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/5.0/howto/static-files/ -STATIC_ROOT = BASE_DIR / 'var' / 'www' / 'service_control' / 'static_files' # noqa: F405 +STATIC_ROOT = BASE_DIR / "var" / "www" / "service_control" / "static_files" # noqa: F405 STORAGES = { "staticfiles": { "BACKEND": "whitenoise.storage.CompressedManifestStaticFilesStorage", diff --git a/app/config/settings_test.py b/app/config/settings_test.py index ef10c339..0a71fd67 100644 --- a/app/config/settings_test.py +++ b/app/config/settings_test.py @@ -3,9 +3,9 @@ from .settings_dev import * # noqa: F403 TESTING = True -SECRET_KEY = 'django-insecure-6-72r#zx=sv6v@-4k@uf1gv32me@%yr*oqa*fu8&5l&a!ws)5#' # nosec B105 +SECRET_KEY = "django-insecure-6-72r#zx=sv6v@-4k@uf1gv32me@%yr*oqa*fu8&5l&a!ws)5#" # nosec B105 # The tests use the default database, remove the BOD so that django doesn't try to create it -del DATABASES['bod'] # noqa: F405 +del DATABASES["bod"] # noqa: F405 os.environ["NINJA_SKIP_REGISTRY"] = "yes" diff --git a/app/config/urls.py b/app/config/urls.py index a877ec91..2e4c0f72 100644 --- a/app/config/urls.py +++ b/app/config/urls.py @@ -14,6 +14,7 @@ 1. Import the include() function: from django.urls import include, path 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) """ + from django.contrib import admin from django.urls import path @@ -21,7 +22,7 @@ from .api import root urlpatterns = [ - path('', root.urls), - path('api/v1/', api.urls), - path('admin/', admin.site.urls), + path("", root.urls), + path("api/v1/", api.urls), + path("admin/", admin.site.urls), ] diff --git a/app/distributions/admin.py b/app/distributions/admin.py index 7d39a6e5..96c27b52 100644 --- a/app/distributions/admin.py +++ b/app/distributions/admin.py @@ -11,27 +11,27 @@ @admin.register(Attribution) class AttributionAdmin(admin.ModelAdmin): # type:ignore[type-arg] - '''Admin View for Attribution''' + """Admin View for Attribution""" - list_display = ('attribution_id', 'name_en', 'provider') - list_filter = (('provider', admin.RelatedOnlyFieldListFilter),) - readonly_fields = ('created', 'updated') + list_display = ("attribution_id", "name_en", "provider") + list_filter = (("provider", admin.RelatedOnlyFieldListFilter),) + readonly_fields = ("created", "updated") @admin.register(Dataset) class DatasetAdmin(admin.ModelAdmin): # type:ignore[type-arg] - '''Admin View for Dataset''' + """Admin View for Dataset""" - list_display = ('dataset_id', 'title_en', 'provider') - list_filter = (('provider', admin.RelatedOnlyFieldListFilter),) - readonly_fields = ('created', 'updated') + list_display = ("dataset_id", "title_en", "provider") + list_filter = (("provider", admin.RelatedOnlyFieldListFilter),) + readonly_fields = ("created", "updated") def get_form( self, request: HttpRequest, obj: Dataset | None = None, change: bool = False, - **kwargs: Any + **kwargs: Any, ) -> Any: form = super().get_form(request, obj=obj, change=change, **kwargs) if obj is not None: @@ -47,11 +47,11 @@ def _add_geocat_url_help_text(self, form: Any, geocat_id: str) -> None: @admin.register(PackageDistribution) class PackageDistributionAdmin(admin.ModelAdmin): # type:ignore[type-arg] - '''Admin View for Package Distribution''' + """Admin View for Package Distribution""" - list_display = ('package_distribution_id', 'managed_by_stac', 'dataset') + list_display = ("package_distribution_id", "managed_by_stac", "dataset") list_filter = ( - 'managed_by_stac', - ('dataset__provider', admin.RelatedOnlyFieldListFilter), + "managed_by_stac", + ("dataset__provider", admin.RelatedOnlyFieldListFilter), ) - readonly_fields = ('created', 'updated') + readonly_fields = ("created", "updated") diff --git a/app/distributions/api.py b/app/distributions/api.py index 8f4e0586..0c0a27ce 100644 --- a/app/distributions/api.py +++ b/app/distributions/api.py @@ -18,7 +18,9 @@ router = Router() -def attribution_to_response(model: Attribution, lang: LanguageCode) -> AttributionSchema: +def attribution_to_response( + model: Attribution, lang: LanguageCode +) -> AttributionSchema: """ Transforms the given model using the given language into a response object. """ @@ -79,12 +81,10 @@ def dataset_to_response(model: Dataset, lang: LanguageCode) -> DatasetSchema: "attributions/{attribution_id}", response={200: AttributionSchema}, exclude_none=True, - auth=PermissionAuth('distributions.view_attribution') + auth=PermissionAuth("distributions.view_attribution"), ) def attribution( - request: HttpRequest, - attribution_id: str, - lang: LanguageCode | None = None + request: HttpRequest, attribution_id: str, lang: LanguageCode | None = None ) -> AttributionSchema: """ Get the attribution with the given ID, return translatable fields in the given language. @@ -141,10 +141,11 @@ def attribution( "attributions", response={200: AttributionListSchema}, exclude_none=True, - auth=PermissionAuth('distributions.view_attribution') + auth=PermissionAuth("distributions.view_attribution"), ) -def attributions(request: HttpRequest, - lang: LanguageCode | None = None) -> dict[str, list[AttributionSchema]]: +def attributions( + request: HttpRequest, lang: LanguageCode | None = None +) -> dict[str, list[AttributionSchema]]: """ Get all attributions, return translatable fields in the given language. @@ -162,7 +163,7 @@ def attributions(request: HttpRequest, "datasets/{dataset_id}", response={200: DatasetSchema}, exclude_none=True, - auth=PermissionAuth('distributions.view_dataset') + auth=PermissionAuth("distributions.view_dataset"), ) def dataset( request: HttpRequest, dataset_id: str, lang: LanguageCode | None = None @@ -180,10 +181,11 @@ def dataset( "datasets", response={200: DatasetListSchema}, exclude_none=True, - auth=PermissionAuth('distributions.view_dataset') + auth=PermissionAuth("distributions.view_dataset"), ) -def datasets(request: HttpRequest, - lang: LanguageCode | None = None) -> dict[str, list[DatasetSchema]]: +def datasets( + request: HttpRequest, lang: LanguageCode | None = None +) -> dict[str, list[DatasetSchema]]: """ Get all datasets. diff --git a/app/distributions/apps.py b/app/distributions/apps.py index abe197a2..bc89d2a3 100644 --- a/app/distributions/apps.py +++ b/app/distributions/apps.py @@ -2,5 +2,5 @@ class DistributionsConfig(AppConfig): - default_auto_field = 'django.db.models.BigAutoField' - name = 'distributions' + default_auto_field = "django.db.models.BigAutoField" + name = "distributions" diff --git a/app/distributions/management/commands/stac_sync.py b/app/distributions/management/commands/stac_sync.py index 0cc0a871..a756a0e6 100644 --- a/app/distributions/management/commands/stac_sync.py +++ b/app/distributions/management/commands/stac_sync.py @@ -20,46 +20,52 @@ from django.core.management.base import CommandParser from django.db import transaction -Counter = TypedDict('Counter', {'added': int, 'cleared': int, 'removed': int, 'updated': int}) -Operation = Literal['added', 'cleared', 'removed', 'updated'] +Counter = TypedDict( + "Counter", {"added": int, "cleared": int, "removed": int, "updated": int} +) +Operation = Literal["added", "cleared", "removed", "updated"] class Handler(CommandHandler): - - def __init__(self, command: CustomBaseCommand, options: dict['str', Any]): + def __init__(self, command: CustomBaseCommand, options: dict["str", Any]): super().__init__(command, options) - self.clear = options['clear'] - self.dry_run = options['dry_run'] - self.similarity = options['similarity'] - self.url = options['url'] - self.endpoint = options['endpoint'] - self.default_dataset = options['default_dataset'] - self.create_default_dataset = not options['no_create_default_dataset'] + self.clear = options["clear"] + self.dry_run = options["dry_run"] + self.similarity = options["similarity"] + self.url = options["url"] + self.endpoint = options["endpoint"] + self.default_dataset = options["default_dataset"] + self.create_default_dataset = not options["no_create_default_dataset"] self.counts: dict[str, Counter] = {} - def increment_counter(self, model_name: str, operation: Operation, value: int = 1) -> None: - """ Updates internal counters of operations on models. """ + def increment_counter( + self, model_name: str, operation: Operation, value: int = 1 + ) -> None: + """Updates internal counters of operations on models.""" - self.counts.setdefault(model_name, {'added': 0, 'cleared': 0, 'removed': 0, 'updated': 0}) + self.counts.setdefault( + model_name, {"added": 0, "cleared": 0, "removed": 0, "updated": 0} + ) self.counts[model_name][operation] += value def clear_package_distributions(self) -> None: - """ Remove existing package distributions previously imported from STAC. """ + """Remove existing package distributions previously imported from STAC.""" _, cleared = PackageDistribution.objects.filter(_legacy_imported=True).delete() for model_class, count in cleared.items(): - model_name = model_class.split('.')[-1].lower() - self.increment_counter(model_name, 'cleared', count) + model_name = model_class.split(".")[-1].lower() + self.increment_counter(model_name, "cleared", count) def ensure_default_dataset(self) -> None: - """ Create the given default dataset if required and not yet available. + """Create the given default dataset if required and not yet available. This will create a provider and attribution with the same ID as the dataset. """ if ( - not self.create_default_dataset or not self.default_dataset or - Dataset.objects.filter(dataset_id=self.default_dataset).first() + not self.create_default_dataset + or not self.default_dataset + or Dataset.objects.filter(dataset_id=self.default_dataset).first() ): return @@ -76,9 +82,13 @@ def ensure_default_dataset(self) -> None: acronym_en="#Missing", ) - attribution = Attribution.objects.filter(attribution_id=self.default_dataset).first() + attribution = Attribution.objects.filter( + attribution_id=self.default_dataset + ).first() if not attribution: - self.print(f"Added attribution '{self.default_dataset}' for default dataset") + self.print( + f"Added attribution '{self.default_dataset}' for default dataset" + ) attribution = Attribution.objects.create( attribution_id=self.default_dataset, name_de="#Missing", @@ -87,7 +97,7 @@ def ensure_default_dataset(self) -> None: description_de="#Missing", description_fr="#Missing", description_en="#Missing", - provider=provider + provider=provider, ) self.print(f"Added default dataset '{self.default_dataset}'") @@ -101,24 +111,26 @@ def ensure_default_dataset(self) -> None: description_en="#Missing", geocat_id="#Missing", provider=provider, - attribution=attribution + attribution=attribution, ) def update_package_distribution( self, collection_id: str, managed_by_stac: bool ) -> Dataset | None: - """ Create or update the package distribution with the given ID. """ - managed = 'managed' if managed_by_stac else 'unmanaged' + """Create or update the package distribution with the given ID.""" + managed = "managed" if managed_by_stac else "unmanaged" # Get dataset dataset = ( - Dataset.objects.filter(dataset_id=collection_id).first() or - Dataset.objects.filter(dataset_id=self.default_dataset).first() + Dataset.objects.filter(dataset_id=collection_id).first() + or Dataset.objects.filter(dataset_id=self.default_dataset).first() ) if not dataset: self.print_warning("No dataset for collection id '%s'", collection_id) if self.default_dataset: - self.print_warning("Default dataset '%s' does not exist", self.default_dataset) + self.print_warning( + "Default dataset '%s' does not exist", self.default_dataset + ) return None # Get or create package distribution @@ -130,26 +142,26 @@ def update_package_distribution( package_distribution_id=collection_id, _legacy_imported=True, managed_by_stac=managed_by_stac, - dataset=dataset + dataset=dataset, ) - self.increment_counter('package_distribution', 'added') + self.increment_counter("package_distribution", "added") self.print(f"Added package distribution '{collection_id}' ({managed})") # Update package distribution if ( - package_distribution.managed_by_stac != managed_by_stac or - package_distribution.dataset != dataset + package_distribution.managed_by_stac != managed_by_stac + or package_distribution.dataset != dataset ): package_distribution.managed_by_stac = managed_by_stac package_distribution.dataset = dataset package_distribution.save() - self.increment_counter('package_distribution', 'updated') + self.increment_counter("package_distribution", "updated") self.print(f"Updated package distribution '{collection_id}' ({managed})") return dataset def import_package_distributions(self) -> None: - """ Import package distributions from STAC. + """Import package distributions from STAC. This function adds new package distributions, updates existing ones and removes orphans. @@ -173,16 +185,16 @@ def import_package_distributions(self) -> None: # Get unmanaged collections from the HTML root response = get(self.url, timeout=60) - element = BeautifulSoup(response.text, 'html.parser').find('div', id="data") + element = BeautifulSoup(response.text, "html.parser").find("div", id="data") if not element: raise ValueError(f"Error parsing {self.url}") - for line in split(r'\r?\n', element.text.strip()): + for line in split(r"\r?\n", element.text.strip()): line = line.strip() if not line: continue - values = line.split(' ') + values = line.split(" ") if len(values) == 0: continue @@ -197,13 +209,13 @@ def import_package_distributions(self) -> None: processed.add(collection_id) # Remove orphaned package distributions - orphans = PackageDistribution.objects.filter( - _legacy_imported=True - ).exclude(package_distribution_id__in=processed,) + orphans = PackageDistribution.objects.filter(_legacy_imported=True).exclude( + package_distribution_id__in=processed, + ) _, removed = orphans.delete() for model_class, count in removed.items(): - model_name = model_class.split('.')[-1].lower() - self.increment_counter(model_name, 'removed', count) + model_name = model_class.split(".")[-1].lower() + self.increment_counter(model_name, "removed", count) def check_provider(self, collection: Collection, dataset: Dataset) -> None: """Checks whether the provider in the STAC collection matches the provider in the dataset @@ -217,22 +229,26 @@ def check_provider(self, collection: Collection, dataset: Dataset) -> None: if not providers: self.print_warning("Collection '%s' has no providers", collection_id) elif len(providers) > 1: - self.print_warning("Collection '%s' has more than one provider", collection_id) + self.print_warning( + "Collection '%s' has more than one provider", collection_id + ) else: name_collection = providers[0].name name_dataset = dataset.provider.name_en if name_dataset != name_collection: - similarity = SequenceMatcher(None, name_collection, name_dataset).ratio() + similarity = SequenceMatcher( + None, name_collection, name_dataset + ).ratio() if similarity < self.similarity: self.print_warning( "Provider in collection and dataset differ (%.2f): '%s' / '%s'", similarity, name_collection, - name_dataset + name_dataset, ) def run(self) -> None: - """ Main entry point of command. """ + """Main entry point of command.""" with transaction.atomic(): # Clear data @@ -266,29 +282,37 @@ class Command(CustomBaseCommand): def add_arguments(self, parser: CommandParser) -> None: super().add_arguments(parser) parser.add_argument( - "--clear", action="store_true", help="Delete existing objects before importing" + "--clear", + action="store_true", + help="Delete existing objects before importing", ) parser.add_argument( - "--dry-run", action="store_true", help="Dry run, abort transaction in the end" + "--dry-run", + action="store_true", + help="Dry run, abort transaction in the end", ) parser.add_argument( "--similarity", type=float, default=1.0, - help="Similarity threshold to use when comparing providers" + help="Similarity threshold to use when comparing providers", + ) + parser.add_argument( + "--url", type=str, default="https://data.geo.admin.ch", help="STAC URL" + ) + parser.add_argument( + "--endpoint", type=str, default="/api/stac/v1", help="STAC endpoint" ) - parser.add_argument("--url", type=str, default="https://data.geo.admin.ch", help="STAC URL") - parser.add_argument("--endpoint", type=str, default="/api/stac/v1", help="STAC endpoint") parser.add_argument( "--default-dataset", type=str, default="", - help="Add packages with missing dataset to this dataset" + help="Add packages with missing dataset to this dataset", ) parser.add_argument( "--no-create-default-dataset", action="store_true", - help="Do not create the default dataset if needed" + help="Do not create the default dataset if needed", ) def handle(self, *args: Any, **options: Any) -> None: diff --git a/app/distributions/migrations/0001_initial.py b/app/distributions/migrations/0001_initial.py index 2a074baf..d9dc8c08 100644 --- a/app/distributions/migrations/0001_initial.py +++ b/app/distributions/migrations/0001_initial.py @@ -6,49 +6,70 @@ class Migration(migrations.Migration): - initial = True dependencies = [ - ('provider', '0004_alter_provider_acronym_de_alter_provider_acronym_en_and_more'), + ( + "provider", + "0004_alter_provider_acronym_de_alter_provider_acronym_en_and_more", + ), ] operations = [ migrations.CreateModel( - name='Attribution', + name="Attribution", fields=[ ( - 'id', + "id", models.BigAutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name='ID' - ) + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("name_de", models.CharField(verbose_name="Name (German)")), + ("name_fr", models.CharField(verbose_name="Name (French)")), + ("name_en", models.CharField(verbose_name="Name (English)")), + ( + "name_it", + models.CharField(blank=True, verbose_name="Name (Italian)"), + ), + ( + "name_rm", + models.CharField(blank=True, verbose_name="Name (Romansh)"), + ), + ( + "description_de", + models.CharField(verbose_name="Description (German)"), + ), + ( + "description_fr", + models.CharField(verbose_name="Description (French)"), + ), + ( + "description_en", + models.CharField(verbose_name="Description (English)"), ), - ('name_de', models.CharField(verbose_name='Name (German)')), - ('name_fr', models.CharField(verbose_name='Name (French)')), - ('name_en', models.CharField(verbose_name='Name (English)')), - ('name_it', models.CharField(blank=True, verbose_name='Name (Italian)')), - ('name_rm', models.CharField(blank=True, verbose_name='Name (Romansh)')), - ('description_de', models.CharField(verbose_name='Description (German)')), - ('description_fr', models.CharField(verbose_name='Description (French)')), - ('description_en', models.CharField(verbose_name='Description (English)')), ( - 'description_it', - models.CharField(blank=True, verbose_name='Description (Italian)') + "description_it", + models.CharField(blank=True, verbose_name="Description (Italian)"), ), ( - 'description_rm', - models.CharField(blank=True, verbose_name='Description (Romansh)') + "description_rm", + models.CharField(blank=True, verbose_name="Description (Romansh)"), ), ( - 'provider', + "provider", models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, to='provider.provider' - ) + on_delete=django.db.models.deletion.CASCADE, + to="provider.provider", + ), ), ], options={ - 'verbose_name': 'attribution', - 'verbose_name_plural': 'attributions', + "verbose_name": "attribution", + "verbose_name_plural": "attributions", }, ), ] diff --git a/app/distributions/migrations/0002_dataset.py b/app/distributions/migrations/0002_dataset.py index 8516ebf9..40e5b504 100644 --- a/app/distributions/migrations/0002_dataset.py +++ b/app/distributions/migrations/0002_dataset.py @@ -6,41 +6,54 @@ class Migration(migrations.Migration): - dependencies = [ - ('distributions', '0001_initial'), - ('provider', '0004_alter_provider_acronym_de_alter_provider_acronym_en_and_more'), + ("distributions", "0001_initial"), + ( + "provider", + "0004_alter_provider_acronym_de_alter_provider_acronym_en_and_more", + ), ] operations = [ migrations.CreateModel( - name='Dataset', + name="Dataset", fields=[ ( - 'id', + "id", models.BigAutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name='ID' - ) + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("slug", models.SlugField(verbose_name="Slug")), + ( + "created", + models.DateTimeField(auto_now_add=True, verbose_name="Created"), + ), + ( + "updated", + models.DateTimeField(auto_now=True, verbose_name="Updated"), ), - ('slug', models.SlugField(verbose_name='Slug')), - ('created', models.DateTimeField(auto_now_add=True, verbose_name='Created')), - ('updated', models.DateTimeField(auto_now=True, verbose_name='Updated')), ( - 'attribution', + "attribution", models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, to='distributions.attribution' - ) + on_delete=django.db.models.deletion.CASCADE, + to="distributions.attribution", + ), ), ( - 'provider', + "provider", models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, to='provider.provider' - ) + on_delete=django.db.models.deletion.CASCADE, + to="provider.provider", + ), ), ], options={ - 'verbose_name': 'dataset', - 'verbose_name_plural': 'datasets', + "verbose_name": "dataset", + "verbose_name_plural": "datasets", }, ), ] diff --git a/app/distributions/migrations/0003_alter_attribution_options_alter_dataset_options.py b/app/distributions/migrations/0003_alter_attribution_options_alter_dataset_options.py index de4204ee..4bbfa901 100644 --- a/app/distributions/migrations/0003_alter_attribution_options_alter_dataset_options.py +++ b/app/distributions/migrations/0003_alter_attribution_options_alter_dataset_options.py @@ -4,18 +4,17 @@ class Migration(migrations.Migration): - dependencies = [ - ('distributions', '0002_dataset'), + ("distributions", "0002_dataset"), ] operations = [ migrations.AlterModelOptions( - name='attribution', + name="attribution", options={}, ), migrations.AlterModelOptions( - name='dataset', + name="dataset", options={}, ), ] diff --git a/app/distributions/migrations/0004_alter_attribution_description_it_and_more.py b/app/distributions/migrations/0004_alter_attribution_description_it_and_more.py index 2c9562bf..b43f31e7 100644 --- a/app/distributions/migrations/0004_alter_attribution_description_it_and_more.py +++ b/app/distributions/migrations/0004_alter_attribution_description_it_and_more.py @@ -5,30 +5,37 @@ class Migration(migrations.Migration): - dependencies = [ - ('distributions', '0003_alter_attribution_options_alter_dataset_options'), + ("distributions", "0003_alter_attribution_options_alter_dataset_options"), ] operations = [ migrations.AlterField( - model_name='attribution', - name='description_it', - field=models.CharField(blank=True, null=True, verbose_name='Description (Italian)'), + model_name="attribution", + name="description_it", + field=models.CharField( + blank=True, null=True, verbose_name="Description (Italian)" + ), ), migrations.AlterField( - model_name='attribution', - name='description_rm', - field=models.CharField(blank=True, null=True, verbose_name='Description (Romansh)'), + model_name="attribution", + name="description_rm", + field=models.CharField( + blank=True, null=True, verbose_name="Description (Romansh)" + ), ), migrations.AlterField( - model_name='attribution', - name='name_it', - field=models.CharField(blank=True, null=True, verbose_name='Name (Italian)'), + model_name="attribution", + name="name_it", + field=models.CharField( + blank=True, null=True, verbose_name="Name (Italian)" + ), ), migrations.AlterField( - model_name='attribution', - name='name_rm', - field=models.CharField(blank=True, null=True, verbose_name='Name (Romansh)'), + model_name="attribution", + name="name_rm", + field=models.CharField( + blank=True, null=True, verbose_name="Name (Romansh)" + ), ), ] diff --git a/app/distributions/migrations/0005_dataset__legacy_id_alter_dataset_slug_and_more.py b/app/distributions/migrations/0005_dataset__legacy_id_alter_dataset_slug_and_more.py index 6c6fd571..c0277f3b 100644 --- a/app/distributions/migrations/0005_dataset__legacy_id_alter_dataset_slug_and_more.py +++ b/app/distributions/migrations/0005_dataset__legacy_id_alter_dataset_slug_and_more.py @@ -5,35 +5,34 @@ class Migration(migrations.Migration): - dependencies = [ - ('distributions', '0004_alter_attribution_description_it_and_more'), + ("distributions", "0004_alter_attribution_description_it_and_more"), ] operations = [ migrations.AddField( - model_name='dataset', - name='_legacy_id', + model_name="dataset", + name="_legacy_id", field=models.IntegerField( blank=True, - help_text='This field is used to track objects imported from the BOD', + help_text="This field is used to track objects imported from the BOD", null=True, - verbose_name='Legacy ID' + verbose_name="Legacy ID", ), ), migrations.AlterField( - model_name='dataset', - name='slug', - field=models.SlugField(max_length=100, verbose_name='Slug'), + model_name="dataset", + name="slug", + field=models.SlugField(max_length=100, verbose_name="Slug"), ), migrations.AddField( - model_name='attribution', - name='_legacy_id', + model_name="attribution", + name="_legacy_id", field=models.IntegerField( blank=True, - help_text='This field is used to track objects imported from the BOD', + help_text="This field is used to track objects imported from the BOD", null=True, - verbose_name='Legacy ID' + verbose_name="Legacy ID", ), ), ] diff --git a/app/distributions/migrations/0006_alter_dataset_slug.py b/app/distributions/migrations/0006_alter_dataset_slug.py index 04240f64..3395cded 100644 --- a/app/distributions/migrations/0006_alter_dataset_slug.py +++ b/app/distributions/migrations/0006_alter_dataset_slug.py @@ -6,15 +6,14 @@ class Migration(migrations.Migration): - dependencies = [ - ('distributions', '0005_dataset__legacy_id_alter_dataset_slug_and_more'), + ("distributions", "0005_dataset__legacy_id_alter_dataset_slug_and_more"), ] operations = [ migrations.AlterField( - model_name='dataset', - name='slug', - field=utils.fields.CustomSlugField(max_length=100, verbose_name='Slug'), + model_name="dataset", + name="slug", + field=utils.fields.CustomSlugField(max_length=100, verbose_name="Slug"), ), ] diff --git a/app/distributions/migrations/0007_packagedistribution.py b/app/distributions/migrations/0007_packagedistribution.py index 1dfaa1f7..212c646e 100644 --- a/app/distributions/migrations/0007_packagedistribution.py +++ b/app/distributions/migrations/0007_packagedistribution.py @@ -8,31 +8,37 @@ class Migration(migrations.Migration): - dependencies = [ - ('distributions', '0006_alter_dataset_slug'), + ("distributions", "0006_alter_dataset_slug"), ] operations = [ migrations.CreateModel( - name='PackageDistribution', + name="PackageDistribution", fields=[ ( - 'id', + "id", models.BigAutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name='ID' - ) + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "slug", + utils.fields.CustomSlugField(max_length=100, verbose_name="Slug"), ), - ('slug', utils.fields.CustomSlugField(max_length=100, verbose_name='Slug')), ( - 'managed_by_stac', - models.BooleanField(max_length=100, verbose_name='Managed by STAC') + "managed_by_stac", + models.BooleanField(max_length=100, verbose_name="Managed by STAC"), ), ( - 'dataset', + "dataset", models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, to='distributions.dataset' - ) + on_delete=django.db.models.deletion.CASCADE, + to="distributions.dataset", + ), ), ], ), diff --git a/app/distributions/migrations/0008_attribution_slug.py b/app/distributions/migrations/0008_attribution_slug.py index e65b2ca1..89d67a20 100644 --- a/app/distributions/migrations/0008_attribution_slug.py +++ b/app/distributions/migrations/0008_attribution_slug.py @@ -9,34 +9,35 @@ def populate_slug(apps: Apps, schema_editor: BaseDatabaseSchemaEditor) -> None: - Attribution = apps.get_model('distributions', 'Attribution') + Attribution = apps.get_model("distributions", "Attribution") for obj in Attribution.objects.all(): obj.slug = generate_short_id() obj.save() class Migration(migrations.Migration): - dependencies = [ - ('distributions', '0007_packagedistribution'), + ("distributions", "0007_packagedistribution"), ] operations = [ migrations.AddField( - model_name='attribution', - name='slug', - field=utils.fields.CustomSlugField(default='1', max_length=100, verbose_name='Slug'), + model_name="attribution", + name="slug", + field=utils.fields.CustomSlugField( + default="1", max_length=100, verbose_name="Slug" + ), preserve_default=False, ), migrations.RunPython(populate_slug, migrations.RunPython.noop), migrations.AlterField( - model_name='attribution', - name='slug', + model_name="attribution", + name="slug", field=utils.fields.CustomSlugField( db_index=True, unique=True, max_length=100, - verbose_name='Slug', + verbose_name="Slug", ), ), ] diff --git a/app/distributions/migrations/0009_alter_dataset_slug_alter_packagedistribution_slug.py b/app/distributions/migrations/0009_alter_dataset_slug_alter_packagedistribution_slug.py index 671358cc..1f5610dc 100644 --- a/app/distributions/migrations/0009_alter_dataset_slug_alter_packagedistribution_slug.py +++ b/app/distributions/migrations/0009_alter_dataset_slug_alter_packagedistribution_slug.py @@ -6,20 +6,23 @@ class Migration(migrations.Migration): - dependencies = [ - ('distributions', '0008_attribution_slug'), + ("distributions", "0008_attribution_slug"), ] operations = [ migrations.AlterField( - model_name='dataset', - name='slug', - field=utils.fields.CustomSlugField(max_length=100, unique=True, verbose_name='Slug'), + model_name="dataset", + name="slug", + field=utils.fields.CustomSlugField( + max_length=100, unique=True, verbose_name="Slug" + ), ), migrations.AlterField( - model_name='packagedistribution', - name='slug', - field=utils.fields.CustomSlugField(max_length=100, unique=True, verbose_name='Slug'), + model_name="packagedistribution", + name="slug", + field=utils.fields.CustomSlugField( + max_length=100, unique=True, verbose_name="Slug" + ), ), ] diff --git a/app/distributions/migrations/0010_rename_slug_attribution_attribution_id_and_more_updated.py b/app/distributions/migrations/0010_rename_slug_attribution_attribution_id_and_more_updated.py index 71748c8a..4a26e7a7 100644 --- a/app/distributions/migrations/0010_rename_slug_attribution_attribution_id_and_more_updated.py +++ b/app/distributions/migrations/0010_rename_slug_attribution_attribution_id_and_more_updated.py @@ -6,46 +6,45 @@ class Migration(migrations.Migration): - dependencies = [ - ('distributions', '0009_alter_dataset_slug_alter_packagedistribution_slug'), + ("distributions", "0009_alter_dataset_slug_alter_packagedistribution_slug"), ] operations = [ migrations.RenameField( - model_name='attribution', - old_name='slug', - new_name='attribution_id', + model_name="attribution", + old_name="slug", + new_name="attribution_id", ), migrations.RenameField( - model_name='dataset', - old_name='slug', - new_name='dataset_id', + model_name="dataset", + old_name="slug", + new_name="dataset_id", ), migrations.RenameField( - model_name='packagedistribution', - old_name='slug', - new_name='package_distribution_id', + model_name="packagedistribution", + old_name="slug", + new_name="package_distribution_id", ), migrations.AlterField( - model_name='attribution', - name='attribution_id', + model_name="attribution", + name="attribution_id", field=utils.fields.CustomSlugField( - db_index=True, max_length=100, unique=True, verbose_name='External ID' + db_index=True, max_length=100, unique=True, verbose_name="External ID" ), ), migrations.AlterField( - model_name='dataset', - name='dataset_id', + model_name="dataset", + name="dataset_id", field=utils.fields.CustomSlugField( - max_length=100, unique=True, verbose_name='External ID' + max_length=100, unique=True, verbose_name="External ID" ), ), migrations.AlterField( - model_name='packagedistribution', - name='package_distribution_id', + model_name="packagedistribution", + name="package_distribution_id", field=utils.fields.CustomSlugField( - max_length=100, unique=True, verbose_name='External ID' + max_length=100, unique=True, verbose_name="External ID" ), ), ] diff --git a/app/distributions/migrations/0011_dataset_description_de_dataset_description_en_and_more.py b/app/distributions/migrations/0011_dataset_description_de_dataset_description_en_and_more.py index 1e805366..506c83a8 100644 --- a/app/distributions/migrations/0011_dataset_description_de_dataset_description_en_and_more.py +++ b/app/distributions/migrations/0011_dataset_description_de_dataset_description_en_and_more.py @@ -5,66 +5,82 @@ class Migration(migrations.Migration): - dependencies = [ - ('distributions', '0010_rename_slug_attribution_attribution_id_and_more_updated'), + ( + "distributions", + "0010_rename_slug_attribution_attribution_id_and_more_updated", + ), ] operations = [ migrations.AddField( - model_name='dataset', - name='description_de', - field=models.CharField(default='changeme', verbose_name='Description (German)'), + model_name="dataset", + name="description_de", + field=models.CharField( + default="changeme", verbose_name="Description (German)" + ), preserve_default=False, ), migrations.AddField( - model_name='dataset', - name='description_en', - field=models.CharField(default='changeme', verbose_name='Description (English)'), + model_name="dataset", + name="description_en", + field=models.CharField( + default="changeme", verbose_name="Description (English)" + ), preserve_default=False, ), migrations.AddField( - model_name='dataset', - name='description_fr', - field=models.CharField(default='changeme', verbose_name='Description (French)'), + model_name="dataset", + name="description_fr", + field=models.CharField( + default="changeme", verbose_name="Description (French)" + ), preserve_default=False, ), migrations.AddField( - model_name='dataset', - name='description_it', - field=models.CharField(blank=True, null=True, verbose_name='Description (Italian)'), + model_name="dataset", + name="description_it", + field=models.CharField( + blank=True, null=True, verbose_name="Description (Italian)" + ), ), migrations.AddField( - model_name='dataset', - name='description_rm', - field=models.CharField(blank=True, null=True, verbose_name='Description (Romansh)'), + model_name="dataset", + name="description_rm", + field=models.CharField( + blank=True, null=True, verbose_name="Description (Romansh)" + ), ), migrations.AddField( - model_name='dataset', - name='title_de', - field=models.CharField(default='changeme', verbose_name='Title (German)'), + model_name="dataset", + name="title_de", + field=models.CharField(default="changeme", verbose_name="Title (German)"), preserve_default=False, ), migrations.AddField( - model_name='dataset', - name='title_en', - field=models.CharField(default='changeme', verbose_name='Title (English)'), + model_name="dataset", + name="title_en", + field=models.CharField(default="changeme", verbose_name="Title (English)"), preserve_default=False, ), migrations.AddField( - model_name='dataset', - name='title_fr', - field=models.CharField(default='changeme', verbose_name='Title (French)'), + model_name="dataset", + name="title_fr", + field=models.CharField(default="changeme", verbose_name="Title (French)"), preserve_default=False, ), migrations.AddField( - model_name='dataset', - name='title_it', - field=models.CharField(blank=True, null=True, verbose_name='Title (Italian)'), + model_name="dataset", + name="title_it", + field=models.CharField( + blank=True, null=True, verbose_name="Title (Italian)" + ), ), migrations.AddField( - model_name='dataset', - name='title_rm', - field=models.CharField(blank=True, null=True, verbose_name='Title (Romansh)'), + model_name="dataset", + name="title_rm", + field=models.CharField( + blank=True, null=True, verbose_name="Title (Romansh)" + ), ), ] diff --git a/app/distributions/migrations/0012_dataset_geocat_id.py b/app/distributions/migrations/0012_dataset_geocat_id.py index 5ac9b81b..7644458f 100644 --- a/app/distributions/migrations/0012_dataset_geocat_id.py +++ b/app/distributions/migrations/0012_dataset_geocat_id.py @@ -9,29 +9,35 @@ def populate_geocat(apps: Apps, schema_editor: BaseDatabaseSchemaEditor) -> None: - Dataset = apps.get_model('distributions', 'Dataset') + Dataset = apps.get_model("distributions", "Dataset") for obj in Dataset.objects.all(): - obj.geocat_id = 'changeme ' + generate_short_id() + obj.geocat_id = "changeme " + generate_short_id() obj.save() class Migration(migrations.Migration): - dependencies = [ - ('distributions', '0011_dataset_description_de_dataset_description_en_and_more'), + ( + "distributions", + "0011_dataset_description_de_dataset_description_en_and_more", + ), ] operations = [ migrations.AddField( - model_name='dataset', - name='geocat_id', - field=models.CharField(default='changeme', max_length=100, verbose_name='Geocat ID'), + model_name="dataset", + name="geocat_id", + field=models.CharField( + default="changeme", max_length=100, verbose_name="Geocat ID" + ), preserve_default=False, ), migrations.RunPython(populate_geocat, migrations.RunPython.noop), migrations.AlterField( - model_name='dataset', - name='geocat_id', - field=models.CharField(max_length=100, unique=True, verbose_name='Geocat ID'), + model_name="dataset", + name="geocat_id", + field=models.CharField( + max_length=100, unique=True, verbose_name="Geocat ID" + ), ), ] diff --git a/app/distributions/migrations/0013_packagedistribution__legacy_imported_and_more.py b/app/distributions/migrations/0013_packagedistribution__legacy_imported_and_more.py index cbf26304..8c4a3937 100644 --- a/app/distributions/migrations/0013_packagedistribution__legacy_imported_and_more.py +++ b/app/distributions/migrations/0013_packagedistribution__legacy_imported_and_more.py @@ -5,24 +5,23 @@ class Migration(migrations.Migration): - dependencies = [ - ('distributions', '0012_dataset_geocat_id'), + ("distributions", "0012_dataset_geocat_id"), ] operations = [ migrations.AddField( - model_name='packagedistribution', - name='_legacy_imported', + model_name="packagedistribution", + name="_legacy_imported", field=models.BooleanField( default=False, - help_text='This field is used to track objects imported from STAC', - verbose_name='Legacy Imported' + help_text="This field is used to track objects imported from STAC", + verbose_name="Legacy Imported", ), ), migrations.AlterField( - model_name='packagedistribution', - name='managed_by_stac', - field=models.BooleanField(verbose_name='Managed by STAC'), + model_name="packagedistribution", + name="managed_by_stac", + field=models.BooleanField(verbose_name="Managed by STAC"), ), ] diff --git a/app/distributions/migrations/0014_attribution_created_attribution_updated_and_more.py b/app/distributions/migrations/0014_attribution_created_attribution_updated_and_more.py index 9017a0fe..13917201 100644 --- a/app/distributions/migrations/0014_attribution_created_attribution_updated_and_more.py +++ b/app/distributions/migrations/0014_attribution_created_attribution_updated_and_more.py @@ -6,36 +6,39 @@ class Migration(migrations.Migration): - dependencies = [ - ('distributions', '0013_packagedistribution__legacy_imported_and_more'), + ("distributions", "0013_packagedistribution__legacy_imported_and_more"), ] operations = [ migrations.AddField( - model_name='attribution', - name='created', + model_name="attribution", + name="created", field=models.DateTimeField( - auto_now_add=True, default=django.utils.timezone.now, verbose_name='Created' + auto_now_add=True, + default=django.utils.timezone.now, + verbose_name="Created", ), preserve_default=False, ), migrations.AddField( - model_name='attribution', - name='updated', - field=models.DateTimeField(auto_now=True, verbose_name='Updated'), + model_name="attribution", + name="updated", + field=models.DateTimeField(auto_now=True, verbose_name="Updated"), ), migrations.AddField( - model_name='packagedistribution', - name='created', + model_name="packagedistribution", + name="created", field=models.DateTimeField( - auto_now_add=True, default=django.utils.timezone.now, verbose_name='Created' + auto_now_add=True, + default=django.utils.timezone.now, + verbose_name="Created", ), preserve_default=False, ), migrations.AddField( - model_name='packagedistribution', - name='updated', - field=models.DateTimeField(auto_now=True, verbose_name='Updated'), + model_name="packagedistribution", + name="updated", + field=models.DateTimeField(auto_now=True, verbose_name="Updated"), ), ] diff --git a/app/distributions/models.py b/app/distributions/models.py index 75cef990..d30b51c1 100644 --- a/app/distributions/models.py +++ b/app/distributions/models.py @@ -9,7 +9,6 @@ class Attribution(models.Model): - _context = "Attribution Model" def __str__(self) -> str: @@ -30,8 +29,12 @@ def __str__(self) -> str: description_de = models.CharField(_(_context, "Description (German)")) description_fr = models.CharField(_(_context, "Description (French)")) description_en = models.CharField(_(_context, "Description (English)")) - description_it = models.CharField(_(_context, "Description (Italian)"), null=True, blank=True) - description_rm = models.CharField(_(_context, "Description (Romansh)"), null=True, blank=True) + description_it = models.CharField( + _(_context, "Description (Italian)"), null=True, blank=True + ) + description_rm = models.CharField( + _(_context, "Description (Romansh)"), null=True, blank=True + ) provider = models.ForeignKey("provider.Provider", on_delete=models.CASCADE) @@ -40,7 +43,7 @@ def __str__(self) -> str: null=True, blank=True, db_index=False, - help_text="This field is used to track objects imported from the BOD" + help_text="This field is used to track objects imported from the BOD", ) def save( @@ -49,27 +52,26 @@ def save( force_insert: bool | tuple[ModelBase, ...] = False, force_update: bool = False, using: str | None = None, - update_fields: Iterable[str] | None = None + update_fields: Iterable[str] | None = None, ) -> None: - self.full_clean() super().save( force_insert=force_insert, force_update=force_update, using=using, - update_fields=update_fields + update_fields=update_fields, ) class Dataset(models.Model): - _context = "Dataset Model" def __str__(self) -> str: return str(self.dataset_id) - - dataset_id = CustomSlugField(_(_context, "External ID"), unique=True, max_length=100) + dataset_id = CustomSlugField( + _(_context, "External ID"), unique=True, max_length=100 + ) created = models.DateTimeField(_(_context, "Created"), auto_now_add=True) updated = models.DateTimeField(_(_context, "Updated"), auto_now=True) @@ -81,8 +83,12 @@ def __str__(self) -> str: description_de = models.CharField(_(_context, "Description (German)")) description_fr = models.CharField(_(_context, "Description (French)")) description_en = models.CharField(_(_context, "Description (English)")) - description_it = models.CharField(_(_context, "Description (Italian)"), null=True, blank=True) - description_rm = models.CharField(_(_context, "Description (Romansh)"), null=True, blank=True) + description_it = models.CharField( + _(_context, "Description (Italian)"), null=True, blank=True + ) + description_rm = models.CharField( + _(_context, "Description (Romansh)"), null=True, blank=True + ) geocat_id = models.CharField(_(_context, "Geocat ID"), unique=True, max_length=100) provider = models.ForeignKey("provider.Provider", on_delete=models.CASCADE) @@ -93,7 +99,7 @@ def __str__(self) -> str: null=True, blank=True, db_index=False, - help_text="This field is used to track objects imported from the BOD" + help_text="This field is used to track objects imported from the BOD", ) def save( @@ -102,20 +108,18 @@ def save( force_insert: bool | tuple[ModelBase, ...] = False, force_update: bool = False, using: str | None = None, - update_fields: Iterable[str] | None = None + update_fields: Iterable[str] | None = None, ) -> None: - self.full_clean() super().save( force_insert=force_insert, force_update=force_update, using=using, - update_fields=update_fields + update_fields=update_fields, ) class PackageDistribution(models.Model): - _context = "Package Distribution Model" def __str__(self) -> str: @@ -132,7 +136,7 @@ def __str__(self) -> str: _(_context, "Legacy Imported"), db_index=False, default=False, - help_text="This field is used to track objects imported from STAC" + help_text="This field is used to track objects imported from STAC", ) dataset = models.ForeignKey(Dataset, on_delete=models.CASCADE) @@ -143,13 +147,12 @@ def save( force_insert: bool | tuple[ModelBase, ...] = False, force_update: bool = False, using: str | None = None, - update_fields: Iterable[str] | None = None + update_fields: Iterable[str] | None = None, ) -> None: - self.full_clean() super().save( force_insert=force_insert, force_update=force_update, using=using, - update_fields=update_fields + update_fields=update_fields, ) diff --git a/app/manage.py b/app/manage.py index 3be6b0af..3884c372 100755 --- a/app/manage.py +++ b/app/manage.py @@ -1,5 +1,6 @@ #!/usr/bin/env python """Django's command-line utility for administrative tasks.""" + import os import sys @@ -7,7 +8,7 @@ def main() -> None: """Run administrative tasks.""" # default to the setting that's being created in DOCKERFILE - os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings') + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings") try: from django.core.management import \ execute_from_command_line # pylint: disable=import-outside-toplevel @@ -20,5 +21,5 @@ def main() -> None: execute_from_command_line(sys.argv) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/app/provider/admin.py b/app/provider/admin.py index 27e14100..ff44b8c5 100644 --- a/app/provider/admin.py +++ b/app/provider/admin.py @@ -5,7 +5,7 @@ @admin.register(Provider) class ProviderAdmin(admin.ModelAdmin): # type:ignore[type-arg] - '''Admin View for Provider''' + """Admin View for Provider""" - list_display = ('provider_id', 'acronym_en', 'name_en') - readonly_fields = ('created', 'updated') + list_display = ("provider_id", "acronym_en", "name_en") + readonly_fields = ("created", "updated") diff --git a/app/provider/api.py b/app/provider/api.py index b1be9884..17e9a4d4 100644 --- a/app/provider/api.py +++ b/app/provider/api.py @@ -36,7 +36,7 @@ def provider_to_response(model: Provider, lang: LanguageCode) -> ProviderSchema: en=model.acronym_en, it=model.acronym_it, rm=model.acronym_rm, - ) + ), ) return response @@ -45,7 +45,7 @@ def provider_to_response(model: Provider, lang: LanguageCode) -> ProviderSchema: "/providers/{provider_id}", response={200: ProviderSchema}, exclude_none=True, - auth=PermissionAuth('provider.view_provider') + auth=PermissionAuth("provider.view_provider"), ) def provider( request: HttpRequest, provider_id: str, lang: LanguageCode | None = None @@ -104,9 +104,11 @@ def provider( "/providers", response={200: ProviderListSchema}, exclude_none=True, - auth=PermissionAuth('provider.view_provider') + auth=PermissionAuth("provider.view_provider"), ) -def providers(request: HttpRequest, lang: LanguageCode | None = None) -> ProviderListSchema: +def providers( + request: HttpRequest, lang: LanguageCode | None = None +) -> ProviderListSchema: """ Get all providers, return translatable fields in the given language. diff --git a/app/provider/apps.py b/app/provider/apps.py index 6990561b..05baa0b1 100644 --- a/app/provider/apps.py +++ b/app/provider/apps.py @@ -2,5 +2,5 @@ class ProviderConfig(AppConfig): - default_auto_field = 'django.db.models.BigAutoField' - name = 'provider' + default_auto_field = "django.db.models.BigAutoField" + name = "provider" diff --git a/app/provider/migrations/0001_initial.py b/app/provider/migrations/0001_initial.py index 113113bc..c7f2f97f 100644 --- a/app/provider/migrations/0001_initial.py +++ b/app/provider/migrations/0001_initial.py @@ -5,27 +5,32 @@ class Migration(migrations.Migration): - initial = True dependencies = [] operations = [ migrations.CreateModel( - name='Provider', + name="Provider", fields=[ ( - 'id', + "id", models.BigAutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name='ID' - ) + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("name", models.CharField(max_length=50, verbose_name="Provider Name")), + ( + "prefix", + models.CharField(max_length=50, verbose_name="Provider prefix"), ), - ('name', models.CharField(max_length=50, verbose_name='Provider Name')), - ('prefix', models.CharField(max_length=50, verbose_name='Provider prefix')), ], options={ - 'verbose_name': 'provider', - 'verbose_name_plural': 'providers', + "verbose_name": "provider", + "verbose_name_plural": "providers", }, ), ] diff --git a/app/provider/migrations/0002_remove_provider_name_remove_provider_prefix_and_more.py b/app/provider/migrations/0002_remove_provider_name_remove_provider_prefix_and_more.py index d33888af..42fafffd 100644 --- a/app/provider/migrations/0002_remove_provider_name_remove_provider_prefix_and_more.py +++ b/app/provider/migrations/0002_remove_provider_name_remove_provider_prefix_and_more.py @@ -5,58 +5,57 @@ class Migration(migrations.Migration): - dependencies = [ - ('provider', '0001_initial'), + ("provider", "0001_initial"), ] operations = [ migrations.RemoveField( - model_name='provider', - name='name', + model_name="provider", + name="name", ), migrations.RemoveField( - model_name='provider', - name='prefix', + model_name="provider", + name="prefix", ), migrations.AddField( - model_name='provider', - name='acronym_de', - field=models.CharField(blank=True, verbose_name='Acronym (German)'), + model_name="provider", + name="acronym_de", + field=models.CharField(blank=True, verbose_name="Acronym (German)"), ), migrations.AddField( - model_name='provider', - name='acronym_en', - field=models.CharField(blank=True, verbose_name='Acronym (English)'), + model_name="provider", + name="acronym_en", + field=models.CharField(blank=True, verbose_name="Acronym (English)"), ), migrations.AddField( - model_name='provider', - name='acronym_fr', - field=models.CharField(blank=True, verbose_name='Acronym (French)'), + model_name="provider", + name="acronym_fr", + field=models.CharField(blank=True, verbose_name="Acronym (French)"), ), migrations.AddField( - model_name='provider', - name='acronym_it', - field=models.CharField(blank=True, verbose_name='Acronym (Italian)'), + model_name="provider", + name="acronym_it", + field=models.CharField(blank=True, verbose_name="Acronym (Italian)"), ), migrations.AddField( - model_name='provider', - name='name_de', - field=models.CharField(blank=True, verbose_name='Name (German)'), + model_name="provider", + name="name_de", + field=models.CharField(blank=True, verbose_name="Name (German)"), ), migrations.AddField( - model_name='provider', - name='name_en', - field=models.CharField(blank=True, verbose_name='Name (English)'), + model_name="provider", + name="name_en", + field=models.CharField(blank=True, verbose_name="Name (English)"), ), migrations.AddField( - model_name='provider', - name='name_fr', - field=models.CharField(blank=True, verbose_name='Name (French)'), + model_name="provider", + name="name_fr", + field=models.CharField(blank=True, verbose_name="Name (French)"), ), migrations.AddField( - model_name='provider', - name='name_it', - field=models.CharField(blank=True, verbose_name='Name (Italian)'), + model_name="provider", + name="name_it", + field=models.CharField(blank=True, verbose_name="Name (Italian)"), ), ] diff --git a/app/provider/migrations/0003_provider_acronym_rm_provider_name_rm.py b/app/provider/migrations/0003_provider_acronym_rm_provider_name_rm.py index 92d81da7..e1aff83f 100644 --- a/app/provider/migrations/0003_provider_acronym_rm_provider_name_rm.py +++ b/app/provider/migrations/0003_provider_acronym_rm_provider_name_rm.py @@ -5,20 +5,19 @@ class Migration(migrations.Migration): - dependencies = [ - ('provider', '0002_remove_provider_name_remove_provider_prefix_and_more'), + ("provider", "0002_remove_provider_name_remove_provider_prefix_and_more"), ] operations = [ migrations.AddField( - model_name='provider', - name='acronym_rm', - field=models.CharField(blank=True, verbose_name='Acronym (Romansh)'), + model_name="provider", + name="acronym_rm", + field=models.CharField(blank=True, verbose_name="Acronym (Romansh)"), ), migrations.AddField( - model_name='provider', - name='name_rm', - field=models.CharField(blank=True, verbose_name='Name (Romansh)'), + model_name="provider", + name="name_rm", + field=models.CharField(blank=True, verbose_name="Name (Romansh)"), ), ] diff --git a/app/provider/migrations/0004_alter_provider_acronym_de_alter_provider_acronym_en_and_more.py b/app/provider/migrations/0004_alter_provider_acronym_de_alter_provider_acronym_en_and_more.py index dc6a57ca..fd593a6f 100644 --- a/app/provider/migrations/0004_alter_provider_acronym_de_alter_provider_acronym_en_and_more.py +++ b/app/provider/migrations/0004_alter_provider_acronym_de_alter_provider_acronym_en_and_more.py @@ -5,40 +5,39 @@ class Migration(migrations.Migration): - dependencies = [ - ('provider', '0003_provider_acronym_rm_provider_name_rm'), + ("provider", "0003_provider_acronym_rm_provider_name_rm"), ] operations = [ migrations.AlterField( - model_name='provider', - name='acronym_de', - field=models.CharField(verbose_name='Acronym (German)'), + model_name="provider", + name="acronym_de", + field=models.CharField(verbose_name="Acronym (German)"), ), migrations.AlterField( - model_name='provider', - name='acronym_en', - field=models.CharField(verbose_name='Acronym (English)'), + model_name="provider", + name="acronym_en", + field=models.CharField(verbose_name="Acronym (English)"), ), migrations.AlterField( - model_name='provider', - name='acronym_fr', - field=models.CharField(verbose_name='Acronym (French)'), + model_name="provider", + name="acronym_fr", + field=models.CharField(verbose_name="Acronym (French)"), ), migrations.AlterField( - model_name='provider', - name='name_de', - field=models.CharField(verbose_name='Name (German)'), + model_name="provider", + name="name_de", + field=models.CharField(verbose_name="Name (German)"), ), migrations.AlterField( - model_name='provider', - name='name_en', - field=models.CharField(verbose_name='Name (English)'), + model_name="provider", + name="name_en", + field=models.CharField(verbose_name="Name (English)"), ), migrations.AlterField( - model_name='provider', - name='name_fr', - field=models.CharField(verbose_name='Name (French)'), + model_name="provider", + name="name_fr", + field=models.CharField(verbose_name="Name (French)"), ), ] diff --git a/app/provider/migrations/0005_alter_provider_options.py b/app/provider/migrations/0005_alter_provider_options.py index f99f96b8..3075ff7c 100644 --- a/app/provider/migrations/0005_alter_provider_options.py +++ b/app/provider/migrations/0005_alter_provider_options.py @@ -4,14 +4,16 @@ class Migration(migrations.Migration): - dependencies = [ - ('provider', '0004_alter_provider_acronym_de_alter_provider_acronym_en_and_more'), + ( + "provider", + "0004_alter_provider_acronym_de_alter_provider_acronym_en_and_more", + ), ] operations = [ migrations.AlterModelOptions( - name='provider', + name="provider", options={}, ), ] diff --git a/app/provider/migrations/0006_alter_provider_acronym_it_alter_provider_acronym_rm_and_more.py b/app/provider/migrations/0006_alter_provider_acronym_it_alter_provider_acronym_rm_and_more.py index 3617549b..85ea42ca 100644 --- a/app/provider/migrations/0006_alter_provider_acronym_it_alter_provider_acronym_rm_and_more.py +++ b/app/provider/migrations/0006_alter_provider_acronym_it_alter_provider_acronym_rm_and_more.py @@ -5,30 +5,37 @@ class Migration(migrations.Migration): - dependencies = [ - ('provider', '0005_alter_provider_options'), + ("provider", "0005_alter_provider_options"), ] operations = [ migrations.AlterField( - model_name='provider', - name='acronym_it', - field=models.CharField(blank=True, null=True, verbose_name='Acronym (Italian)'), + model_name="provider", + name="acronym_it", + field=models.CharField( + blank=True, null=True, verbose_name="Acronym (Italian)" + ), ), migrations.AlterField( - model_name='provider', - name='acronym_rm', - field=models.CharField(blank=True, null=True, verbose_name='Acronym (Romansh)'), + model_name="provider", + name="acronym_rm", + field=models.CharField( + blank=True, null=True, verbose_name="Acronym (Romansh)" + ), ), migrations.AlterField( - model_name='provider', - name='name_it', - field=models.CharField(blank=True, null=True, verbose_name='Name (Italian)'), + model_name="provider", + name="name_it", + field=models.CharField( + blank=True, null=True, verbose_name="Name (Italian)" + ), ), migrations.AlterField( - model_name='provider', - name='name_rm', - field=models.CharField(blank=True, null=True, verbose_name='Name (Romansh)'), + model_name="provider", + name="name_rm", + field=models.CharField( + blank=True, null=True, verbose_name="Name (Romansh)" + ), ), ] diff --git a/app/provider/migrations/0007_provider__legacy_id.py b/app/provider/migrations/0007_provider__legacy_id.py index 555d6fa0..0415846c 100644 --- a/app/provider/migrations/0007_provider__legacy_id.py +++ b/app/provider/migrations/0007_provider__legacy_id.py @@ -5,20 +5,22 @@ class Migration(migrations.Migration): - dependencies = [ - ('provider', '0006_alter_provider_acronym_it_alter_provider_acronym_rm_and_more'), + ( + "provider", + "0006_alter_provider_acronym_it_alter_provider_acronym_rm_and_more", + ), ] operations = [ migrations.AddField( - model_name='provider', - name='_legacy_id', + model_name="provider", + name="_legacy_id", field=models.IntegerField( blank=True, - help_text='This field is used to track objects imported from the BOD', + help_text="This field is used to track objects imported from the BOD", null=True, - verbose_name='Legacy ID' + verbose_name="Legacy ID", ), ), ] diff --git a/app/provider/migrations/0008_provider_slug.py b/app/provider/migrations/0008_provider_slug.py index ded6224f..a5934106 100644 --- a/app/provider/migrations/0008_provider_slug.py +++ b/app/provider/migrations/0008_provider_slug.py @@ -9,34 +9,35 @@ def populate_slug(apps: Apps, schema_editor: BaseDatabaseSchemaEditor) -> None: - Provider = apps.get_model('provider', 'Provider') + Provider = apps.get_model("provider", "Provider") for obj in Provider.objects.all(): obj.slug = generate_short_id() obj.save() class Migration(migrations.Migration): - dependencies = [ - ('provider', '0007_provider__legacy_id'), + ("provider", "0007_provider__legacy_id"), ] operations = [ migrations.AddField( - model_name='provider', - name='slug', - field=utils.fields.CustomSlugField(default='1', max_length=100, verbose_name='Slug'), + model_name="provider", + name="slug", + field=utils.fields.CustomSlugField( + default="1", max_length=100, verbose_name="Slug" + ), preserve_default=False, ), migrations.RunPython(populate_slug, migrations.RunPython.noop), migrations.AlterField( - model_name='provider', - name='slug', + model_name="provider", + name="slug", field=utils.fields.CustomSlugField( db_index=True, unique=True, max_length=100, - verbose_name='Slug', + verbose_name="Slug", ), ), ] diff --git a/app/provider/migrations/0009_rename_slug_provider_provider_id_and_more.py b/app/provider/migrations/0009_rename_slug_provider_provider_id_and_more.py index 942dbbe6..52f81971 100644 --- a/app/provider/migrations/0009_rename_slug_provider_provider_id_and_more.py +++ b/app/provider/migrations/0009_rename_slug_provider_provider_id_and_more.py @@ -6,22 +6,21 @@ class Migration(migrations.Migration): - dependencies = [ - ('provider', '0008_provider_slug'), + ("provider", "0008_provider_slug"), ] operations = [ migrations.RenameField( - model_name='provider', - old_name='slug', - new_name='provider_id', + model_name="provider", + old_name="slug", + new_name="provider_id", ), migrations.AlterField( - model_name='provider', - name='provider_id', + model_name="provider", + name="provider_id", field=utils.fields.CustomSlugField( - db_index=True, max_length=100, unique=True, verbose_name='External ID' + db_index=True, max_length=100, unique=True, verbose_name="External ID" ), ), ] diff --git a/app/provider/migrations/0010_provider_created_provider_updated.py b/app/provider/migrations/0010_provider_created_provider_updated.py index 0f757ecd..cb11dcc2 100644 --- a/app/provider/migrations/0010_provider_created_provider_updated.py +++ b/app/provider/migrations/0010_provider_created_provider_updated.py @@ -6,23 +6,24 @@ class Migration(migrations.Migration): - dependencies = [ - ('provider', '0009_rename_slug_provider_provider_id_and_more'), + ("provider", "0009_rename_slug_provider_provider_id_and_more"), ] operations = [ migrations.AddField( - model_name='provider', - name='created', + model_name="provider", + name="created", field=models.DateTimeField( - auto_now_add=True, default=django.utils.timezone.now, verbose_name='Created' + auto_now_add=True, + default=django.utils.timezone.now, + verbose_name="Created", ), preserve_default=False, ), migrations.AddField( - model_name='provider', - name='updated', - field=models.DateTimeField(auto_now=True, verbose_name='Updated'), + model_name="provider", + name="updated", + field=models.DateTimeField(auto_now=True, verbose_name="Updated"), ), ] diff --git a/app/provider/models.py b/app/provider/models.py index 42d64e8b..4d6343af 100644 --- a/app/provider/models.py +++ b/app/provider/models.py @@ -9,16 +9,15 @@ class Provider(models.Model): - _context = "Provider model" def __str__(self) -> str: return str(self.provider_id) - ''' + """ Note: The "blank=False" for a model field doesn't prevent DB changes. It only has an effect on form validation. - ''' + """ provider_id = CustomSlugField( _(_context, "External ID"), max_length=100, unique=True, db_index=True ) @@ -34,15 +33,19 @@ def __str__(self) -> str: acronym_de = models.CharField(_(_context, "Acronym (German)")) acronym_fr = models.CharField(_(_context, "Acronym (French)")) acronym_en = models.CharField(_(_context, "Acronym (English)")) - acronym_it = models.CharField(_(_context, "Acronym (Italian)"), null=True, blank=True) - acronym_rm = models.CharField(_(_context, "Acronym (Romansh)"), null=True, blank=True) + acronym_it = models.CharField( + _(_context, "Acronym (Italian)"), null=True, blank=True + ) + acronym_rm = models.CharField( + _(_context, "Acronym (Romansh)"), null=True, blank=True + ) _legacy_id = models.IntegerField( _(_context, "Legacy ID"), null=True, blank=True, db_index=False, - help_text="This field is used to track objects imported from the BOD" + help_text="This field is used to track objects imported from the BOD", ) def save( @@ -51,13 +54,12 @@ def save( force_insert: bool | tuple[ModelBase, ...] = False, force_update: bool = False, using: str | None = None, - update_fields: Iterable[str] | None = None + update_fields: Iterable[str] | None = None, ) -> None: - self.full_clean() super().save( force_insert=force_insert, force_update=force_update, using=using, - update_fields=update_fields + update_fields=update_fields, ) diff --git a/app/support/apps.py b/app/support/apps.py index e18c7bee..dd1d778a 100644 --- a/app/support/apps.py +++ b/app/support/apps.py @@ -2,5 +2,5 @@ class SupportConfig(AppConfig): - default_auto_field = 'django.db.models.BigAutoField' - name = 'support' + default_auto_field = "django.db.models.BigAutoField" + name = "support" diff --git a/app/support/management/commands/manage_superuser.py b/app/support/management/commands/manage_superuser.py index 93d75d58..9e2e6127 100644 --- a/app/support/management/commands/manage_superuser.py +++ b/app/support/management/commands/manage_superuser.py @@ -19,20 +19,20 @@ class Handler(CommandHandler): def run(self) -> None: User = get_user_model() # pylint: disable=invalid-name - username = env.str('DJANGO_SUPERUSER_USERNAME', default='').strip() - email = env.str('DJANGO_SUPERUSER_EMAIL', default='').strip() - password = env.str('DJANGO_SUPERUSER_PASSWORD', default='').strip() + username = env.str("DJANGO_SUPERUSER_USERNAME", default="").strip() + email = env.str("DJANGO_SUPERUSER_EMAIL", default="").strip() + password = env.str("DJANGO_SUPERUSER_PASSWORD", default="").strip() if not username or not email or not password: - self.print_error('Environment variables not set or empty') + self.print_error("Environment variables not set or empty") return try: admin = User.objects.get(username=username) - operation = 'Updated' + operation = "Updated" except User.DoesNotExist: admin = User.objects.create(username=username) - operation = 'Created' + operation = "Created" admin.set_password(password) admin.email = email @@ -40,7 +40,7 @@ def run(self) -> None: admin.is_superuser = True admin.save() - self.print_success('%s the superuser %s', operation, username) + self.print_success("%s the superuser %s", operation, username) class Command(CustomBaseCommand): diff --git a/app/tests/access/test_access_admin.py b/app/tests/access/test_access_admin.py index 15910611..79baf352 100644 --- a/app/tests/access/test_access_admin.py +++ b/app/tests/access/test_access_admin.py @@ -6,36 +6,38 @@ from django.urls import reverse -@fixture(name='user') +@fixture(name="user") def fixture_user(provider): - with patch('access.models.Client') as client: + with patch("access.models.Client") as client: client.return_value.create_user.return_value = True yield User.objects.create( username="dude", first_name="Jeffrey", last_name="Lebowski", email="dude@bowling.com", - provider=provider + provider=provider, ) -@patch('access.models.Client') +@patch("access.models.Client") def test_disabled_users_are_displayed(cognito_client, client, admin_user, user): user.disable() client.force_login(admin_user) - url = reverse('admin:access_user_changelist') + url = reverse("admin:access_user_changelist") response = client.get(url) - assert b'Lebowski' in response.content + assert b"Lebowski" in response.content -@patch('access.models.Client') +@patch("access.models.Client") def test_disable_users(cognito_client, client, admin_user, user): cognito_client.return_value.disable_user.return_value = True client.force_login(admin_user) - url = reverse('admin:access_user_changelist') - response = client.post(url, data={'action': 'disable', '_selected_action': [user.id]}) + url = reverse("admin:access_user_changelist") + response = client.post( + url, data={"action": "disable", "_selected_action": [user.id]} + ) assert response.status_code == 302 assert cognito_client.return_value.disable_user.called @@ -43,16 +45,19 @@ def test_disable_users(cognito_client, client, admin_user, user): assert user.deleted_at -@patch('access.models.Client') +@patch("access.models.Client") def test_delete_users(cognito_client, client, admin_user, user): cognito_client.return_value.delete_user.return_value = True client.force_login(admin_user) - url = reverse('admin:access_user_changelist') + url = reverse("admin:access_user_changelist") response = client.post( - url, data={ - 'action': 'delete_selected', '_selected_action': [user.id], 'post': 'yes' - } + url, + data={ + "action": "delete_selected", + "_selected_action": [user.id], + "post": "yes", + }, ) assert response.status_code == 302 assert cognito_client.return_value.delete_user.called @@ -60,33 +65,35 @@ def test_delete_users(cognito_client, client, admin_user, user): assert User.all_objects.first() is None -@patch('access.models.Client') -def test_user_id_and_deleted_at_readonly(cognito_client, client, admin_user, provider, user): +@patch("access.models.Client") +def test_user_id_and_deleted_at_readonly( + cognito_client, client, admin_user, provider, user +): cognito_client.return_value.update_user.return_value = True client.force_login(admin_user) - url = reverse('admin:access_user_change', args=[user.id]) + url = reverse("admin:access_user_change", args=[user.id]) response = client.post( url, data={ - 'username': 'a', - 'user_id': 'b', - 'first_name': 'c', - 'last_name': 'd', - 'email': 'e@f.gh', - 'deleted_at_0': '2024-12-17', - 'deleted_at_1': '14:30:00', - 'provider': provider.id, - } + "username": "a", + "user_id": "b", + "first_name": "c", + "last_name": "d", + "email": "e@f.gh", + "deleted_at_0": "2024-12-17", + "deleted_at_1": "14:30:00", + "provider": provider.id, + }, ) assert response.status_code == 302 assert cognito_client.return_value.update_user.called user.refresh_from_db() - assert user.username == 'a' - assert user.user_id != 'b' - assert user.first_name == 'c' - assert user.last_name == 'd' - assert user.email == 'e@f.gh' + assert user.username == "a" + assert user.user_id != "b" + assert user.first_name == "c" + assert user.last_name == "d" + assert user.email == "e@f.gh" assert user.deleted_at is None assert user.provider == provider diff --git a/app/tests/access/test_access_api.py b/app/tests/access/test_access_api.py index 43f57cc7..ca8b73a9 100644 --- a/app/tests/access/test_access_api.py +++ b/app/tests/access/test_access_api.py @@ -7,21 +7,20 @@ from pytest import fixture -@fixture(name='user') +@fixture(name="user") def fixture_user(provider): - with patch('access.models.Client') as client: + with patch("access.models.Client") as client: client.return_value.create_user.return_value = True yield User.objects.create( username="dude", first_name="Jeffrey", last_name="Lebowski", email="dude@bowling.com", - provider=provider + provider=provider, ) def test_user_to_response_maps_fields_correctly(user): - model = User.objects.last() actual = user_to_response(model) @@ -38,8 +37,8 @@ def test_user_to_response_maps_fields_correctly(user): def test_get_user_returns_existing_user(user, django_user_factory, client): - django_user_factory('test', 'test', [('access', 'user', 'view_user')]) - client.login(username='test', password='test') + django_user_factory("test", "test", [("access", "user", "view_user")]) + client.login(username="test", password="test") response = client.get("/api/v1/users/dude") @@ -54,8 +53,8 @@ def test_get_user_returns_existing_user(user, django_user_factory, client): def test_get_user_returns_404_if_nonexisting(user, django_user_factory, client): - django_user_factory('test', 'test', [('access', 'user', 'view_user')]) - client.login(username='test', password='test') + django_user_factory("test", "test", [("access", "user", "view_user")]) + client.login(username="test", password="test") response = client.get("/api/v1/users/nihilist") @@ -71,8 +70,8 @@ def test_get_user_returns_401_if_not_logged_in(user, django_user_factory, client def test_get_user_returns_403_if_no_permission(user, django_user_factory, client): - django_user_factory('test', 'test', []) - client.login(username='test', password='test') + django_user_factory("test", "test", []) + client.login(username="test", password="test") response = client.get("/api/v1/users/dude") @@ -81,29 +80,33 @@ def test_get_user_returns_403_if_no_permission(user, django_user_factory, client def test_get_users_returns_single_user(user, django_user_factory, client): - django_user_factory('test', 'test', [('access', 'user', 'view_user')]) - client.login(username='test', password='test') + django_user_factory("test", "test", [("access", "user", "view_user")]) + client.login(username="test", password="test") response = client.get("/api/v1/users") assert response.status_code == 200 assert response.json() == { - "items": [{ - "username": "dude", - "first_name": "Jeffrey", - "last_name": "Lebowski", - "email": "dude@bowling.com", - "provider_id": "ch.bafu", - }] + "items": [ + { + "username": "dude", + "first_name": "Jeffrey", + "last_name": "Lebowski", + "email": "dude@bowling.com", + "provider_id": "ch.bafu", + } + ] } -@patch('access.models.Client') -def test_get_users_returns_users_ordered_by_id(cognito_client, user, django_user_factory, client): +@patch("access.models.Client") +def test_get_users_returns_users_ordered_by_id( + cognito_client, user, django_user_factory, client +): cognito_client.return_value.create_user.return_value = True - django_user_factory('test', 'test', [('access', 'user', 'view_user')]) - client.login(username='test', password='test') + django_user_factory("test", "test", [("access", "user", "view_user")]) + client.login(username="test", password="test") model_fields = { "username": "veteran", @@ -145,8 +148,8 @@ def test_get_users_returns_401_if_not_logged_in(user, django_user_factory, clien def test_get_users_returns_403_if_no_permission(user, django_user_factory, client): - django_user_factory('test', 'test', []) - client.login(username='test', password='test') + django_user_factory("test", "test", []) + client.login(username="test", password="test") response = client.get("/api/v1/users") @@ -154,14 +157,14 @@ def test_get_users_returns_403_if_no_permission(user, django_user_factory, clien assert response.json() == {"code": 403, "description": "Forbidden"} -@patch('access.models.Client') +@patch("access.models.Client") def test_post_users_creates_new_user_in_db_and_returns_it( cognito_client, user, django_user_factory, client ): cognito_client.return_value.create_user.return_value = True - django_user_factory('test', 'test', [('access', 'user', 'add_user')]) - client.login(username='test', password='test') + django_user_factory("test", "test", [("access", "user", "add_user")]) + client.login(username="test", password="test") payload = { "username": "donny", @@ -171,7 +174,9 @@ def test_post_users_creates_new_user_in_db_and_returns_it( "provider_id": "ch.bafu", } - response = client.post("/api/v1/users", data=payload, content_type='application/json') + response = client.post( + "/api/v1/users", data=payload, content_type="application/json" + ) assert response.status_code == 201 assert response.json() == payload @@ -179,14 +184,14 @@ def test_post_users_creates_new_user_in_db_and_returns_it( assert cognito_client.return_value.create_user.called -@patch('access.models.Client') +@patch("access.models.Client") def test_post_users_returns_404_if_provider_id_does_not_exist( cognito_client, user, django_user_factory, client ): cognito_client.return_value.create_user.return_value = False - django_user_factory('test', 'test', [('access', 'user', 'add_user')]) - client.login(username='test', password='test') + django_user_factory("test", "test", [("access", "user", "add_user")]) + client.login(username="test", password="test") payload = { "username": "donny", @@ -196,21 +201,23 @@ def test_post_users_returns_404_if_provider_id_does_not_exist( "provider_id": "non_existing_provider_id", } - response = client.post("/api/v1/users", data=payload, content_type='application/json') + response = client.post( + "/api/v1/users", data=payload, content_type="application/json" + ) assert response.status_code == 404 assert response.json() == {"code": 404, "description": "Resource not found"} assert not cognito_client.return_value.create_user.called -@patch('access.models.Client') +@patch("access.models.Client") def test_post_users_returns_422_if_email_format_invalid( cognito_client, user, django_user_factory, client ): cognito_client.return_value.create_user.return_value = False - django_user_factory('test', 'test', [('access', 'user', 'add_user')]) - client.login(username='test', password='test') + django_user_factory("test", "test", [("access", "user", "add_user")]) + client.login(username="test", password="test") invalid_email = "donny_at_bowling_dot_com" payload = { @@ -221,21 +228,26 @@ def test_post_users_returns_422_if_email_format_invalid( "provider_id": "ch.bafu", } - response = client.post("/api/v1/users", data=payload, content_type='application/json') + response = client.post( + "/api/v1/users", data=payload, content_type="application/json" + ) assert response.status_code == 422 - assert response.json() == {'code': 422, 'description': ["Enter a valid email address."]} + assert response.json() == { + "code": 422, + "description": ["Enter a valid email address."], + } assert not cognito_client.return_value.create_user.called -@patch('access.models.Client') +@patch("access.models.Client") def test_post_users_returns_409_if_user_exists_already( cognito_client, user, django_user_factory, client ): cognito_client.return_value.create_user.return_value = False - django_user_factory('test', 'test', [('access', 'user', 'add_user')]) - client.login(username='test', password='test') + django_user_factory("test", "test", [("access", "user", "add_user")]) + client.login(username="test", password="test") payload = { "username": "dude", @@ -245,23 +257,26 @@ def test_post_users_returns_409_if_user_exists_already( "provider_id": "ch.bafu", } - response = client.post("/api/v1/users", data=payload, content_type='application/json') + response = client.post( + "/api/v1/users", data=payload, content_type="application/json" + ) assert response.status_code == 409 assert response.json() == { - 'code': 409, 'description': ["User with this User name already exists."] + "code": 409, + "description": ["User with this User name already exists."], } assert not cognito_client.return_value.create_user.called -@patch('access.models.Client') +@patch("access.models.Client") def test_post_users_returns_409_and_reports_all_errors_if_multiple_things_amiss( cognito_client, user, django_user_factory, client ): cognito_client.return_value.create_user.return_value = False - django_user_factory('test', 'test', [('access', 'user', 'add_user')]) - client.login(username='test', password='test') + django_user_factory("test", "test", [("access", "user", "add_user")]) + client.login(username="test", password="test") invalid_email = "donny_at_bowling_dot_com" payload = { @@ -272,24 +287,29 @@ def test_post_users_returns_409_and_reports_all_errors_if_multiple_things_amiss( "provider_id": "ch.bafu", } - response = client.post("/api/v1/users", data=payload, content_type='application/json') + response = client.post( + "/api/v1/users", data=payload, content_type="application/json" + ) assert response.status_code == 409 assert response.json() == { - 'code': 409, - 'description': ["Enter a valid email address.", "User with this User name already exists."] + "code": 409, + "description": [ + "Enter a valid email address.", + "User with this User name already exists.", + ], } assert not cognito_client.return_value.create_user.called -@patch('access.models.Client') +@patch("access.models.Client") def test_post_users_returns_500_if_cognito_inconsistent( cognito_client, user, django_user_factory, client ): cognito_client.return_value.create_user.return_value = False - django_user_factory('test', 'test', [('access', 'user', 'add_user')]) - client.login(username='test', password='test') + django_user_factory("test", "test", [("access", "user", "add_user")]) + client.login(username="test", password="test") payload = { "username": "donny", @@ -299,22 +319,26 @@ def test_post_users_returns_500_if_cognito_inconsistent( "provider_id": "ch.bafu", } - response = client.post("/api/v1/users", data=payload, content_type='application/json') + response = client.post( + "/api/v1/users", data=payload, content_type="application/json" + ) assert response.status_code == 500 - assert response.json() == {'code': 500, 'description': 'Internal Server Error'} + assert response.json() == {"code": 500, "description": "Internal Server Error"} assert User.objects.count() == 1 assert cognito_client.return_value.create_user.called -@patch('access.models.Client') -def test_post_users_returns_503_if_cognito_down(cognito_client, user, django_user_factory, client): +@patch("access.models.Client") +def test_post_users_returns_503_if_cognito_down( + cognito_client, user, django_user_factory, client +): cognito_client.return_value.create_user.side_effect = EndpointConnectionError( - endpoint_url='http://localhost' + endpoint_url="http://localhost" ) - django_user_factory('test', 'test', [('access', 'user', 'add_user')]) - client.login(username='test', password='test') + django_user_factory("test", "test", [("access", "user", "add_user")]) + client.login(username="test", password="test") payload = { "username": "donny", @@ -324,19 +348,23 @@ def test_post_users_returns_503_if_cognito_down(cognito_client, user, django_use "provider_id": "ch.bafu", } - response = client.post("/api/v1/users", data=payload, content_type='application/json') + response = client.post( + "/api/v1/users", data=payload, content_type="application/json" + ) assert response.status_code == 503 - assert response.json() == {'code': 503, 'description': 'Service Unavailable'} + assert response.json() == {"code": 503, "description": "Service Unavailable"} assert User.objects.count() == 1 assert cognito_client.return_value.create_user.called -@patch('access.models.Client') -def test_post_user_returns_401_if_not_logged_in(cognito_client, user, django_user_factory, client): +@patch("access.models.Client") +def test_post_user_returns_401_if_not_logged_in( + cognito_client, user, django_user_factory, client +): cognito_client.return_value.create_user.return_value = True - response = client.post("/api/v1/users", data={}, content_type='application/json') + response = client.post("/api/v1/users", data={}, content_type="application/json") assert response.status_code == 401 assert response.json() == {"code": 401, "description": "Unauthorized"} @@ -344,14 +372,16 @@ def test_post_user_returns_401_if_not_logged_in(cognito_client, user, django_use assert User.objects.count() == 1 -@patch('access.models.Client') -def test_post_user_returns_403_if_no_permission(cognito_client, user, django_user_factory, client): +@patch("access.models.Client") +def test_post_user_returns_403_if_no_permission( + cognito_client, user, django_user_factory, client +): cognito_client.return_value.create_user.return_value = True - django_user_factory('test', 'test', []) - client.login(username='test', password='test') + django_user_factory("test", "test", []) + client.login(username="test", password="test") - response = client.post("/api/v1/users", data={}, content_type='application/json') + response = client.post("/api/v1/users", data={}, content_type="application/json") assert response.status_code == 403 assert response.json() == {"code": 403, "description": "Forbidden"} @@ -359,27 +389,29 @@ def test_post_user_returns_403_if_no_permission(cognito_client, user, django_use assert User.objects.count() == 1 -@patch('access.models.Client') +@patch("access.models.Client") def test_delete_user_deletes_user(cognito_client, user, django_user_factory, client): cognito_client.return_value.disable_user.return_value = True - django_user_factory('test', 'test', [('access', 'user', 'delete_user')]) - client.login(username='test', password='test') + django_user_factory("test", "test", [("access", "user", "delete_user")]) + client.login(username="test", password="test") response = client.delete("/api/v1/users/dude") assert response.status_code == 204 - assert response.content == b'' + assert response.content == b"" assert User.objects.count() == 0 assert cognito_client.return_value.disable_user.called -@patch('access.models.Client') -def test_delete_user_returns_404_if_nonexisting(cognito_client, user, django_user_factory, client): +@patch("access.models.Client") +def test_delete_user_returns_404_if_nonexisting( + cognito_client, user, django_user_factory, client +): cognito_client.return_value.disable_user.return_value = False - django_user_factory('test', 'test', [('access', 'user', 'delete_user')]) - client.login(username='test', password='test') + django_user_factory("test", "test", [("access", "user", "delete_user")]) + client.login(username="test", password="test") response = client.delete("/api/v1/users/lebowski") @@ -389,14 +421,14 @@ def test_delete_user_returns_404_if_nonexisting(cognito_client, user, django_use assert not cognito_client.return_value.disable_user.called -@patch('access.models.Client') +@patch("access.models.Client") def test_delete_user_returns_500_if_cognito_inconsistent( cognito_client, user, django_user_factory, client ): cognito_client.return_value.disable_user.return_value = False - django_user_factory('test', 'test', [('access', 'user', 'delete_user')]) - client.login(username='test', password='test') + django_user_factory("test", "test", [("access", "user", "delete_user")]) + client.login(username="test", password="test") response = client.delete("/api/v1/users/dude") @@ -406,14 +438,16 @@ def test_delete_user_returns_500_if_cognito_inconsistent( assert cognito_client.return_value.disable_user.called -@patch('access.models.Client') -def test_delete_user_returns_503_if_cognito_down(cognito_client, user, django_user_factory, client): +@patch("access.models.Client") +def test_delete_user_returns_503_if_cognito_down( + cognito_client, user, django_user_factory, client +): cognito_client.return_value.disable_user.side_effect = EndpointConnectionError( - endpoint_url='http://localhost' + endpoint_url="http://localhost" ) - django_user_factory('test', 'test', [('access', 'user', 'delete_user')]) - client.login(username='test', password='test') + django_user_factory("test", "test", [("access", "user", "delete_user")]) + client.login(username="test", password="test") response = client.delete("/api/v1/users/dude") @@ -423,13 +457,15 @@ def test_delete_user_returns_503_if_cognito_down(cognito_client, user, django_us assert cognito_client.return_value.disable_user.called -@patch('access.models.Client') +@patch("access.models.Client") def test_delete_user_returns_401_if_not_logged_in( cognito_client, user, django_user_factory, client ): cognito_client.return_value.disable_user.return_value = True - response = client.delete("/api/v1/users/dude", data={}, content_type='application/json') + response = client.delete( + "/api/v1/users/dude", data={}, content_type="application/json" + ) assert response.status_code == 401 assert response.json() == {"code": 401, "description": "Unauthorized"} @@ -437,16 +473,18 @@ def test_delete_user_returns_401_if_not_logged_in( assert not cognito_client.return_value.disable_user.called -@patch('access.models.Client') +@patch("access.models.Client") def test_delete_user_returns_403_if_no_permission( cognito_client, user, django_user_factory, client ): cognito_client.return_value.disable_user.return_value = True - django_user_factory('test', 'test', []) - client.login(username='test', password='test') + django_user_factory("test", "test", []) + client.login(username="test", password="test") - response = client.delete("/api/v1/users/dude", data={}, content_type='application/json') + response = client.delete( + "/api/v1/users/dude", data={}, content_type="application/json" + ) assert response.status_code == 403 assert response.json() == {"code": 403, "description": "Forbidden"} @@ -462,8 +500,8 @@ def test_update_user_returns_401_if_not_logged_in(user, django_user_factory, cli def test_update_user_returns_403_if_no_permission(user, django_user_factory, client): - django_user_factory('test', 'test', []) - client.login(username='test', password='test') + django_user_factory("test", "test", []) + client.login(username="test", password="test") response = client.put("/api/v1/users/dude") @@ -471,14 +509,14 @@ def test_update_user_returns_403_if_no_permission(user, django_user_factory, cli assert response.json() == {"code": 403, "description": "Forbidden"} -@patch('access.models.Client') +@patch("access.models.Client") def test_update_user_updates_existing_user_as_expected( cognito_client, user, django_user_factory, client ): cognito_client.return_value.update_user.return_value = True - django_user_factory('test', 'test', [('access', 'user', 'change_user')]) - client.login(username='test', password='test') + django_user_factory("test", "test", [("access", "user", "change_user")]) + client.login(username="test", password="test") payload = { "username": "dude", @@ -488,7 +526,9 @@ def test_update_user_updates_existing_user_as_expected( "provider_id": "ch.bafu", } - response = client.put("/api/v1/users/dude", data=payload, content_type='application/json') + response = client.put( + "/api/v1/users/dude", data=payload, content_type="application/json" + ) assert response.status_code == 200 assert response.json() == payload @@ -506,9 +546,8 @@ def test_update_user_updates_existing_user_as_expected( def test_update_user_returns_404_and_leaves_user_as_is_if_user_nonexistent( user, django_user_factory, client ): - - django_user_factory('test', 'test', [('access', 'user', 'change_user')]) - client.login(username='test', password='test') + django_user_factory("test", "test", [("access", "user", "change_user")]) + client.login(username="test", password="test") user_before = User.objects.filter(username="dude").first() payload = { @@ -521,7 +560,9 @@ def test_update_user_returns_404_and_leaves_user_as_is_if_user_nonexistent( nonexistent_username = "maude" response = client.put( - f"/api/v1/users/{nonexistent_username}", data=payload, content_type='application/json' + f"/api/v1/users/{nonexistent_username}", + data=payload, + content_type="application/json", ) assert response.status_code == 404 @@ -533,9 +574,8 @@ def test_update_user_returns_404_and_leaves_user_as_is_if_user_nonexistent( def test_update_user_returns_400_and_leaves_user_as_is_if_provider_nonexistent( user, django_user_factory, client ): - - django_user_factory('test', 'test', [('access', 'user', 'change_user')]) - client.login(username='test', password='test') + django_user_factory("test", "test", [("access", "user", "change_user")]) + client.login(username="test", password="test") user_before = User.objects.filter(username="dude").first() payload = { @@ -546,7 +586,9 @@ def test_update_user_returns_400_and_leaves_user_as_is_if_provider_nonexistent( "provider_id": "nonexistent_id", } - response = client.put("/api/v1/users/dude", data=payload, content_type="application/json") + response = client.put( + "/api/v1/users/dude", data=payload, content_type="application/json" + ) assert response.status_code == 400 assert response.json() == {"code": 400, "description": "Provider does not exist"} @@ -554,14 +596,14 @@ def test_update_user_returns_400_and_leaves_user_as_is_if_provider_nonexistent( assert user_after == user_before -@patch('access.models.Client') +@patch("access.models.Client") def test_update_user_returns_500_and_leaves_user_as_is_if_cognito_inconsistent( cognito_client, user, django_user_factory, client ): cognito_client.return_value.update_user.return_value = False - django_user_factory('test', 'test', [('access', 'user', 'change_user')]) - client.login(username='test', password='test') + django_user_factory("test", "test", [("access", "user", "change_user")]) + client.login(username="test", password="test") user_before = User.objects.filter(username="dude").first() payload = { @@ -572,7 +614,9 @@ def test_update_user_returns_500_and_leaves_user_as_is_if_cognito_inconsistent( "provider_id": "ch.bafu", } - response = client.put("/api/v1/users/dude", data=payload, content_type="application/json") + response = client.put( + "/api/v1/users/dude", data=payload, content_type="application/json" + ) assert response.status_code == 500 assert response.json() == {"code": 500, "description": "Internal Server Error"} @@ -581,16 +625,16 @@ def test_update_user_returns_500_and_leaves_user_as_is_if_cognito_inconsistent( assert cognito_client.return_value.update_user.called -@patch('access.models.Client') +@patch("access.models.Client") def test_update_user_returns_503_and_leaves_user_as_is_if_cognito_down( cognito_client, user, django_user_factory, client ): cognito_client.return_value.update_user.side_effect = EndpointConnectionError( - endpoint_url='http://localhost' + endpoint_url="http://localhost" ) - django_user_factory('test', 'test', [('access', 'user', 'change_user')]) - client.login(username='test', password='test') + django_user_factory("test", "test", [("access", "user", "change_user")]) + client.login(username="test", password="test") user_before = User.objects.filter(username="dude").first() payload = { @@ -601,7 +645,9 @@ def test_update_user_returns_503_and_leaves_user_as_is_if_cognito_down( "provider_id": "ch.bafu", } - response = client.put("/api/v1/users/dude", data=payload, content_type="application/json") + response = client.put( + "/api/v1/users/dude", data=payload, content_type="application/json" + ) assert response.status_code == 503 assert response.json() == {"code": 503, "description": "Service Unavailable"} diff --git a/app/tests/access/test_access_models.py b/app/tests/access/test_access_models.py index d4c47047..a6c2a179 100644 --- a/app/tests/access/test_access_models.py +++ b/app/tests/access/test_access_models.py @@ -10,7 +10,7 @@ from django.utils import timezone -@patch('access.models.Client') +@patch("access.models.Client") def test_user_stored_as_expected_for_valid_input(client, provider): client.return_value.create_user.return_value = True @@ -34,7 +34,7 @@ def test_user_stored_as_expected_for_valid_input(client, provider): assert client.return_value.create_user.called -@patch('access.models.Client') +@patch("access.models.Client") def test_user_raises_exception_for_user_with_existing_user_name(client, provider): client.return_value.create_user.return_value = True @@ -43,7 +43,7 @@ def test_user_raises_exception_for_user_with_existing_user_name(client, provider first_name="Jeffrey", last_name="Lebowski", email="dude@bowling.com", - provider=provider + provider=provider, ) with raises(ValidationError): User.objects.create( @@ -51,15 +51,17 @@ def test_user_raises_exception_for_user_with_existing_user_name(client, provider first_name="XXX", last_name="YYY", email="xxx@yyy.com", - provider=provider + provider=provider, ) assert User.objects.count() == 1 assert client.return_value.create_user.call_count == 1 -@patch('access.models.Client') -def test_user_with_invalid_email_raises_exception_when_creating_db_record(client, provider): +@patch("access.models.Client") +def test_user_with_invalid_email_raises_exception_when_creating_db_record( + client, provider +): client.return_value.create_user.return_value = True model_fields = { @@ -77,8 +79,8 @@ def test_user_with_invalid_email_raises_exception_when_creating_db_record(client assert not client.return_value.create_user.called -@patch('access.models.Client') -@patch('access.models.logger') +@patch("access.models.Client") +@patch("access.models.logger") def test_create_user_raises_cognito_exception(logger, client, provider): client.return_value.create_user.return_value = False @@ -96,12 +98,13 @@ def test_create_user_raises_cognito_exception(logger, client, provider): assert User.objects.count() == 0 assert client.return_value.create_user.called - assert call.critical( - 'User %s already exists in cognito, not created', '2ihg2ox304po' - ) in logger.mock_calls + assert ( + call.critical("User %s already exists in cognito, not created", "2ihg2ox304po") + in logger.mock_calls + ) -@patch('access.models.Client') +@patch("access.models.Client") def test_save_user_updates_records(client, provider): client.return_value.create_user.return_value = True client.return_value.update_user.return_value = True @@ -125,11 +128,11 @@ def test_save_user_updates_records(client, provider): updated = User.objects.first() - assert updated.email == 'jeffrey.lebowski@bowling.com' + assert updated.email == "jeffrey.lebowski@bowling.com" assert client.return_value.update_user.called -@patch('access.models.Client') +@patch("access.models.Client") def test_save_disabled_user_updates_records(client, provider): client.return_value.create_user.return_value = True client.return_value.update_user.return_value = True @@ -140,7 +143,7 @@ def test_save_disabled_user_updates_records(client, provider): "last_name": "Lebowski", "email": "dude@bowling.com", "provider": provider, - "deleted_at": timezone.now() + "deleted_at": timezone.now(), } User.objects.create(**model_fields) @@ -154,11 +157,11 @@ def test_save_disabled_user_updates_records(client, provider): updated = User.all_objects.first() - assert updated.email == 'jeffrey.lebowski@bowling.com' + assert updated.email == "jeffrey.lebowski@bowling.com" assert client.return_value.update_user.called -@patch('access.models.Client') +@patch("access.models.Client") def test_save_user_raises_cognito_exception(client, provider): client.return_value.create_user.return_value = True client.return_value.update_user.return_value = False @@ -183,11 +186,11 @@ def test_save_user_raises_cognito_exception(client, provider): retained = User.objects.first() - assert retained.email == 'dude@bowling.com' + assert retained.email == "dude@bowling.com" assert client.return_value.update_user.called -@patch('access.models.Client') +@patch("access.models.Client") def test_delete_user_deletes_records(client, provider): client.return_value.create_user.return_value = True client.return_value.delete_user.return_value = True @@ -211,7 +214,7 @@ def test_delete_user_deletes_records(client, provider): assert client.return_value.delete_user.called -@patch('access.models.Client') +@patch("access.models.Client") def test_delete_disabled_user_deletes_records(client, provider): client.return_value.create_user.return_value = True client.return_value.delete_user.return_value = True @@ -222,7 +225,7 @@ def test_delete_disabled_user_deletes_records(client, provider): "last_name": "Lebowski", "email": "dude@bowling.com", "provider": provider, - "deleted_at": timezone.now() + "deleted_at": timezone.now(), } User.objects.create(**model_fields) @@ -236,7 +239,7 @@ def test_delete_disabled_user_deletes_records(client, provider): assert client.return_value.delete_user.called -@patch('access.models.Client') +@patch("access.models.Client") def test_delete_user_raises_cognito_exception(client, provider): client.return_value.create_user.return_value = True client.return_value.delete_user.return_value = False @@ -261,7 +264,7 @@ def test_delete_user_raises_cognito_exception(client, provider): assert client.return_value.delete_user.called -@patch('access.models.Client') +@patch("access.models.Client") def test_disable_user_disables_records(client, provider): client.return_value.create_user.return_value = True client.return_value.disable_user.return_value = True @@ -286,7 +289,7 @@ def test_disable_user_disables_records(client, provider): assert client.return_value.disable_user.called -@patch('access.models.Client') +@patch("access.models.Client") def test_disable_user_raises_cognito_exception(client, provider): client.return_value.create_user.return_value = True client.return_value.disable_user.return_value = False @@ -311,12 +314,11 @@ def test_disable_user_raises_cognito_exception(client, provider): assert client.return_value.disable_user.called -@patch('access.models.Client') +@patch("access.models.Client") def test_form_invalid_for_user_with_invalid_email(client, provider): client.return_value.create_user.return_value = True class UserForm(ModelForm): - class Meta: model = User fields = "__all__" diff --git a/app/tests/bod/test_bod_sync_command.py b/app/tests/bod/test_bod_sync_command.py index 48b6653e..cfb970f7 100644 --- a/app/tests/bod/test_bod_sync_command.py +++ b/app/tests/bod/test_bod_sync_command.py @@ -12,14 +12,14 @@ from django.core.management import call_command -@fixture(name='bod_translation') +@fixture(name="bod_translation") def fixture_bod_translation(db): yield BodTranslations.objects.create( msg_id="ch.bafu", de="BAFU", fr="OFEV", en="FOEN", it="UFAM", rm="UFAM" ) -@fixture(name='bod_contact_organisation') +@fixture(name="bod_contact_organisation") def fixture_bod_contact_organisation(bod_translation): yield BodContactOrganisation.objects.create( pk_contactorganisation_id=17, @@ -33,22 +33,22 @@ def fixture_bod_contact_organisation(bod_translation): abkuerzung_en="FOEN", abkuerzung_it="UFAM", abkuerzung_rm="UFAM", - attribution="ch.bafu" + attribution="ch.bafu", ) -@fixture(name='bod_dataset') +@fixture(name="bod_dataset") def fixture_bod_dataset(bod_contact_organisation): yield BodDataset.objects.create( id=170, id_dataset="ch.bafu.auen-vegetationskarten", fk_geocat="ab76361f-657d-4705-9053-95f89ecab126", fk_contactorganisation_id=bod_contact_organisation.pk_contactorganisation_id, - staging="prod" + staging="prod", ) -@fixture(name='bod_geocat_publish') +@fixture(name="bod_geocat_publish") def fixture_bod_geocat_publish(bod_dataset): yield BodGeocatPublish.objects.create( bgdi_id=170, @@ -62,11 +62,11 @@ def fixture_bod_geocat_publish(bod_dataset): abstract_fr="abstract_vegetationskarten_fr", abstract_it="abstract_vegetationskarten_it", abstract_rm="abstract_vegetationskarten_rm", - abstract_en="abstract_vegetationskarten_en" + abstract_en="abstract_vegetationskarten_en", ) -@fixture(name='bod_geocat_publish_missing_english') +@fixture(name="bod_geocat_publish_missing_english") def fixture_bod_geocat_publish_missing_english(bod_dataset): yield BodGeocatPublish.objects.create( bgdi_id=170, @@ -81,7 +81,12 @@ def fixture_bod_geocat_publish_missing_english(bod_dataset): def test_command_imports(bod_geocat_publish): out = StringIO() call_command( - "bod_sync", providers=True, attributions=True, datasets=True, verbosity=2, stdout=out + "bod_sync", + providers=True, + attributions=True, + datasets=True, + verbosity=2, + stdout=out, ) assert "Added provider 'Federal Office for the Environment'" in out.getvalue() assert "1 provider(s) added" in out.getvalue() @@ -136,23 +141,38 @@ def test_command_imports(bod_geocat_publish): def test_command_does_not_need_to_import(db): out = StringIO() call_command( - "bod_sync", providers=True, attributions=True, datasets=True, verbosity=2, stdout=out + "bod_sync", + providers=True, + attributions=True, + datasets=True, + verbosity=2, + stdout=out, ) - assert 'nothing to be done, already in sync' in out.getvalue() + assert "nothing to be done, already in sync" in out.getvalue() def test_command_no_flag_set(bod_geocat_publish): out = StringIO() call_command( - "bod_sync", providers=False, attributions=False, datasets=False, verbosity=2, stdout=out + "bod_sync", + providers=False, + attributions=False, + datasets=False, + verbosity=2, + stdout=out, ) - assert 'no option provided, nothing changed' in out.getvalue() + assert "no option provided, nothing changed" in out.getvalue() def test_command_imports_providers(bod_geocat_publish): out = StringIO() call_command( - "bod_sync", providers=True, attributions=False, datasets=False, verbosity=2, stdout=out + "bod_sync", + providers=True, + attributions=False, + datasets=False, + verbosity=2, + stdout=out, ) assert "Added provider 'Federal Office for the Environment'" in out.getvalue() @@ -182,7 +202,12 @@ def test_command_skips_invalid_providers(bod_contact_organisation, bod_geocat_pu out = StringIO() call_command( - "bod_sync", providers=True, attributions=False, datasets=False, verbosity=2, stdout=out + "bod_sync", + providers=True, + attributions=False, + datasets=False, + verbosity=2, + stdout=out, ) assert "nothing to be done" in out.getvalue() assert Provider.objects.count() == 0 @@ -199,12 +224,17 @@ def test_command_imports_attributions(bod_contact_organisation, bod_geocat_publi acronym_de="BAFU", acronym_fr="XXX", acronym_en="XXX", - _legacy_id=bod_contact_organisation.pk_contactorganisation_id + _legacy_id=bod_contact_organisation.pk_contactorganisation_id, ) out = StringIO() call_command( - "bod_sync", providers=False, attributions=True, datasets=False, verbosity=2, stdout=out + "bod_sync", + providers=False, + attributions=True, + datasets=False, + verbosity=2, + stdout=out, ) assert "Added attribution 'ch.bafu'" in out.getvalue() assert "1 attribution(s) added" in out.getvalue() @@ -226,18 +256,26 @@ def test_command_imports_attributions(bod_contact_organisation, bod_geocat_publi assert attribution.description_rm == "UFAM" -def test_command_skips_invalid_attributions(bod_contact_organisation, bod_geocat_publish): +def test_command_skips_invalid_attributions( + bod_contact_organisation, bod_geocat_publish +): for attribution in (bod_contact_organisation.attribution, "", None): bod_contact_organisation.attribution = attribution bod_contact_organisation.save() out = StringIO() call_command( - "bod_sync", providers=False, attributions=True, datasets=False, verbosity=2, stdout=out + "bod_sync", + providers=False, + attributions=True, + datasets=False, + verbosity=2, + stdout=out, ) - assert (f"skipping attribution '{attribution}' as no matching provider was found" - ) in out.getvalue() - assert 'nothing to be done, already in sync' in out.getvalue() + assert ( + f"skipping attribution '{attribution}' as no matching provider was found" + ) in out.getvalue() + assert "nothing to be done, already in sync" in out.getvalue() assert Provider.objects.count() == 0 assert Attribution.objects.count() == 0 assert Dataset.objects.count() == 0 @@ -252,7 +290,7 @@ def test_command_imports_datasets(bod_contact_organisation, bod_geocat_publish): acronym_de="BAFU", acronym_fr="XXX", acronym_en="XXX", - _legacy_id=bod_contact_organisation.pk_contactorganisation_id + _legacy_id=bod_contact_organisation.pk_contactorganisation_id, ) attribution = Attribution.objects.create( attribution_id="ch.bafu", @@ -263,12 +301,17 @@ def test_command_imports_datasets(bod_contact_organisation, bod_geocat_publish): description_fr="XXX", description_en="XXX", provider=provider, - _legacy_id=bod_contact_organisation.pk_contactorganisation_id + _legacy_id=bod_contact_organisation.pk_contactorganisation_id, ) out = StringIO() call_command( - "bod_sync", providers=False, attributions=False, datasets=True, verbosity=2, stdout=out + "bod_sync", + providers=False, + attributions=False, + datasets=True, + verbosity=2, + stdout=out, ) assert "Added dataset 'ch.bafu.auen-vegetationskarten'" in out.getvalue() assert "1 dataset(s) added" in out.getvalue() @@ -303,7 +346,7 @@ def test_command_imports_datasets_missing_english( acronym_de="BAFU", acronym_fr="XXX", acronym_en="XXX", - _legacy_id=bod_contact_organisation.pk_contactorganisation_id + _legacy_id=bod_contact_organisation.pk_contactorganisation_id, ) attribution = Attribution.objects.create( attribution_id="ch.bafu", @@ -314,12 +357,17 @@ def test_command_imports_datasets_missing_english( description_fr="XXX", description_en="XXX", provider=provider, - _legacy_id=bod_contact_organisation.pk_contactorganisation_id + _legacy_id=bod_contact_organisation.pk_contactorganisation_id, ) out = StringIO() call_command( - "bod_sync", providers=False, attributions=False, datasets=True, verbosity=2, stdout=out + "bod_sync", + providers=False, + attributions=False, + datasets=True, + verbosity=2, + stdout=out, ) assert "Added dataset 'ch.bafu.auen-vegetationskarten'" in out.getvalue() assert "1 dataset(s) added" in out.getvalue() @@ -352,7 +400,7 @@ def test_command_imports_datasets_missing_geocat(bod_contact_organisation, bod_d acronym_de="BAFU", acronym_fr="XXX", acronym_en="XXX", - _legacy_id=bod_contact_organisation.pk_contactorganisation_id + _legacy_id=bod_contact_organisation.pk_contactorganisation_id, ) attribution = Attribution.objects.create( attribution_id="ch.bafu", @@ -363,12 +411,17 @@ def test_command_imports_datasets_missing_geocat(bod_contact_organisation, bod_d description_fr="XXX", description_en="XXX", provider=provider, - _legacy_id=bod_contact_organisation.pk_contactorganisation_id + _legacy_id=bod_contact_organisation.pk_contactorganisation_id, ) out = StringIO() call_command( - "bod_sync", providers=False, attributions=False, datasets=True, verbosity=2, stdout=out + "bod_sync", + providers=False, + attributions=False, + datasets=True, + verbosity=2, + stdout=out, ) assert "Added dataset 'ch.bafu.auen-vegetationskarten'" in out.getvalue() assert "1 dataset(s) added" in out.getvalue() @@ -395,32 +448,42 @@ def test_command_imports_datasets_missing_geocat(bod_contact_organisation, bod_d def test_command_skips_invalid_datasets(bod_geocat_publish): out = StringIO() call_command( - "bod_sync", providers=False, attributions=False, datasets=True, verbosity=2, stdout=out + "bod_sync", + providers=False, + attributions=False, + datasets=True, + verbosity=2, + stdout=out, ) assert ( - "skipping dataset 'ch.bafu.auen-vegetationskarten' " + - "as no matching attribution was found" + "skipping dataset 'ch.bafu.auen-vegetationskarten' " + + "as no matching attribution was found" ) in out.getvalue() - assert 'nothing to be done, already in sync' in out.getvalue() + assert "nothing to be done, already in sync" in out.getvalue() assert Provider.objects.count() == 0 assert Attribution.objects.count() == 0 assert Dataset.objects.count() == 0 def test_command_skips_non_prod_datasets(bod_dataset, bod_geocat_publish): - bod_dataset.staging = 'test' + bod_dataset.staging = "test" out = StringIO() call_command( - "bod_sync", providers=False, attributions=False, datasets=True, verbosity=2, stdout=out - ) - assert 'nothing to be done, already in sync' in out.getvalue() + "bod_sync", + providers=False, + attributions=False, + datasets=True, + verbosity=2, + stdout=out, + ) + assert "nothing to be done, already in sync" in out.getvalue() assert Provider.objects.count() == 0 assert Attribution.objects.count() == 0 assert Dataset.objects.count() == 0 -#pylint: disable=too-many-statements +# pylint: disable=too-many-statements def test_command_updates(bod_contact_organisation, bod_dataset, bod_geocat_publish): # Add objects that will be updated provider = Provider.objects.create( @@ -431,7 +494,7 @@ def test_command_updates(bod_contact_organisation, bod_dataset, bod_geocat_publi acronym_de="BAFU", acronym_fr="XXX", acronym_en="XXX", - _legacy_id=bod_contact_organisation.pk_contactorganisation_id + _legacy_id=bod_contact_organisation.pk_contactorganisation_id, ) attribution = Attribution.objects.create( attribution_id="ch.bafu", @@ -442,7 +505,7 @@ def test_command_updates(bod_contact_organisation, bod_dataset, bod_geocat_publi description_fr="XXX", description_en="XXX", provider=provider, - _legacy_id=bod_contact_organisation.pk_contactorganisation_id + _legacy_id=bod_contact_organisation.pk_contactorganisation_id, ) dataset = Dataset.objects.create( dataset_id="xxx", @@ -455,12 +518,17 @@ def test_command_updates(bod_contact_organisation, bod_dataset, bod_geocat_publi description_en="xxx", provider=provider, attribution=attribution, - _legacy_id=bod_dataset.id + _legacy_id=bod_dataset.id, ) out = StringIO() call_command( - "bod_sync", providers=True, attributions=True, datasets=True, verbosity=2, stdout=out + "bod_sync", + providers=True, + attributions=True, + datasets=True, + verbosity=2, + stdout=out, ) assert f"Changed Provider {provider.id} name_de" in out.getvalue() assert f"Changed Provider {provider.id} acronym_de" not in out.getvalue() @@ -525,7 +593,7 @@ def test_command_removes_orphaned_provider(bod_geocat_publish): acronym_de="XXX", acronym_fr="XXX", acronym_en="XXX", - _legacy_id=16 + _legacy_id=16, ) attribution = Attribution.objects.create( attribution_id="ch.xxx", @@ -536,7 +604,7 @@ def test_command_removes_orphaned_provider(bod_geocat_publish): description_fr="XXX", description_en="XXX", provider=provider, - _legacy_id=16 + _legacy_id=16, ) Dataset.objects.create( dataset_id="xxx", @@ -549,7 +617,7 @@ def test_command_removes_orphaned_provider(bod_geocat_publish): description_en="xxx", provider=provider, attribution=attribution, - _legacy_id=160 + _legacy_id=160, ) # Add objects which will not be removed @@ -570,7 +638,7 @@ def test_command_removes_orphaned_provider(bod_geocat_publish): description_de="YYY", description_fr="YYY", description_en="YYY", - provider=provider + provider=provider, ) Dataset.objects.create( dataset_id="yyyy", @@ -582,12 +650,17 @@ def test_command_removes_orphaned_provider(bod_geocat_publish): description_fr="yyyy", description_en="yyyy", provider=provider, - attribution=attribution + attribution=attribution, ) out = StringIO() call_command( - "bod_sync", providers=True, attributions=True, datasets=True, verbosity=2, stdout=out + "bod_sync", + providers=True, + attributions=True, + datasets=True, + verbosity=2, + stdout=out, ) assert "1 provider(s) removed" in out.getvalue() assert "1 attribution(s) removed" in out.getvalue() @@ -598,10 +671,15 @@ def test_command_removes_orphaned_provider(bod_geocat_publish): assert Provider.objects.count() == 2 assert Attribution.objects.count() == 2 assert Dataset.objects.count() == 2 - assert {'BAFU', 'YYYY'} == set(Provider.objects.values_list('acronym_de', flat=True)) - assert {'BAFU', 'YYYY'} == set(Attribution.objects.values_list('name_de', flat=True)) - assert {'ch.bafu.auen-vegetationskarten', - 'yyyy'} == set(Dataset.objects.values_list('dataset_id', flat=True)) + assert {"BAFU", "YYYY"} == set( + Provider.objects.values_list("acronym_de", flat=True) + ) + assert {"BAFU", "YYYY"} == set( + Attribution.objects.values_list("name_de", flat=True) + ) + assert {"ch.bafu.auen-vegetationskarten", "yyyy"} == set( + Dataset.objects.values_list("dataset_id", flat=True) + ) def test_command_removes_orphaned_attribution(bod_contact_organisation): @@ -614,7 +692,7 @@ def test_command_removes_orphaned_attribution(bod_contact_organisation): acronym_de="XXX", acronym_fr="XXX", acronym_en="XXX", - _legacy_id=bod_contact_organisation.pk_contactorganisation_id + _legacy_id=bod_contact_organisation.pk_contactorganisation_id, ) # Add objects which will be removed @@ -627,7 +705,7 @@ def test_command_removes_orphaned_attribution(bod_contact_organisation): description_fr="XXX", description_en="XXX", provider=provider, - _legacy_id=16 + _legacy_id=16, ) Dataset.objects.create( dataset_id="xxx", @@ -640,12 +718,17 @@ def test_command_removes_orphaned_attribution(bod_contact_organisation): description_en="xxx", provider=provider, attribution=attribution, - _legacy_id=160 + _legacy_id=160, ) out = StringIO() call_command( - "bod_sync", providers=True, attributions=True, datasets=True, verbosity=2, stdout=out + "bod_sync", + providers=True, + attributions=True, + datasets=True, + verbosity=2, + stdout=out, ) assert "provider(s) removed" not in out.getvalue() assert "1 attribution(s) removed" in out.getvalue() @@ -656,8 +739,8 @@ def test_command_removes_orphaned_attribution(bod_contact_organisation): assert Provider.objects.count() == 1 assert Attribution.objects.count() == 1 assert Dataset.objects.count() == 0 - assert {'BAFU'} == set(Provider.objects.values_list('acronym_de', flat=True)) - assert {'BAFU'} == set(Attribution.objects.values_list('name_de', flat=True)) + assert {"BAFU"} == set(Provider.objects.values_list("acronym_de", flat=True)) + assert {"BAFU"} == set(Attribution.objects.values_list("name_de", flat=True)) def test_command_removes_orphaned_dataset(bod_contact_organisation): @@ -670,7 +753,7 @@ def test_command_removes_orphaned_dataset(bod_contact_organisation): acronym_de="XXX", acronym_fr="XXX", acronym_en="XXX", - _legacy_id=bod_contact_organisation.pk_contactorganisation_id + _legacy_id=bod_contact_organisation.pk_contactorganisation_id, ) attribution = Attribution.objects.create( attribution_id="ch.xxx", @@ -681,7 +764,7 @@ def test_command_removes_orphaned_dataset(bod_contact_organisation): description_fr="XXX", description_en="XXX", provider=provider, - _legacy_id=bod_contact_organisation.pk_contactorganisation_id + _legacy_id=bod_contact_organisation.pk_contactorganisation_id, ) # Add objects which will be removed @@ -696,12 +779,17 @@ def test_command_removes_orphaned_dataset(bod_contact_organisation): description_en="xxx", provider=provider, attribution=attribution, - _legacy_id=160 + _legacy_id=160, ) out = StringIO() call_command( - "bod_sync", providers=True, attributions=True, datasets=True, verbosity=2, stdout=out + "bod_sync", + providers=True, + attributions=True, + datasets=True, + verbosity=2, + stdout=out, ) assert "provider(s) removed" not in out.getvalue() assert "attribution(s) removed" not in out.getvalue() @@ -712,14 +800,19 @@ def test_command_removes_orphaned_dataset(bod_contact_organisation): assert Provider.objects.count() == 1 assert Attribution.objects.count() == 1 assert Dataset.objects.count() == 0 - assert {'BAFU'} == set(Provider.objects.values_list('acronym_de', flat=True)) - assert {'BAFU'} == set(Attribution.objects.values_list('name_de', flat=True)) + assert {"BAFU"} == set(Provider.objects.values_list("acronym_de", flat=True)) + assert {"BAFU"} == set(Attribution.objects.values_list("name_de", flat=True)) def test_command_does_not_import_if_dry_run(bod_geocat_publish): out = StringIO() call_command( - "bod_sync", providers=True, attributions=True, datasets=True, dry_run=True, stdout=out + "bod_sync", + providers=True, + attributions=True, + datasets=True, + dry_run=True, + stdout=out, ) assert "1 provider(s) added" in out.getvalue() assert "1 attribution(s) added" in out.getvalue() @@ -739,7 +832,7 @@ def test_command_clears_existing_data(bod_geocat_publish): acronym_de="XXX", acronym_fr="XXX", acronym_en="XXX", - _legacy_id=150 + _legacy_id=150, ) attribution = Attribution.objects.create( attribution_id="ch.xxx", @@ -749,7 +842,7 @@ def test_command_clears_existing_data(bod_geocat_publish): description_de="XXX", description_fr="XXX", description_en="XXX", - provider=provider + provider=provider, ) Dataset.objects.create( dataset_id="yyyy", @@ -761,12 +854,17 @@ def test_command_clears_existing_data(bod_geocat_publish): description_fr="yyyy", description_en="yyyy", provider=provider, - attribution=attribution + attribution=attribution, ) out = StringIO() call_command( - "bod_sync", clear=True, providers=True, attributions=True, datasets=True, stdout=out + "bod_sync", + clear=True, + providers=True, + attributions=True, + datasets=True, + stdout=out, ) assert "1 provider(s) cleared" in out.getvalue() assert "1 attribution(s) cleared" in out.getvalue() diff --git a/app/tests/cognito/test_cognito_client.py b/app/tests/cognito/test_cognito_client.py index 87baff64..d81ab456 100644 --- a/app/tests/cognito/test_cognito_client.py +++ b/app/tests/cognito/test_cognito_client.py @@ -6,290 +6,325 @@ def test_user_attributes_to_dict(): - attributes = [{'Name': 'email', 'Value': 'test@example.org'}, {'Name': 'flag', 'Value': 'true'}] + attributes = [ + {"Name": "email", "Value": "test@example.org"}, + {"Name": "flag", "Value": "true"}, + ] attributes = user_attributes_to_dict(attributes) - assert attributes == {'email': 'test@example.org', 'flag': 'true'} + assert attributes == {"email": "test@example.org", "flag": "true"} -@patch('cognito.utils.client.client') +@patch("cognito.utils.client.client") def test_list_users_returns_only_managed(boto3, cognito_user_response_factory): - managed = cognito_user_response_factory('2ihg2ox304po', '1234', managed=True) - unmanaged = cognito_user_response_factory('2ihg2ox304po', '1234', managed=False) - boto3.return_value.list_users.return_value = {'Users': [managed, unmanaged]} + managed = cognito_user_response_factory("2ihg2ox304po", "1234", managed=True) + unmanaged = cognito_user_response_factory("2ihg2ox304po", "1234", managed=False) + boto3.return_value.list_users.return_value = {"Users": [managed, unmanaged]} client = Client() users = client.list_users() assert users == [managed] - assert call().list_users(UserPoolId=client.user_pool_id, Limit=60) in boto3.mock_calls + assert ( + call().list_users(UserPoolId=client.user_pool_id, Limit=60) in boto3.mock_calls + ) -@patch('cognito.utils.client.client') +@patch("cognito.utils.client.client") def test_list_users_pagination(boto3, cognito_user_response_factory): users = [ - cognito_user_response_factory(f'2ihg2ox304po{count}', str(count), True) + cognito_user_response_factory(f"2ihg2ox304po{count}", str(count), True) for count in range(1, 131) ] - response_1 = {'Users': users[0:60], 'PaginationToken': '1'} - response_2 = {'Users': users[60:120], 'PaginationToken': '2'} - response_3 = {'Users': users[120:130]} + response_1 = {"Users": users[0:60], "PaginationToken": "1"} + response_2 = {"Users": users[60:120], "PaginationToken": "2"} + response_3 = {"Users": users[120:130]} boto3.return_value.list_users.side_effect = [response_1, response_2, response_3] client = Client() users = client.list_users() - assert [user['Username'] for user in users] == [f'2ihg2ox304po{x}' for x in range(1, 131)] - assert call().list_users(UserPoolId=client.user_pool_id, Limit=60) in boto3.mock_calls - assert call().list_users( - UserPoolId=client.user_pool_id, Limit=60, PaginationToken='2' - ) in boto3.mock_calls - assert call().list_users( - UserPoolId=client.user_pool_id, Limit=60, PaginationToken='2' - ) in boto3.mock_calls + assert [user["Username"] for user in users] == [ + f"2ihg2ox304po{x}" for x in range(1, 131) + ] + assert ( + call().list_users(UserPoolId=client.user_pool_id, Limit=60) in boto3.mock_calls + ) + assert ( + call().list_users(UserPoolId=client.user_pool_id, Limit=60, PaginationToken="2") + in boto3.mock_calls + ) + assert ( + call().list_users(UserPoolId=client.user_pool_id, Limit=60, PaginationToken="2") + in boto3.mock_calls + ) -@patch('cognito.utils.client.client') +@patch("cognito.utils.client.client") def test_get_user_returns_managed(boto3, cognito_user_response_factory): response = cognito_user_response_factory( - '2ihg2ox304po', '1234', managed=True, attributes_key='UserAttributes' + "2ihg2ox304po", "1234", managed=True, attributes_key="UserAttributes" ) boto3.return_value.admin_get_user.return_value = response client = Client() - user = client.get_user('1234') + user = client.get_user("1234") assert user == response - assert call().admin_get_user( - UserPoolId=client.user_pool_id, Username='1234' - ) in boto3.mock_calls + assert ( + call().admin_get_user(UserPoolId=client.user_pool_id, Username="1234") + in boto3.mock_calls + ) -@patch('cognito.utils.client.client') +@patch("cognito.utils.client.client") def test_get_user_does_not_return_unmanaged(boto3, cognito_user_response_factory): boto3.return_value.admin_get_user.return_value = cognito_user_response_factory( - '2ihg2ox304po', '1234', managed=False, attributes_key='UserAttributes' + "2ihg2ox304po", "1234", managed=False, attributes_key="UserAttributes" ) client = Client() - user = client.get_user('1234') + user = client.get_user("1234") assert user is None - assert call().admin_get_user( - UserPoolId=client.user_pool_id, Username='1234' - ) in boto3.mock_calls + assert ( + call().admin_get_user(UserPoolId=client.user_pool_id, Username="1234") + in boto3.mock_calls + ) -@patch('cognito.utils.client.client') +@patch("cognito.utils.client.client") def test_get_user_returns_unmanaged(boto3, cognito_user_response_factory): response = cognito_user_response_factory( - '2ihg2ox304po', '1234', managed=False, attributes_key='UserAttributes' + "2ihg2ox304po", "1234", managed=False, attributes_key="UserAttributes" ) boto3.return_value.admin_get_user.return_value = response client = Client() - user = client.get_user('1234', return_unmanaged=True) + user = client.get_user("1234", return_unmanaged=True) assert user == response - assert call().admin_get_user( - UserPoolId=client.user_pool_id, Username='1234' - ) in boto3.mock_calls + assert ( + call().admin_get_user(UserPoolId=client.user_pool_id, Username="1234") + in boto3.mock_calls + ) -@patch('cognito.utils.client.client') +@patch("cognito.utils.client.client") def test_create_user_creates_managed(boto3, cognito_user_response_factory): boto3.return_value.admin_get_user.return_value = None client = Client() - created = client.create_user('2ihg2ox304po', '1234', 'test@example.org') + created = client.create_user("2ihg2ox304po", "1234", "test@example.org") assert created is True - assert '().admin_create_user' in [call[0] for call in boto3.mock_calls] - assert call().admin_create_user( - UserPoolId=client.user_pool_id, - Username='2ihg2ox304po', - UserAttributes=[{ - 'Name': 'email', 'Value': 'test@example.org' - }, { - 'Name': 'email_verified', 'Value': 'true' - }, { - 'Name': 'preferred_username', 'Value': '1234' - }, { - 'Name': client.managed_flag_name, 'Value': 'true' - }], - DesiredDeliveryMediums=['EMAIL'] - ) in boto3.mock_calls - - -@patch('cognito.utils.client.client') -def test_create_user_does_not_create_if_managed_exists(boto3, cognito_user_response_factory): + assert "().admin_create_user" in [call[0] for call in boto3.mock_calls] + assert ( + call().admin_create_user( + UserPoolId=client.user_pool_id, + Username="2ihg2ox304po", + UserAttributes=[ + {"Name": "email", "Value": "test@example.org"}, + {"Name": "email_verified", "Value": "true"}, + {"Name": "preferred_username", "Value": "1234"}, + {"Name": client.managed_flag_name, "Value": "true"}, + ], + DesiredDeliveryMediums=["EMAIL"], + ) + in boto3.mock_calls + ) + + +@patch("cognito.utils.client.client") +def test_create_user_does_not_create_if_managed_exists( + boto3, cognito_user_response_factory +): boto3.return_value.admin_get_user.return_value = cognito_user_response_factory( - '2ihg2ox304po', '1234', managed=True, attributes_key='UserAttributes' + "2ihg2ox304po", "1234", managed=True, attributes_key="UserAttributes" ) client = Client() - created = client.create_user('2ihg2ox304po', '1234', 'test@example.org') + created = client.create_user("2ihg2ox304po", "1234", "test@example.org") assert created is False - assert '().admin_create_user' not in [call[0] for call in boto3.mock_calls] + assert "().admin_create_user" not in [call[0] for call in boto3.mock_calls] -@patch('cognito.utils.client.client') -def test_create_user_does_not_create_if_unmanaged_exists(boto3, cognito_user_response_factory): +@patch("cognito.utils.client.client") +def test_create_user_does_not_create_if_unmanaged_exists( + boto3, cognito_user_response_factory +): boto3.return_value.admin_get_user.return_value = cognito_user_response_factory( - '2ihg2ox304po', '1234', managed=False, attributes_key='UserAttributes' + "2ihg2ox304po", "1234", managed=False, attributes_key="UserAttributes" ) client = Client() - created = client.create_user('2ihg2ox304po', '1234', 'test@example.org') + created = client.create_user("2ihg2ox304po", "1234", "test@example.org") assert created is False - assert '().admin_create_user' not in [call[0] for call in boto3.mock_calls] + assert "().admin_create_user" not in [call[0] for call in boto3.mock_calls] -@patch('cognito.utils.client.client') +@patch("cognito.utils.client.client") def test_delete_user_deletes_managed(boto3, cognito_user_response_factory): boto3.return_value.admin_get_user.return_value = cognito_user_response_factory( - '2ihg2ox304po', '1234', managed=True, attributes_key='UserAttributes' + "2ihg2ox304po", "1234", managed=True, attributes_key="UserAttributes" ) client = Client() - deleted = client.delete_user('1234') + deleted = client.delete_user("1234") assert deleted is True - assert call().admin_delete_user( - UserPoolId=client.user_pool_id, Username='1234' - ) in boto3.mock_calls + assert ( + call().admin_delete_user(UserPoolId=client.user_pool_id, Username="1234") + in boto3.mock_calls + ) -@patch('cognito.utils.client.client') +@patch("cognito.utils.client.client") def test_delete_user_does_not_delete_unmanaged(boto3, cognito_user_response_factory): boto3.return_value.admin_get_user.return_value = cognito_user_response_factory( - '2ihg2ox304po', '1234', managed=False, attributes_key='UserAttributes' + "2ihg2ox304po", "1234", managed=False, attributes_key="UserAttributes" ) client = Client() - deleted = client.delete_user('1234') + deleted = client.delete_user("1234") assert deleted is False - assert call().admin_delete_user( - UserPoolId=client.user_pool_id, Username='1234' - ) not in boto3.mock_calls + assert ( + call().admin_delete_user(UserPoolId=client.user_pool_id, Username="1234") + not in boto3.mock_calls + ) -@patch('cognito.utils.client.client') +@patch("cognito.utils.client.client") def test_update_user_updates_managed(boto3, cognito_user_response_factory): boto3.return_value.admin_get_user.return_value = cognito_user_response_factory( - '2ihg2ox304po', '1234', managed=True, attributes_key='UserAttributes' + "2ihg2ox304po", "1234", managed=True, attributes_key="UserAttributes" ) client = Client() - updated = client.update_user('2ihg2ox304po', '5678', 'new@example.org') + updated = client.update_user("2ihg2ox304po", "5678", "new@example.org") assert updated is True - assert '().admin_update_user_attributes' in [call[0] for call in boto3.mock_calls] - assert '().admin_reset_user_password' in [call[0] for call in boto3.mock_calls] - assert call().admin_update_user_attributes( - UserPoolId=client.user_pool_id, - Username='2ihg2ox304po', - UserAttributes=[{ - 'Name': 'email', 'Value': 'new@example.org' - }, { - 'Name': 'email_verified', 'Value': 'true' - }, { - 'Name': 'preferred_username', 'Value': '5678' - }] - ) in boto3.mock_calls - assert call().admin_reset_user_password( - UserPoolId=client.user_pool_id, Username='2ihg2ox304po' - ) in boto3.mock_calls - - -@patch('cognito.utils.client.client') + assert "().admin_update_user_attributes" in [call[0] for call in boto3.mock_calls] + assert "().admin_reset_user_password" in [call[0] for call in boto3.mock_calls] + assert ( + call().admin_update_user_attributes( + UserPoolId=client.user_pool_id, + Username="2ihg2ox304po", + UserAttributes=[ + {"Name": "email", "Value": "new@example.org"}, + {"Name": "email_verified", "Value": "true"}, + {"Name": "preferred_username", "Value": "5678"}, + ], + ) + in boto3.mock_calls + ) + assert ( + call().admin_reset_user_password( + UserPoolId=client.user_pool_id, Username="2ihg2ox304po" + ) + in boto3.mock_calls + ) + + +@patch("cognito.utils.client.client") def test_update_user_updates_partial_managed(boto3, cognito_user_response_factory): boto3.return_value.admin_get_user.return_value = cognito_user_response_factory( - '2ihg2ox304po', '1234', managed=True, attributes_key='UserAttributes' + "2ihg2ox304po", "1234", managed=True, attributes_key="UserAttributes" ) client = Client() - updated = client.update_user('2ihg2ox304po', '5678', 'test@example.org') + updated = client.update_user("2ihg2ox304po", "5678", "test@example.org") assert updated is True - assert '().admin_update_user_attributes' in [call[0] for call in boto3.mock_calls] - assert '().admin_reset_user_password' not in [call[0] for call in boto3.mock_calls] - assert call().admin_update_user_attributes( - UserPoolId=client.user_pool_id, - Username='2ihg2ox304po', - UserAttributes=[{ - 'Name': 'preferred_username', 'Value': '5678' - }] - ) in boto3.mock_calls - - -@patch('cognito.utils.client.client') -def test_update_user_does_not_update_unchanged_managed(boto3, cognito_user_response_factory): + assert "().admin_update_user_attributes" in [call[0] for call in boto3.mock_calls] + assert "().admin_reset_user_password" not in [call[0] for call in boto3.mock_calls] + assert ( + call().admin_update_user_attributes( + UserPoolId=client.user_pool_id, + Username="2ihg2ox304po", + UserAttributes=[{"Name": "preferred_username", "Value": "5678"}], + ) + in boto3.mock_calls + ) + + +@patch("cognito.utils.client.client") +def test_update_user_does_not_update_unchanged_managed( + boto3, cognito_user_response_factory +): boto3.return_value.admin_get_user.return_value = cognito_user_response_factory( - '2ihg2ox304po', '1234', managed=True, attributes_key='UserAttributes' + "2ihg2ox304po", "1234", managed=True, attributes_key="UserAttributes" ) client = Client() - updated = client.update_user('2ihg2ox304po', '1234', 'test@example.org') + updated = client.update_user("2ihg2ox304po", "1234", "test@example.org") assert updated is True - assert '().admin_update_user_attributes' not in [call[0] for call in boto3.mock_calls] - assert '().admin_reset_user_password' not in [call[0] for call in boto3.mock_calls] + assert "().admin_update_user_attributes" not in [ + call[0] for call in boto3.mock_calls + ] + assert "().admin_reset_user_password" not in [call[0] for call in boto3.mock_calls] -@patch('cognito.utils.client.client') +@patch("cognito.utils.client.client") def test_update_user_does_not_update_unmanaged(boto3, cognito_user_response_factory): boto3.return_value.admin_get_user.return_value = cognito_user_response_factory( - '2ihg2ox304po', '1234', managed=False, attributes_key='UserAttributes' + "2ihg2ox304po", "1234", managed=False, attributes_key="UserAttributes" ) client = Client() - updated = client.update_user('2ihg2ox304po', '1234', 'test@example.org') + updated = client.update_user("2ihg2ox304po", "1234", "test@example.org") assert updated is False - assert '().admin_update_user_attributes' not in [call[0] for call in boto3.mock_calls] + assert "().admin_update_user_attributes" not in [ + call[0] for call in boto3.mock_calls + ] -@patch('cognito.utils.client.client') +@patch("cognito.utils.client.client") def test_disable_user_disables_managed(boto3, cognito_user_response_factory): boto3.return_value.admin_get_user.return_value = cognito_user_response_factory( - '2ihg2ox304po', '1234', managed=True, attributes_key='UserAttributes' + "2ihg2ox304po", "1234", managed=True, attributes_key="UserAttributes" ) client = Client() - disabled = client.disable_user('1234') + disabled = client.disable_user("1234") assert disabled is True - assert call().admin_disable_user( - UserPoolId=client.user_pool_id, Username='1234' - ) in boto3.mock_calls + assert ( + call().admin_disable_user(UserPoolId=client.user_pool_id, Username="1234") + in boto3.mock_calls + ) -@patch('cognito.utils.client.client') +@patch("cognito.utils.client.client") def test_disable_user_does_not_disable_unmanaged(boto3, cognito_user_response_factory): boto3.return_value.admin_get_user.return_value = cognito_user_response_factory( - '2ihg2ox304po', '1234', managed=False, attributes_key='UserAttributes' + "2ihg2ox304po", "1234", managed=False, attributes_key="UserAttributes" ) client = Client() - disabled = client.disable_user('1234') + disabled = client.disable_user("1234") assert disabled is False - assert call().admin_disable_user( - UserPoolId=client.user_pool_id, Username='1234' - ) not in boto3.mock_calls + assert ( + call().admin_disable_user(UserPoolId=client.user_pool_id, Username="1234") + not in boto3.mock_calls + ) -@patch('cognito.utils.client.client') +@patch("cognito.utils.client.client") def test_enable_user_enables_managed(boto3, cognito_user_response_factory): boto3.return_value.admin_get_user.return_value = cognito_user_response_factory( - '2ihg2ox304po', '1234', managed=True, attributes_key='UserAttributes' + "2ihg2ox304po", "1234", managed=True, attributes_key="UserAttributes" ) client = Client() - enabled = client.enable_user('1234') + enabled = client.enable_user("1234") assert enabled is True - assert call().admin_enable_user( - UserPoolId=client.user_pool_id, Username='1234' - ) in boto3.mock_calls + assert ( + call().admin_enable_user(UserPoolId=client.user_pool_id, Username="1234") + in boto3.mock_calls + ) -@patch('cognito.utils.client.client') +@patch("cognito.utils.client.client") def test_enable_user_does_not_enable_unmanaged(boto3, cognito_user_response_factory): boto3.return_value.admin_get_user.return_value = cognito_user_response_factory( - '2ihg2ox304po', '1234', managed=False, attributes_key='UserAttributes' + "2ihg2ox304po", "1234", managed=False, attributes_key="UserAttributes" ) client = Client() - enabled = client.enable_user('1234') + enabled = client.enable_user("1234") assert enabled is False - assert call().admin_enable_user( - UserPoolId=client.user_pool_id, Username='1234' - ) not in boto3.mock_calls + assert ( + call().admin_enable_user(UserPoolId=client.user_pool_id, Username="1234") + not in boto3.mock_calls + ) diff --git a/app/tests/cognito/test_cognito_sync_command.py b/app/tests/cognito/test_cognito_sync_command.py index f6214809..7251022d 100644 --- a/app/tests/cognito/test_cognito_sync_command.py +++ b/app/tests/cognito/test_cognito_sync_command.py @@ -9,164 +9,181 @@ from django.utils import timezone -@fixture(name='user') +@fixture(name="user") def fixture_user(provider): - with patch('access.models.Client') as client: + with patch("access.models.Client") as client: client.return_value.create_user.return_value = True yield User.objects.create( - user_id='2ihg2ox304po', - username='1', - first_name='1', - last_name='1', - email='1@example.org', - provider=provider + user_id="2ihg2ox304po", + username="1", + first_name="1", + last_name="1", + email="1@example.org", + provider=provider, ) -@patch('cognito.management.commands.cognito_sync.Client') +@patch("cognito.management.commands.cognito_sync.Client") def test_command_adds(cognito_client, user): cognito_client.return_value.list_users.return_value = [] out = StringIO() - call_command('cognito_sync', verbosity=2, stdout=out) + call_command("cognito_sync", verbosity=2, stdout=out) - assert 'adding user 2ihg2ox304po' in out.getvalue() - assert '1 user(s) added' in out.getvalue() - assert call().create_user('2ihg2ox304po', '1', '1@example.org') in cognito_client.mock_calls + assert "adding user 2ihg2ox304po" in out.getvalue() + assert "1 user(s) added" in out.getvalue() + assert ( + call().create_user("2ihg2ox304po", "1", "1@example.org") + in cognito_client.mock_calls + ) -@patch('cognito.management.commands.cognito_sync.Client') +@patch("cognito.management.commands.cognito_sync.Client") def test_command_deletes(cognito_client, db, cognito_user_response_factory): cognito_client.return_value.list_users.return_value = [ - cognito_user_response_factory('2ihg2ox304po', '1', '1@example.org') + cognito_user_response_factory("2ihg2ox304po", "1", "1@example.org") ] out = StringIO() - call_command('cognito_sync', verbosity=2, stdout=out) + call_command("cognito_sync", verbosity=2, stdout=out) - assert 'deleting user 2ihg2ox304po' in out.getvalue() - assert '1 user(s) deleted' in out.getvalue() - assert call().delete_user('2ihg2ox304po') in cognito_client.mock_calls + assert "deleting user 2ihg2ox304po" in out.getvalue() + assert "1 user(s) deleted" in out.getvalue() + assert call().delete_user("2ihg2ox304po") in cognito_client.mock_calls -@patch('cognito.management.commands.cognito_sync.Client') +@patch("cognito.management.commands.cognito_sync.Client") def test_command_updates(cognito_client, user, cognito_user_response_factory): cognito_client.return_value.list_users.return_value = [ - cognito_user_response_factory('2ihg2ox304po', '2@example.org') + cognito_user_response_factory("2ihg2ox304po", "2@example.org") ] out = StringIO() - call_command('cognito_sync', verbosity=2, stdout=out) + call_command("cognito_sync", verbosity=2, stdout=out) - assert 'updating user 2ihg2ox304po' in out.getvalue() - assert '1 user(s) updated' in out.getvalue() - assert call().update_user('2ihg2ox304po', '1', '1@example.org') in cognito_client.mock_calls + assert "updating user 2ihg2ox304po" in out.getvalue() + assert "1 user(s) updated" in out.getvalue() + assert ( + call().update_user("2ihg2ox304po", "1", "1@example.org") + in cognito_client.mock_calls + ) -@patch('cognito.management.commands.cognito_sync.Client') +@patch("cognito.management.commands.cognito_sync.Client") def test_command_updates_disabled(cognito_client, user, cognito_user_response_factory): user.deleted_at = timezone.now() user.save() cognito_client.return_value.list_users.return_value = [ - cognito_user_response_factory('2ihg2ox304po', '1', '1@example.org') + cognito_user_response_factory("2ihg2ox304po", "1", "1@example.org") ] out = StringIO() - call_command('cognito_sync', verbosity=2, stdout=out) + call_command("cognito_sync", verbosity=2, stdout=out) - assert 'disabling user 2ihg2ox304po' in out.getvalue() - assert '1 user(s) disabled' in out.getvalue() - assert call().disable_user('2ihg2ox304po') in cognito_client.mock_calls + assert "disabling user 2ihg2ox304po" in out.getvalue() + assert "1 user(s) disabled" in out.getvalue() + assert call().disable_user("2ihg2ox304po") in cognito_client.mock_calls -@patch('cognito.management.commands.cognito_sync.Client') +@patch("cognito.management.commands.cognito_sync.Client") def test_command_updates_enabled(cognito_client, user, cognito_user_response_factory): cognito_client.return_value.list_users.return_value = [ - cognito_user_response_factory('2ihg2ox304po', '1', '1@example.org', False) + cognito_user_response_factory("2ihg2ox304po", "1", "1@example.org", False) ] out = StringIO() - call_command('cognito_sync', verbosity=2, stdout=out) + call_command("cognito_sync", verbosity=2, stdout=out) - assert 'enabling user 2ihg2ox304po' in out.getvalue() - assert '1 user(s) enabled' in out.getvalue() - assert call().enable_user('2ihg2ox304po') in cognito_client.mock_calls + assert "enabling user 2ihg2ox304po" in out.getvalue() + assert "1 user(s) enabled" in out.getvalue() + assert call().enable_user("2ihg2ox304po") in cognito_client.mock_calls -@patch('cognito.management.commands.cognito_sync.Client') -def test_command_does_not_updates_if_unchanged(cognito_client, user, cognito_user_response_factory): +@patch("cognito.management.commands.cognito_sync.Client") +def test_command_does_not_updates_if_unchanged( + cognito_client, user, cognito_user_response_factory +): cognito_client.return_value.list_users.return_value = [ - cognito_user_response_factory('2ihg2ox304po', '1', '1@example.org') + cognito_user_response_factory("2ihg2ox304po", "1", "1@example.org") ] out = StringIO() - call_command('cognito_sync', verbosity=2, stdout=out) + call_command("cognito_sync", verbosity=2, stdout=out) - assert 'nothing to be done' in out.getvalue() + assert "nothing to be done" in out.getvalue() -@patch('builtins.input') -@patch('cognito.management.commands.cognito_sync.Client') -def test_command_clears_if_confirmed(cognito_client, input_, user, cognito_user_response_factory): - input_.side_effect = ['yes'] - cognito_client.return_value.list_users.side_effect = [[ - cognito_user_response_factory('2ihg2ox304po', '1', '1@example.org') - ], []] +@patch("builtins.input") +@patch("cognito.management.commands.cognito_sync.Client") +def test_command_clears_if_confirmed( + cognito_client, input_, user, cognito_user_response_factory +): + input_.side_effect = ["yes"] + cognito_client.return_value.list_users.side_effect = [ + [cognito_user_response_factory("2ihg2ox304po", "1", "1@example.org")], + [], + ] out = StringIO() - call_command('cognito_sync', clear=True, verbosity=2, stdout=out) - - assert 'This action will delete all managed users from cognito' in out.getvalue() - assert 'deleting user 2ihg2ox304po' in out.getvalue() - assert '1 user(s) deleted' in out.getvalue() - assert 'adding user 2ihg2ox304po' in out.getvalue() - assert '1 user(s) added' in out.getvalue() - assert call().delete_user('2ihg2ox304po') in cognito_client.mock_calls - assert call().create_user('2ihg2ox304po', '1', '1@example.org') in cognito_client.mock_calls + call_command("cognito_sync", clear=True, verbosity=2, stdout=out) + + assert "This action will delete all managed users from cognito" in out.getvalue() + assert "deleting user 2ihg2ox304po" in out.getvalue() + assert "1 user(s) deleted" in out.getvalue() + assert "adding user 2ihg2ox304po" in out.getvalue() + assert "1 user(s) added" in out.getvalue() + assert call().delete_user("2ihg2ox304po") in cognito_client.mock_calls + assert ( + call().create_user("2ihg2ox304po", "1", "1@example.org") + in cognito_client.mock_calls + ) -@patch('builtins.input') -@patch('cognito.management.commands.cognito_sync.Client') +@patch("builtins.input") +@patch("cognito.management.commands.cognito_sync.Client") def test_command_does_not_clears_if_not_confirmed( cognito_client, input_, user, cognito_user_response_factory ): - input_.side_effect = ['no'] - cognito_client.return_value.list_users.side_effect = [[ - cognito_user_response_factory('2ihg2ox304po', '1', '1@example.org') - ], []] + input_.side_effect = ["no"] + cognito_client.return_value.list_users.side_effect = [ + [cognito_user_response_factory("2ihg2ox304po", "1", "1@example.org")], + [], + ] out = StringIO() - call_command('cognito_sync', clear=True, verbosity=2, stdout=out) + call_command("cognito_sync", clear=True, verbosity=2, stdout=out) - assert 'This action will delete all managed users from cognito' in out.getvalue() - assert 'operation cancelled' in out.getvalue() - assert call().delete_user('1') not in cognito_client.mock_calls - assert call().create_user('1', '1@example.org') not in cognito_client.mock_calls + assert "This action will delete all managed users from cognito" in out.getvalue() + assert "operation cancelled" in out.getvalue() + assert call().delete_user("1") not in cognito_client.mock_calls + assert call().create_user("1", "1@example.org") not in cognito_client.mock_calls -@patch('cognito.management.commands.cognito_sync.Client') -def test_command_runs_dry(cognito_client, provider, user, cognito_user_response_factory): +@patch("cognito.management.commands.cognito_sync.Client") +def test_command_runs_dry( + cognito_client, provider, user, cognito_user_response_factory +): User.objects.create( - user_id='goho4o3ggg2o', - username='2', - first_name='2', - last_name='2', - email='2@example.org', - provider=provider + user_id="goho4o3ggg2o", + username="2", + first_name="2", + last_name="2", + email="2@example.org", + provider=provider, ) cognito_client.return_value.list_users.return_value = [ - cognito_user_response_factory('2ihg2ox304po', '1', '10@example.org'), - cognito_user_response_factory('04i4p3g4iggh', '3', '3@example.org') + cognito_user_response_factory("2ihg2ox304po", "1", "10@example.org"), + cognito_user_response_factory("04i4p3g4iggh", "3", "3@example.org"), ] out = StringIO() - call_command('cognito_sync', dry_run=True, verbosity=2, stdout=out) + call_command("cognito_sync", dry_run=True, verbosity=2, stdout=out) - assert 'adding user goho4o3ggg2o' in out.getvalue() - assert 'deleting user 04i4p3g4iggh' in out.getvalue() - assert 'updating user 2ihg2ox304po' in out.getvalue() - assert 'dry run' in out.getvalue() + assert "adding user goho4o3ggg2o" in out.getvalue() + assert "deleting user 04i4p3g4iggh" in out.getvalue() + assert "updating user 2ihg2ox304po" in out.getvalue() + assert "dry run" in out.getvalue() assert cognito_client.return_value.list_users.called assert not cognito_client.return_value.create_user.called assert not cognito_client.return_value.delete_user.called diff --git a/app/tests/config/mock_api.py b/app/tests/config/mock_api.py index b2d3bd3a..c13bfe13 100644 --- a/app/tests/config/mock_api.py +++ b/app/tests/config/mock_api.py @@ -56,7 +56,7 @@ def trigger_django_validation_error(request: HttpRequest) -> dict[str, bool | st @router.get("/trigger-cognito-connection-error") def trigger_cognito_connection_error(request: HttpRequest) -> dict[str, bool | str]: - raise EndpointConnectionError(endpoint_url='localhost') + raise EndpointConnectionError(endpoint_url="localhost") @router.get("/trigger-200-response") diff --git a/app/tests/config/test_checker.py b/app/tests/config/test_checker.py index 766f0f0c..2673f080 100644 --- a/app/tests/config/test_checker.py +++ b/app/tests/config/test_checker.py @@ -1,11 +1,11 @@ def test_checker_url(client): # intentionally not using reverse here as we want to # make sure the URL really is /checker - response = client.get('/checker') + response = client.get("/checker") assert response.status_code == 200 content = response.json() - assert 'success' in content - assert 'message' in content - assert content['success'] is True - assert content['message'] == "OK" + assert "success" in content + assert "message" in content + assert content["success"] is True + assert content["message"] == "OK" diff --git a/app/tests/config/test_exception_handler.py b/app/tests/config/test_exception_handler.py index 5415a5ca..0ceb568f 100644 --- a/app/tests/config/test_exception_handler.py +++ b/app/tests/config/test_exception_handler.py @@ -2,48 +2,48 @@ def test_handle_404_not_found(client): - response = client.get('/api/v1/trigger-not-found') + response = client.get("/api/v1/trigger-not-found") assert response.status_code == 404 - assert response.json() == {'code': 404, 'description': 'Resource not found'} + assert response.json() == {"code": 404, "description": "Resource not found"} def test_handle_does_not_exist(client, db): - response = client.get('/api/v1/trigger-does-not-exist') + response = client.get("/api/v1/trigger-does-not-exist") assert response.status_code == 404 - assert response.json() == {'code': 404, 'description': 'Resource not found'} + assert response.json() == {"code": 404, "description": "Resource not found"} def test_handle_http_error(client): - response = client.get('/api/v1/trigger-http-error') + response = client.get("/api/v1/trigger-http-error") assert response.status_code == 303 - assert response.json() == {'code': 303, 'description': 'See other'} + assert response.json() == {"code": 303, "description": "See other"} def test_handle_ninja_validation_error(client): - response = client.get('/api/v1/trigger-ninja-validation-error') + response = client.get("/api/v1/trigger-ninja-validation-error") assert response.status_code == 422 - assert response.json() == {'code': 422, 'description': ['Not a valid email.']} + assert response.json() == {"code": 422, "description": ["Not a valid email."]} def test_handle_unauthorized(client): - response = client.get('/api/v1/trigger-authentication-error') + response = client.get("/api/v1/trigger-authentication-error") assert response.status_code == 401 - assert response.json() == {'code': 401, 'description': 'Unauthorized'} + assert response.json() == {"code": 401, "description": "Unauthorized"} def test_handle_exception(client): - response = client.get('/api/v1/trigger-internal-server-error') + response = client.get("/api/v1/trigger-internal-server-error") assert response.status_code == 500 - assert response.json() == {'code': 500, 'description': 'Internal Server Error'} + assert response.json() == {"code": 500, "description": "Internal Server Error"} def test_handle_django_validation_error(client): - response = client.get('/api/v1/trigger-django-validation-error') + response = client.get("/api/v1/trigger-django-validation-error") assert response.status_code == 422 - assert response.json() == {'code': 422, 'description': ['Not a valid email.']} + assert response.json() == {"code": 422, "description": ["Not a valid email."]} def test_handle_cognito_connection_error(client): - response = client.get('/api/v1/trigger-cognito-connection-error') + response = client.get("/api/v1/trigger-cognito-connection-error") assert response.status_code == 503 - assert response.json() == {'code': 503, 'description': 'Service Unavailable'} + assert response.json() == {"code": 503, "description": "Service Unavailable"} diff --git a/app/tests/config/test_request_logging.py b/app/tests/config/test_request_logging.py index 65eea12a..c43c6fb7 100644 --- a/app/tests/config/test_request_logging.py +++ b/app/tests/config/test_request_logging.py @@ -14,133 +14,135 @@ from django.http import HttpResponse -@pytest.fixture(name='configure_logger') +@pytest.fixture(name="configure_logger") def fixture_configure_logger(caplog): caplog.handler.setFormatter(StdlibFormatter()) def test_generate_log_extra(settings): - settings.LOG_ALLOWED_HEADERS = [h.lower() for h in ['Content-Type', 'Lebowski', 'x-apigw-id']] + settings.LOG_ALLOWED_HEADERS = [ + h.lower() for h in ["Content-Type", "Lebowski", "x-apigw-id"] + ] request = HttpRequest() # overwrite the headers request.headers = { - 'opinion': 'just like yours', - 'content-type': 'bowling', - 'secret-header': 'remove this', - 'Lebowski': 'Jeffrey' + "opinion": "just like yours", + "content-type": "bowling", + "secret-header": "remove this", + "Lebowski": "Jeffrey", } response = HttpResponse() - response['Content-type'] = "Nihilism" - response['Set-Cookie'] = "Rug" + response["Content-type"] = "Nihilism" + response["Set-Cookie"] = "Rug" out = generate_log_extra(request, response) - request_headers = out['http']['request']['header'].keys() - response_headers = out['http']['response']['header'].keys() + request_headers = out["http"]["request"]["header"].keys() + response_headers = out["http"]["response"]["header"].keys() - assert 'content-type' in request_headers - assert 'lebowski' in request_headers - assert 'secret-header' not in request_headers - assert 'opinion' not in request_headers + assert "content-type" in request_headers + assert "lebowski" in request_headers + assert "secret-header" not in request_headers + assert "opinion" not in request_headers - assert 'content-type' in response_headers - assert 'set-cookie' not in response_headers + assert "content-type" in response_headers + assert "set-cookie" not in response_headers def test_404_logging(client, caplog, configure_logger): - path = '/api/v1/trigger-not-found' + path = "/api/v1/trigger-not-found" client.get(path) log_entry = json.loads(caplog.text) - assert log_entry['log.level'] == 'warning' - assert log_entry['message'] == f'Response 404 on {path}' - assert log_entry['http']['request']['method'] == 'GET' - assert log_entry['http']['response']['status_code'] == 404 - assert log_entry['url']['path'] == path + assert log_entry["log.level"] == "warning" + assert log_entry["message"] == f"Response 404 on {path}" + assert log_entry["http"]["request"]["method"] == "GET" + assert log_entry["http"]["response"]["status_code"] == 404 + assert log_entry["url"]["path"] == path def test_404_logging_with_query(client, caplog, configure_logger): - path = '/api/v1/trigger-not-found' - client.get(path + '?foo=bar') + path = "/api/v1/trigger-not-found" + client.get(path + "?foo=bar") log_entry = json.loads(caplog.text) - assert log_entry['log.level'] == 'warning' - assert log_entry['message'] == f'Response 404 on {path}' - assert log_entry['http']['request']['method'] == 'GET' - assert log_entry['http']['response']['status_code'] == 404 - assert log_entry['url']['path'] == path + assert log_entry["log.level"] == "warning" + assert log_entry["message"] == f"Response 404 on {path}" + assert log_entry["http"]["request"]["method"] == "GET" + assert log_entry["http"]["response"]["status_code"] == 404 + assert log_entry["url"]["path"] == path def test_404_logging_post(client, caplog, configure_logger): - path = '/api/v1/trigger-not-found-post' + path = "/api/v1/trigger-not-found-post" client.post(path) log_entry = json.loads(caplog.text) - assert log_entry['log.level'] == 'warning' - assert log_entry['message'] == f'Response 404 on {path}' - assert log_entry['http']['request']['method'] == 'POST' - assert log_entry['http']['response']['status_code'] == 404 - assert log_entry['url']['path'] == path + assert log_entry["log.level"] == "warning" + assert log_entry["message"] == f"Response 404 on {path}" + assert log_entry["http"]["request"]["method"] == "POST" + assert log_entry["http"]["response"]["status_code"] == 404 + assert log_entry["url"]["path"] == path def test_ninja_validation_logging(client, caplog, configure_logger): - path = '/api/v1/trigger-ninja-validation-error' + path = "/api/v1/trigger-ninja-validation-error" client.get(path) log_entry = json.loads(caplog.text) - assert log_entry['log.level'] == 'warning' - assert log_entry['message'] == f'Response 422 on {path}' - assert log_entry['http']['request']['method'] == 'GET' - assert log_entry['http']['response']['status_code'] == 422 - assert log_entry['url']['path'] == path + assert log_entry["log.level"] == "warning" + assert log_entry["message"] == f"Response 422 on {path}" + assert log_entry["http"]["request"]["method"] == "GET" + assert log_entry["http"]["response"]["status_code"] == 422 + assert log_entry["url"]["path"] == path def test_500_server_error_logging(client, caplog, configure_logger): - path = '/api/v1/trigger-internal-server-error' + path = "/api/v1/trigger-internal-server-error" client.get(path) # we need to split the caplog, since I can't get rid of the bloody # django.log which also logs the request - log_entry = json.loads(caplog.text.split('\n')[0]) + log_entry = json.loads(caplog.text.split("\n")[0]) - assert log_entry['log.level'] == 'error' - assert log_entry['message'] == 'RuntimeError()' - assert log_entry['http']['response']['status_code'] == 500 - assert 'error' in log_entry - assert 'stack_trace' in log_entry['error'] - assert 'raise RuntimeError()' in log_entry['error']['stack_trace'] - assert log_entry['url']['path'] == path + assert log_entry["log.level"] == "error" + assert log_entry["message"] == "RuntimeError()" + assert log_entry["http"]["response"]["status_code"] == 500 + assert "error" in log_entry + assert "stack_trace" in log_entry["error"] + assert "raise RuntimeError()" in log_entry["error"]["stack_trace"] + assert log_entry["url"]["path"] == path def test_http_error_logging(client, caplog, configure_logger): - path = '/api/v1/trigger-http-error' + path = "/api/v1/trigger-http-error" client.get(path) # we need to split the caplog, since I can't get rid of the bloody # django.log which also logs the request - log_entry = json.loads(caplog.text.split('\n')[0]) + log_entry = json.loads(caplog.text.split("\n")[0]) - assert log_entry['log.level'] == 'info' - assert log_entry['http']['response']['status_code'] == 303 - assert log_entry['message'] == f"Response 303 on {path}" - assert log_entry['url']['path'] == path + assert log_entry["log.level"] == "info" + assert log_entry["http"]["response"]["status_code"] == 303 + assert log_entry["message"] == f"Response 303 on {path}" + assert log_entry["url"]["path"] == path def test_positive_request_log(client, caplog, configure_logger): - path = '/api/v1/trigger-200-response' + path = "/api/v1/trigger-200-response" client.get(path) # we need to split the caplog, since I can't get rid of the bloody # django.log which also logs the request - log_entry = json.loads(caplog.text.split('\n')[0]) + log_entry = json.loads(caplog.text.split("\n")[0]) - assert log_entry['message'] == f'Response 200 on {path}' - assert log_entry['http']['request']['method'] == "GET" - assert log_entry['http']['response']['status_code'] == 200 - assert log_entry['url']['path'] == path + assert log_entry["message"] == f"Response 200 on {path}" + assert log_entry["http"]["request"]["method"] == "GET" + assert log_entry["http"]["response"]["status_code"] == 200 + assert log_entry["url"]["path"] == path diff --git a/app/tests/conftest.py b/app/tests/conftest.py index f79f0748..2f1e6642 100644 --- a/app/tests/conftest.py +++ b/app/tests/conftest.py @@ -10,7 +10,7 @@ from django.contrib.contenttypes.models import ContentType -@fixture(name='provider') +@fixture(name="provider") def fixture_provider(db): yield Provider.objects.create( provider_id="ch.bafu", @@ -27,7 +27,7 @@ def fixture_provider(db): ) -@fixture(name='attribution') +@fixture(name="attribution") def fixture_attribution(provider): yield Attribution.objects.create( attribution_id="ch.bafu.kt", @@ -41,7 +41,7 @@ def fixture_attribution(provider): description_en="Federal Office for the Environment and cantons", description_it="Ufficio federale dell'ambiente e cantoni", description_rm="Uffizi federal per l'ambient e chantuns", - provider=provider + provider=provider, ) @@ -62,10 +62,14 @@ def test_something(django_user_factory): def create_user_with_permissions( username: str, password: str, permissions: list[tuple[str, str, str]] ) -> Any: - user = get_user_model().objects.create_user(username=username, password=password) + user = get_user_model().objects.create_user( + username=username, password=password + ) for app_label, model, codename in permissions: content_type = ContentType.objects.get(app_label=app_label, model=model) - permission = Permission.objects.get(content_type=content_type, codename=codename) + permission = Permission.objects.get( + content_type=content_type, codename=codename + ) user.user_permissions.add(permission) return user @@ -74,7 +78,7 @@ def create_user_with_permissions( @fixture def cognito_user_response_factory(): - """ A fixture to create Cognito responses containing user data. + """A fixture to create Cognito responses containing user data. Returns a callable that accepts the following parameters: - username: The username of the user. @@ -99,19 +103,19 @@ def test_something(cognito_user_response_factory): def create_cognito_user_response( username, preferred_username, - email='test@example.org', + email="test@example.org", enabled=True, managed=True, - attributes_key='Attributes' + attributes_key="Attributes", ): - - attributes = [{ - 'Name': 'email', 'Value': email - }, { - 'Name': 'preferred_username', 'Value': preferred_username - }] + attributes = [ + {"Name": "email", "Value": email}, + {"Name": "preferred_username", "Value": preferred_username}, + ] if managed: - attributes.append({'Name': settings.COGNITO_MANAGED_FLAG_NAME, 'Value': 'true'}) - return {'Username': username, attributes_key: attributes, 'Enabled': enabled} + attributes.append( + {"Name": settings.COGNITO_MANAGED_FLAG_NAME, "Value": "true"} + ) + return {"Username": username, attributes_key: attributes, "Enabled": enabled} return create_cognito_user_response diff --git a/app/tests/distributions/test_attribution_model.py b/app/tests/distributions/test_attribution_model.py index 5ab8d008..cfb4ea40 100644 --- a/app/tests/distributions/test_attribution_model.py +++ b/app/tests/distributions/test_attribution_model.py @@ -18,7 +18,7 @@ def test_object_created_in_db_with_all_fields_defined(provider): "description_en": "Federal Office for the Environment", "description_it": "Ufficio federale dell'ambiente", "description_rm": "Uffizi federal per l'ambient", - "provider": provider + "provider": provider, } Attribution.objects.create(**attribution) @@ -57,7 +57,7 @@ def test_object_created_in_db_with_optional_fields_null(provider): "description_en": "Federal Office for the Environment", "description_it": None, "description_rm": None, - "provider": provider + "provider": provider, } Attribution.objects.create(**attribution) @@ -87,9 +87,7 @@ def test_raises_exception_when_creating_db_object_with_mandatory_field_null(prov def test_form_valid_for_blank_optional_field(provider): - class AttributionForm(ModelForm): - class Meta: model = Attribution fields = "__all__" @@ -110,9 +108,7 @@ class Meta: def test_form_invalid_for_blank_mandatory_field(provider): - class AttributionForm(ModelForm): - class Meta: model = Attribution fields = "__all__" diff --git a/app/tests/distributions/test_dataset_model.py b/app/tests/distributions/test_dataset_model.py index 0dc8b264..23e2345a 100644 --- a/app/tests/distributions/test_dataset_model.py +++ b/app/tests/distributions/test_dataset_model.py @@ -15,14 +15,16 @@ def test_object_created_in_db_with_all_fields_defined(provider, attribution): title_de = "Invasive gebietsfremde Pflanzen - Potentialkarte Haargurke" title_fr = "Plantes exotiques envahissantes - Carte de distribution potentiel Sicyos anguleux" title_en = "Invasive alien plants - map of the potential area Sicyos angulatus" - title_it = "Piante esotiche invasive - carte di distribuzione potenziale Sicios angoloso" + title_it = ( + "Piante esotiche invasive - carte di distribuzione potenziale Sicios angoloso" + ) title_rm = "Plantas exoticas invasivas - Charta da la derasaziun potenziala dal sichius angulus" description_de = "Beschreibung Haargurke" description_fr = "Description Sicyos anguleux" description_en = "Description Sicyos angulatus" description_it = "Descrizione Sicios angoloso" description_rm = "Descripziun Sicyos angulatus" - with mock.patch('django.utils.timezone.now', mock.Mock(return_value=time_created)): + with mock.patch("django.utils.timezone.now", mock.Mock(return_value=time_created)): Dataset.objects.create( dataset_id=dataset_id, geocat_id=geocat_id, @@ -67,10 +69,10 @@ def test_object_created_in_db_with_all_fields_defined(provider, attribution): def test_field_created_matches_creation_time(provider, attribution): time_created = datetime.datetime(2024, 9, 12, 15, 28, 0, tzinfo=datetime.UTC) - with mock.patch('django.utils.timezone.now', mock.Mock(return_value=time_created)): + with mock.patch("django.utils.timezone.now", mock.Mock(return_value=time_created)): dataset = Dataset.objects.create( - dataset_id='xxxx', - geocat_id='xxxx', + dataset_id="xxxx", + geocat_id="xxxx", title_de="xxxx", title_fr="xxxx", title_en="xxxx", @@ -85,8 +87,8 @@ def test_field_created_matches_creation_time(provider, attribution): def test_field_updated_matches_update_time(provider, attribution): dataset = Dataset.objects.create( - dataset_id='xxxx', - geocat_id='xxxx', + dataset_id="xxxx", + geocat_id="xxxx", title_de="xxxx", title_fr="xxxx", title_en="xxxx", @@ -94,11 +96,11 @@ def test_field_updated_matches_update_time(provider, attribution): description_fr="xxxx", description_en="xxxx", provider=provider, - attribution=attribution + attribution=attribution, ) time_updated = datetime.datetime(2024, 9, 12, 15, 42, 0, tzinfo=datetime.UTC) - with mock.patch('django.utils.timezone.now', mock.Mock(return_value=time_updated)): + with mock.patch("django.utils.timezone.now", mock.Mock(return_value=time_updated)): dataset.dataset_id = "ch.bafu.neophyten-goetterbaum" dataset.save() @@ -106,6 +108,7 @@ def test_field_updated_matches_update_time(provider, attribution): def test_raises_exception_when_creating_db_object_with_mandatory_field_null( - provider, attribution): + provider, attribution +): with raises(ValidationError): Dataset.objects.create(provider=provider, attribution=attribution) diff --git a/app/tests/distributions/test_distributions_api.py b/app/tests/distributions/test_distributions_api.py index a278fb87..2786516c 100644 --- a/app/tests/distributions/test_distributions_api.py +++ b/app/tests/distributions/test_distributions_api.py @@ -10,36 +10,33 @@ from schemas import TranslationsSchema -@fixture(name='time_created') +@fixture(name="time_created") def fixture_time_created(): yield datetime.datetime(2024, 9, 12, 15, 28, 0, tzinfo=datetime.UTC) -@fixture(name='dataset') +@fixture(name="dataset") def fixture_dataset(attribution, time_created): - with mock.patch('django.utils.timezone.now', mock.Mock(return_value=time_created)): + with mock.patch("django.utils.timezone.now", mock.Mock(return_value=time_created)): yield Dataset.objects.create( dataset_id="ch.bafu.neophyten-haargurke", geocat_id="ab76361f-657d-4705-9053-95f89ecab126", title_de="Invasive gebietsfremde Pflanzen - Potentialkarte Haargurke", - title_fr= - "Plantes exotiques envahissantes - Carte de distribution potentiel Sicyos anguleux", + title_fr="Plantes exotiques envahissantes - Carte de distribution potentiel Sicyos anguleux", title_en="Invasive alien plants - map of the potential area Sicyos angulatus", title_it="Piante esotiche invasive - carte di distribuzione potenziale Sicios angoloso", - title_rm= - "Plantas exoticas invasivas - Charta da la derasaziun potenziala dal sichius angulus", + title_rm="Plantas exoticas invasivas - Charta da la derasaziun potenziala dal sichius angulus", description_de="Beschreibung Haargurke", description_fr="Description Sicyos anguleux", description_en="Description Sicyos angulatus", description_it="Descrizione Sicios angoloso", description_rm="Descripziun Sicyos angulatus", provider=attribution.provider, - attribution=attribution + attribution=attribution, ) def test_attribution_to_response_returns_response_with_language_as_defined(attribution): - actual = attribution_to_response(attribution, lang="de") expected = AttributionSchema( @@ -60,14 +57,15 @@ def test_attribution_to_response_returns_response_with_language_as_defined(attri it="Ufficio federale dell'ambiente e cantoni", rm="Uffizi federal per l'ambient e chantuns", ), - provider_id="ch.bafu" + provider_id="ch.bafu", ) assert actual == expected -def test_attribution_to_response_returns_response_with_default_language_if_undefined(attribution): - +def test_attribution_to_response_returns_response_with_default_language_if_undefined( + attribution, +): attribution.name_it = None attribution.name_rm = None attribution.description_it = None @@ -102,8 +100,10 @@ def test_attribution_to_response_returns_response_with_default_language_if_undef def test_get_attribution_returns_existing_attribution_with_default_language( attribution, client, django_user_factory ): - django_user_factory('test', 'test', [('distributions', 'attribution', 'view_attribution')]) - client.login(username='test', password='test') + django_user_factory( + "test", "test", [("distributions", "attribution", "view_attribution")] + ) + client.login(username="test", password="test") response = client.get(f"/api/v1/attributions/{attribution.attribution_id}") @@ -133,8 +133,10 @@ def test_get_attribution_returns_existing_attribution_with_default_language( def test_get_attribution_returns_attribution_with_language_from_query( attribution, client, django_user_factory ): - django_user_factory('test', 'test', [('distributions', 'attribution', 'view_attribution')]) - client.login(username='test', password='test') + django_user_factory( + "test", "test", [("distributions", "attribution", "view_attribution")] + ) + client.login(username="test", password="test") response = client.get(f"/api/v1/attributions/{attribution.attribution_id}?lang=de") @@ -161,9 +163,13 @@ def test_get_attribution_returns_attribution_with_language_from_query( } -def test_get_attribution_returns_404_for_nonexisting_attribution(client, django_user_factory): - django_user_factory('test', 'test', [('distributions', 'attribution', 'view_attribution')]) - client.login(username='test', password='test') +def test_get_attribution_returns_404_for_nonexisting_attribution( + client, django_user_factory +): + django_user_factory( + "test", "test", [("distributions", "attribution", "view_attribution")] + ) + client.login(username="test", password="test") response = client.get("/api/v1/attributions/9999") @@ -174,8 +180,10 @@ def test_get_attribution_returns_404_for_nonexisting_attribution(client, django_ def test_get_attribution_skips_translations_that_are_not_available( attribution, client, django_user_factory ): - django_user_factory('test', 'test', [('distributions', 'attribution', 'view_attribution')]) - client.login(username='test', password='test') + django_user_factory( + "test", "test", [("distributions", "attribution", "view_attribution")] + ) + client.login(username="test", password="test") attribution.name_it = None attribution.name_rm = None @@ -207,11 +215,14 @@ def test_get_attribution_skips_translations_that_are_not_available( def test_get_attribution_returns_attribution_with_language_from_header( attribution, client, django_user_factory ): - django_user_factory('test', 'test', [('distributions', 'attribution', 'view_attribution')]) - client.login(username='test', password='test') + django_user_factory( + "test", "test", [("distributions", "attribution", "view_attribution")] + ) + client.login(username="test", password="test") response = client.get( - f"/api/v1/attributions/{attribution.attribution_id}", headers={"Accept-Language": "de"} + f"/api/v1/attributions/{attribution.attribution_id}", + headers={"Accept-Language": "de"}, ) assert response.status_code == 200 @@ -240,12 +251,14 @@ def test_get_attribution_returns_attribution_with_language_from_header( def test_get_attribution_returns_attribution_with_language_from_query_param_even_if_header_set( attribution, client, django_user_factory ): - django_user_factory('test', 'test', [('distributions', 'attribution', 'view_attribution')]) - client.login(username='test', password='test') + django_user_factory( + "test", "test", [("distributions", "attribution", "view_attribution")] + ) + client.login(username="test", password="test") response = client.get( f"/api/v1/attributions/{attribution.attribution_id}?lang=fr", - headers={"Accept-Language": "de"} + headers={"Accept-Language": "de"}, ) assert response.status_code == 200 @@ -274,11 +287,14 @@ def test_get_attribution_returns_attribution_with_language_from_query_param_even def test_get_attribution_returns_attribution_with_default_language_if_header_empty( attribution, client, django_user_factory ): - django_user_factory('test', 'test', [('distributions', 'attribution', 'view_attribution')]) - client.login(username='test', password='test') + django_user_factory( + "test", "test", [("distributions", "attribution", "view_attribution")] + ) + client.login(username="test", password="test") response = client.get( - f"/api/v1/attributions/{attribution.attribution_id}", headers={"Accept-Language": ""} + f"/api/v1/attributions/{attribution.attribution_id}", + headers={"Accept-Language": ""}, ) assert response.status_code == 200 @@ -307,12 +323,14 @@ def test_get_attribution_returns_attribution_with_default_language_if_header_emp def test_get_attribution_returns_attribution_with_first_known_language_from_header( attribution, client, django_user_factory ): - django_user_factory('test', 'test', [('distributions', 'attribution', 'view_attribution')]) - client.login(username='test', password='test') + django_user_factory( + "test", "test", [("distributions", "attribution", "view_attribution")] + ) + client.login(username="test", password="test") response = client.get( f"/api/v1/attributions/{attribution.attribution_id}", - headers={"Accept-Language": "cn, *, de-DE, en"} + headers={"Accept-Language": "cn, *, de-DE, en"}, ) assert response.status_code == 200 @@ -341,12 +359,14 @@ def test_get_attribution_returns_attribution_with_first_known_language_from_head def test_get_attribution_returns_attribution_with_first_language_from_header_ignoring_qfactor( attribution, client, django_user_factory ): - django_user_factory('test', 'test', [('distributions', 'attribution', 'view_attribution')]) - client.login(username='test', password='test') + django_user_factory( + "test", "test", [("distributions", "attribution", "view_attribution")] + ) + client.login(username="test", password="test") response = client.get( f"/api/v1/attributions/{attribution.attribution_id}", - headers={"Accept-Language": "fr;q=0.9, de;q=0.8"} + headers={"Accept-Language": "fr;q=0.9, de;q=0.8"}, ) assert response.status_code == 200 @@ -373,16 +393,17 @@ def test_get_attribution_returns_attribution_with_first_language_from_header_ign def test_get_attribution_returns_401_if_not_logged_in(attribution, client): - response = client.get(f"/api/v1/attributions/{attribution.attribution_id}") assert response.status_code == 401 assert response.json() == {"code": 401, "description": "Unauthorized"} -def test_get_attribution_returns_403_if_no_permission(attribution, client, django_user_factory): - django_user_factory('test', 'test', []) - client.login(username='test', password='test') +def test_get_attribution_returns_403_if_no_permission( + attribution, client, django_user_factory +): + django_user_factory("test", "test", []) + client.login(username="test", password="test") response = client.get(f"/api/v1/attributions/{attribution.attribution_id}") @@ -393,41 +414,47 @@ def test_get_attribution_returns_403_if_no_permission(attribution, client, djang def test_get_attributions_returns_single_attribution_with_given_language( attribution, client, django_user_factory ): - django_user_factory('test', 'test', [('distributions', 'attribution', 'view_attribution')]) - client.login(username='test', password='test') + django_user_factory( + "test", "test", [("distributions", "attribution", "view_attribution")] + ) + client.login(username="test", password="test") response = client.get("/api/v1/attributions?lang=fr") assert response.status_code == 200 assert response.json() == { - "items": [{ - "id": "ch.bafu.kt", - "name": "OFEV + cantons", - "name_translations": { - "de": "BAFU + Kantone", - "fr": "OFEV + cantons", - "en": "FOEN + cantons", - "it": "UFAM + cantoni", - "rm": "UFAM + chantuns", - }, - "description": "Office fédéral de l'environnement et cantons", - "description_translations": { - "de": "Bundesamt für Umwelt und Kantone", - "fr": "Office fédéral de l'environnement et cantons", - "en": "Federal Office for the Environment and cantons", - "it": "Ufficio federale dell'ambiente e cantoni", - "rm": "Uffizi federal per l'ambient e chantuns", - }, - "provider_id": "ch.bafu", - }] + "items": [ + { + "id": "ch.bafu.kt", + "name": "OFEV + cantons", + "name_translations": { + "de": "BAFU + Kantone", + "fr": "OFEV + cantons", + "en": "FOEN + cantons", + "it": "UFAM + cantoni", + "rm": "UFAM + chantuns", + }, + "description": "Office fédéral de l'environnement et cantons", + "description_translations": { + "de": "Bundesamt für Umwelt und Kantone", + "fr": "Office fédéral de l'environnement et cantons", + "en": "Federal Office for the Environment and cantons", + "it": "Ufficio federale dell'ambiente e cantoni", + "rm": "Uffizi federal per l'ambient e chantuns", + }, + "provider_id": "ch.bafu", + } + ] } def test_get_attributions_skips_translations_that_are_not_available( attribution, client, django_user_factory ): - django_user_factory('test', 'test', [('distributions', 'attribution', 'view_attribution')]) - client.login(username='test', password='test') + django_user_factory( + "test", "test", [("distributions", "attribution", "view_attribution")] + ) + client.login(username="test", password="test") attribution.name_it = None attribution.name_rm = None @@ -439,63 +466,71 @@ def test_get_attributions_skips_translations_that_are_not_available( assert response.status_code == 200 assert response.json() == { - "items": [{ - "id": "ch.bafu.kt", - "name": "FOEN + cantons", - "name_translations": { - "de": "BAFU + Kantone", - "fr": "OFEV + cantons", - "en": "FOEN + cantons", - }, - "description": "Federal Office for the Environment and cantons", - "description_translations": { - "de": "Bundesamt für Umwelt und Kantone", - "fr": "Office fédéral de l'environnement et cantons", - "en": "Federal Office for the Environment and cantons", - }, - "provider_id": "ch.bafu", - }] + "items": [ + { + "id": "ch.bafu.kt", + "name": "FOEN + cantons", + "name_translations": { + "de": "BAFU + Kantone", + "fr": "OFEV + cantons", + "en": "FOEN + cantons", + }, + "description": "Federal Office for the Environment and cantons", + "description_translations": { + "de": "Bundesamt für Umwelt und Kantone", + "fr": "Office fédéral de l'environnement et cantons", + "en": "Federal Office for the Environment and cantons", + }, + "provider_id": "ch.bafu", + } + ] } def test_get_attributions_returns_attribution_with_language_from_header( attribution, client, django_user_factory ): - django_user_factory('test', 'test', [('distributions', 'attribution', 'view_attribution')]) - client.login(username='test', password='test') + django_user_factory( + "test", "test", [("distributions", "attribution", "view_attribution")] + ) + client.login(username="test", password="test") response = client.get("/api/v1/attributions", headers={"Accept-Language": "de"}) assert response.status_code == 200 assert response.json() == { - "items": [{ - "id": "ch.bafu.kt", - "name": "BAFU + Kantone", - "name_translations": { - "de": "BAFU + Kantone", - "fr": "OFEV + cantons", - "en": "FOEN + cantons", - "it": "UFAM + cantoni", - "rm": "UFAM + chantuns", - }, - "description": "Bundesamt für Umwelt und Kantone", - "description_translations": { - "de": "Bundesamt für Umwelt und Kantone", - "fr": "Office fédéral de l'environnement et cantons", - "en": "Federal Office for the Environment and cantons", - "it": "Ufficio federale dell'ambiente e cantoni", - "rm": "Uffizi federal per l'ambient e chantuns", - }, - "provider_id": "ch.bafu", - }] + "items": [ + { + "id": "ch.bafu.kt", + "name": "BAFU + Kantone", + "name_translations": { + "de": "BAFU + Kantone", + "fr": "OFEV + cantons", + "en": "FOEN + cantons", + "it": "UFAM + cantoni", + "rm": "UFAM + chantuns", + }, + "description": "Bundesamt für Umwelt und Kantone", + "description_translations": { + "de": "Bundesamt für Umwelt und Kantone", + "fr": "Office fédéral de l'environnement et cantons", + "en": "Federal Office for the Environment and cantons", + "it": "Ufficio federale dell'ambiente e cantoni", + "rm": "Uffizi federal per l'ambient e chantuns", + }, + "provider_id": "ch.bafu", + } + ] } def test_get_attributions_returns_all_attributions_ordered_by_id_with_given_language( attribution, client, django_user_factory ): - django_user_factory('test', 'test', [('distributions', 'attribution', 'view_attribution')]) - client.login(username='test', password='test') + django_user_factory( + "test", "test", [("distributions", "attribution", "view_attribution")] + ) + client.login(username="test", password="test") provider2 = Provider.objects.create( provider_id="ch.provider2", @@ -504,7 +539,7 @@ def test_get_attributions_returns_all_attributions_ordered_by_id_with_given_lang acronym_en="Provider2", name_de="Provider2", name_fr="Provider2", - name_en="Provider2" + name_en="Provider2", ) model_fields = { "attribution_id": "ch.provider2.bav", @@ -571,16 +606,20 @@ def test_get_attributions_returns_all_attributions_ordered_by_id_with_given_lang } -def test_get_attributions_returns_401_if_not_logged_in(attribution, client, django_user_factory): +def test_get_attributions_returns_401_if_not_logged_in( + attribution, client, django_user_factory +): response = client.get("/api/v1/providers") assert response.status_code == 401 assert response.json() == {"code": 401, "description": "Unauthorized"} -def test_get_attributions_returns_403_if_no_permission(attribution, client, django_user_factory): - django_user_factory('test', 'test', []) - client.login(username='test', password='test') +def test_get_attributions_returns_403_if_no_permission( + attribution, client, django_user_factory +): + django_user_factory("test", "test", []) + client.login(username="test", password="test") response = client.get("/api/v1/providers") @@ -588,9 +627,11 @@ def test_get_attributions_returns_403_if_no_permission(attribution, client, djan assert response.json() == {"code": 403, "description": "Forbidden"} -def test_get_dataset_returns_specified_dataset(dataset, client, django_user_factory, time_created): - django_user_factory('test', 'test', [('distributions', 'dataset', 'view_dataset')]) - client.login(username='test', password='test') +def test_get_dataset_returns_specified_dataset( + dataset, client, django_user_factory, time_created +): + django_user_factory("test", "test", [("distributions", "dataset", "view_dataset")]) + client.login(username="test", password="test") response = client.get(f"/api/v1/datasets/{dataset.dataset_id}") @@ -600,13 +641,11 @@ def test_get_dataset_returns_specified_dataset(dataset, client, django_user_fact "title": "Invasive alien plants - map of the potential area Sicyos angulatus", "title_translations": { "de": "Invasive gebietsfremde Pflanzen - Potentialkarte Haargurke", - "fr": - "Plantes exotiques envahissantes - Carte de distribution potentiel Sicyos anguleux", + "fr": "Plantes exotiques envahissantes - Carte de distribution potentiel Sicyos anguleux", "en": "Invasive alien plants - map of the potential area Sicyos angulatus", "it": "Piante esotiche invasive - carte di distribuzione potenziale Sicios angoloso", - "rm": - "Plantas exoticas invasivas - Charta da " + - "la derasaziun potenziala dal sichius angulus", + "rm": "Plantas exoticas invasivas - Charta da " + + "la derasaziun potenziala dal sichius angulus", }, "description": "Description Sicyos angulatus", "description_translations": { @@ -619,7 +658,7 @@ def test_get_dataset_returns_specified_dataset(dataset, client, django_user_fact "created": time_created.strftime("%Y-%m-%dT%H:%M:%SZ"), "updated": time_created.strftime("%Y-%m-%dT%H:%M:%SZ"), "provider_id": "ch.bafu", - "attribution_id": "ch.bafu.kt" + "attribution_id": "ch.bafu.kt", } @@ -631,8 +670,8 @@ def test_get_dataset_returns_401_if_not_logged_in(dataset, client): def test_get_dataset_returns_403_if_no_permission(dataset, client, django_user_factory): - django_user_factory('test', 'test', []) - client.login(username='test', password='test') + django_user_factory("test", "test", []) + client.login(username="test", password="test") response = client.get(f"/api/v1/datasets/{dataset.dataset_id}") @@ -643,50 +682,49 @@ def test_get_dataset_returns_403_if_no_permission(dataset, client, django_user_f def test_get_datasets_returns_single_dataset_as_expected( dataset, client, django_user_factory, time_created ): - django_user_factory('test', 'test', [('distributions', 'dataset', 'view_dataset')]) - client.login(username='test', password='test') + django_user_factory("test", "test", [("distributions", "dataset", "view_dataset")]) + client.login(username="test", password="test") response = client.get("/api/v1/datasets") assert response.status_code == 200 assert response.json() == { - "items": [{ - "id": "ch.bafu.neophyten-haargurke", - "title": "Invasive alien plants - map of the potential area Sicyos angulatus", - "title_translations": { - "de": "Invasive gebietsfremde Pflanzen - Potentialkarte Haargurke", - "fr": - "Plantes exotiques envahissantes - " + - "Carte de distribution potentiel Sicyos anguleux", - "en": "Invasive alien plants - map of the potential area Sicyos angulatus", - "it": - "Piante esotiche invasive - carte " + - "di distribuzione potenziale Sicios angoloso", - "rm": - "Plantas exoticas invasivas - Charta " + - "da la derasaziun potenziala dal sichius angulus", - }, - "description": "Description Sicyos angulatus", - "description_translations": { - "de": "Beschreibung Haargurke", - "fr": "Description Sicyos anguleux", - "en": "Description Sicyos angulatus", - "it": "Descrizione Sicios angoloso", - "rm": "Descripziun Sicyos angulatus", - }, - "created": time_created.strftime("%Y-%m-%dT%H:%M:%SZ"), - "updated": time_created.strftime("%Y-%m-%dT%H:%M:%SZ"), - "provider_id": "ch.bafu", - "attribution_id": dataset.attribution.attribution_id, - }] + "items": [ + { + "id": "ch.bafu.neophyten-haargurke", + "title": "Invasive alien plants - map of the potential area Sicyos angulatus", + "title_translations": { + "de": "Invasive gebietsfremde Pflanzen - Potentialkarte Haargurke", + "fr": "Plantes exotiques envahissantes - " + + "Carte de distribution potentiel Sicyos anguleux", + "en": "Invasive alien plants - map of the potential area Sicyos angulatus", + "it": "Piante esotiche invasive - carte " + + "di distribuzione potenziale Sicios angoloso", + "rm": "Plantas exoticas invasivas - Charta " + + "da la derasaziun potenziala dal sichius angulus", + }, + "description": "Description Sicyos angulatus", + "description_translations": { + "de": "Beschreibung Haargurke", + "fr": "Description Sicyos anguleux", + "en": "Description Sicyos angulatus", + "it": "Descrizione Sicios angoloso", + "rm": "Descripziun Sicyos angulatus", + }, + "created": time_created.strftime("%Y-%m-%dT%H:%M:%SZ"), + "updated": time_created.strftime("%Y-%m-%dT%H:%M:%SZ"), + "provider_id": "ch.bafu", + "attribution_id": dataset.attribution.attribution_id, + } + ] } def test_get_datasets_returns_all_datasets_ordered_by_dataset_id( dataset, client, django_user_factory, time_created ): - django_user_factory('test', 'test', [('distributions', 'dataset', 'view_dataset')]) - client.login(username='test', password='test') + django_user_factory("test", "test", [("distributions", "dataset", "view_dataset")]) + client.login(username="test", password="test") provider2 = Provider.objects.create( provider_id="ch.provider2", @@ -695,7 +733,7 @@ def test_get_datasets_returns_all_datasets_ordered_by_dataset_id( acronym_en="Provider2", name_de="Provider2", name_fr="Provider2", - name_en="Provider2" + name_en="Provider2", ) attribution2 = Attribution.objects.create( attribution_id="ch.provider2.attribution2", @@ -720,7 +758,7 @@ def test_get_datasets_returns_all_datasets_ordered_by_dataset_id( "attribution": attribution2, } time_created2 = datetime.datetime(2024, 9, 12, 16, 28, 0, tzinfo=datetime.UTC) - with mock.patch('django.utils.timezone.now', mock.Mock(return_value=time_created2)): + with mock.patch("django.utils.timezone.now", mock.Mock(return_value=time_created2)): dataset2 = Dataset.objects.create(**model_fields2) response = client.get("/api/v1/datasets") @@ -733,16 +771,13 @@ def test_get_datasets_returns_all_datasets_ordered_by_dataset_id( "title": "Invasive alien plants - map of the potential area Sicyos angulatus", "title_translations": { "de": "Invasive gebietsfremde Pflanzen - Potentialkarte Haargurke", - "fr": - "Plantes exotiques envahissantes - " + - "Carte de distribution potentiel Sicyos anguleux", + "fr": "Plantes exotiques envahissantes - " + + "Carte de distribution potentiel Sicyos anguleux", "en": "Invasive alien plants - map of the potential area Sicyos angulatus", - "it": - "Piante esotiche invasive - carte di" + - " distribuzione potenziale Sicios angoloso", - "rm": - "Plantas exoticas invasivas - Charta " + - "da la derasaziun potenziala dal sichius angulus", + "it": "Piante esotiche invasive - carte di" + + " distribuzione potenziale Sicios angoloso", + "rm": "Plantas exoticas invasivas - Charta " + + "da la derasaziun potenziala dal sichius angulus", }, "description": "Description Sicyos angulatus", "description_translations": { @@ -787,9 +822,11 @@ def test_get_datasets_returns_401_if_not_logged_in(dataset, client): assert response.json() == {"code": 401, "description": "Unauthorized"} -def test_get_datasets_returns_403_if_no_permission(dataset, client, django_user_factory): - django_user_factory('test', 'test', []) - client.login(username='test', password='test') +def test_get_datasets_returns_403_if_no_permission( + dataset, client, django_user_factory +): + django_user_factory("test", "test", []) + client.login(username="test", password="test") response = client.get("/api/v1/datasets") diff --git a/app/tests/distributions/test_package_distribution_model.py b/app/tests/distributions/test_package_distribution_model.py index 650a2428..bfb67b9b 100644 --- a/app/tests/distributions/test_package_distribution_model.py +++ b/app/tests/distributions/test_package_distribution_model.py @@ -6,7 +6,7 @@ from django.core.exceptions import ValidationError -@fixture(name='dataset') +@fixture(name="dataset") def fixture_dataset(provider, attribution): yield Dataset.objects.create( dataset_id="ch.agroscope.feuchtflaechenpotential-kulturlandschaft", @@ -26,7 +26,7 @@ def test_create_package_distribution_in_database(dataset): PackageDistribution.objects.create( package_distribution_id="ch.agroscope.feuchtflaechenpotential-kulturlandschaft", managed_by_stac=True, - dataset=dataset + dataset=dataset, ) distributions = PackageDistribution.objects.all() @@ -35,13 +35,14 @@ def test_create_package_distribution_in_database(dataset): distribution = distributions[0] - assert distribution.package_distribution_id == \ - "ch.agroscope.feuchtflaechenpotential-kulturlandschaft" + assert ( + distribution.package_distribution_id + == "ch.agroscope.feuchtflaechenpotential-kulturlandschaft" + ) assert distribution.managed_by_stac is True assert distribution.dataset == dataset -def test_raises_exception_when_creating_db_object_with_mandatory_field_null( - dataset): +def test_raises_exception_when_creating_db_object_with_mandatory_field_null(dataset): with raises(ValidationError): PackageDistribution.objects.create(dataset=dataset) diff --git a/app/tests/distributions/test_stac_sync_command.py b/app/tests/distributions/test_stac_sync_command.py index 3016794b..ef1df6f7 100644 --- a/app/tests/distributions/test_stac_sync_command.py +++ b/app/tests/distributions/test_stac_sync_command.py @@ -9,8 +9,8 @@ from django.core.management import call_command -@patch('distributions.management.commands.stac_sync.Client') -@patch('distributions.management.commands.stac_sync.get') +@patch("distributions.management.commands.stac_sync.Client") +@patch("distributions.management.commands.stac_sync.get") def test_command_imports(get, stac_client, provider, attribution): dataset_1 = Dataset.objects.create( dataset_id="ch.bafu.alpweiden-herdenschutzhunde", @@ -47,10 +47,10 @@ def test_command_imports(get, stac_client, provider, attribution): stac_client.open.return_value.collection_search.return_value.collections.return_value = [ Collection( - id='ch.bafu.alpweiden-herdenschutzhunde', + id="ch.bafu.alpweiden-herdenschutzhunde", description=None, extent=None, - providers=[StacProvider(name='Federal Office for the Environment')] + providers=[StacProvider(name="Federal Office for the Environment")], ) ] get.return_value.text = '
ch.bafu.hydrologie-hintergrundkarte
' @@ -58,25 +58,31 @@ def test_command_imports(get, stac_client, provider, attribution): out = StringIO() call_command("stac_sync", verbosity=2, stdout=out) out = out.getvalue() - assert "Added package distribution 'ch.bafu.alpweiden-herdenschutzhunde' (managed)" in out - assert "Added package distribution 'ch.bafu.hydrologie-hintergrundkarte' (unmanaged)" in out + assert ( + "Added package distribution 'ch.bafu.alpweiden-herdenschutzhunde' (managed)" + in out + ) + assert ( + "Added package distribution 'ch.bafu.hydrologie-hintergrundkarte' (unmanaged)" + in out + ) assert "2 package_distribution(s) added" in out package_distribution = PackageDistribution.objects.get( - package_distribution_id='ch.bafu.alpweiden-herdenschutzhunde' + package_distribution_id="ch.bafu.alpweiden-herdenschutzhunde" ) assert package_distribution.managed_by_stac is True assert package_distribution.dataset == dataset_1 package_distribution = PackageDistribution.objects.get( - package_distribution_id='ch.bafu.hydrologie-hintergrundkarte' + package_distribution_id="ch.bafu.hydrologie-hintergrundkarte" ) assert package_distribution.managed_by_stac is False assert package_distribution.dataset == dataset_2 -@patch('distributions.management.commands.stac_sync.Client') -@patch('distributions.management.commands.stac_sync.get') +@patch("distributions.management.commands.stac_sync.Client") +@patch("distributions.management.commands.stac_sync.get") def test_command_imports_to_default_dataset(get, stac_client, provider, attribution): dataset = Dataset.objects.create( dataset_id="default", @@ -97,43 +103,51 @@ def test_command_imports_to_default_dataset(get, stac_client, provider, attribut stac_client.open.return_value.collection_search.return_value.collections.return_value = [ Collection( - id='ch.bafu.alpweiden-herdenschutzhunde', + id="ch.bafu.alpweiden-herdenschutzhunde", description=None, extent=None, - providers=[StacProvider(name='Federal Office for the Environment')] + providers=[StacProvider(name="Federal Office for the Environment")], ) ] get.return_value.text = '
ch.bafu.hydrologie-hintergrundkarte
' out = StringIO() - call_command("stac_sync", default_dataset='default', verbosity=2, stdout=out) + call_command("stac_sync", default_dataset="default", verbosity=2, stdout=out) out = out.getvalue() - assert "Added package distribution 'ch.bafu.alpweiden-herdenschutzhunde' (managed)" in out - assert "Added package distribution 'ch.bafu.hydrologie-hintergrundkarte' (unmanaged)" in out + assert ( + "Added package distribution 'ch.bafu.alpweiden-herdenschutzhunde' (managed)" + in out + ) + assert ( + "Added package distribution 'ch.bafu.hydrologie-hintergrundkarte' (unmanaged)" + in out + ) assert "2 package_distribution(s) added" in out package_distribution = PackageDistribution.objects.get( - package_distribution_id='ch.bafu.alpweiden-herdenschutzhunde' + package_distribution_id="ch.bafu.alpweiden-herdenschutzhunde" ) assert package_distribution.managed_by_stac is True assert package_distribution.dataset == dataset package_distribution = PackageDistribution.objects.get( - package_distribution_id='ch.bafu.hydrologie-hintergrundkarte' + package_distribution_id="ch.bafu.hydrologie-hintergrundkarte" ) assert package_distribution.managed_by_stac is False assert package_distribution.dataset == dataset -@patch('distributions.management.commands.stac_sync.Client') -@patch('distributions.management.commands.stac_sync.get') -def test_command_fails_to_import_if_dataset_is_missing(get, stac_client, provider, attribution): +@patch("distributions.management.commands.stac_sync.Client") +@patch("distributions.management.commands.stac_sync.get") +def test_command_fails_to_import_if_dataset_is_missing( + get, stac_client, provider, attribution +): stac_client.open.return_value.collection_search.return_value.collections.return_value = [ Collection( - id='ch.bafu.alpweiden-herdenschutzhunde', + id="ch.bafu.alpweiden-herdenschutzhunde", description=None, extent=None, - providers=[StacProvider(name='Federal Office for the Environment')] + providers=[StacProvider(name="Federal Office for the Environment")], ) ] get.return_value.text = '
ch.bafu.hydrologie-hintergrundkarte
' @@ -146,15 +160,17 @@ def test_command_fails_to_import_if_dataset_is_missing(get, stac_client, provide assert PackageDistribution.objects.count() == 0 -@patch('distributions.management.commands.stac_sync.Client') -@patch('distributions.management.commands.stac_sync.get') -def test_command_fails_if_default_dataset_is_missing(get, stac_client, provider, attribution): +@patch("distributions.management.commands.stac_sync.Client") +@patch("distributions.management.commands.stac_sync.get") +def test_command_fails_if_default_dataset_is_missing( + get, stac_client, provider, attribution +): stac_client.open.return_value.collection_search.return_value.collections.return_value = [ Collection( - id='ch.bafu.alpweiden-herdenschutzhunde', + id="ch.bafu.alpweiden-herdenschutzhunde", description=None, extent=None, - providers=[StacProvider(name='Federal Office for the Environment')] + providers=[StacProvider(name="Federal Office for the Environment")], ) ] get.return_value.text = '
ch.bafu.hydrologie-hintergrundkarte
' @@ -162,10 +178,10 @@ def test_command_fails_if_default_dataset_is_missing(get, stac_client, provider, out = StringIO() call_command( "stac_sync", - default_dataset='default', + default_dataset="default", no_create_default_dataset=True, verbosity=2, - stdout=out + stdout=out, ) out = out.getvalue() assert "No dataset for collection id 'ch.bafu.alpweiden-herdenschutzhunde'" in out @@ -174,49 +190,55 @@ def test_command_fails_if_default_dataset_is_missing(get, stac_client, provider, assert PackageDistribution.objects.count() == 0 -@patch('distributions.management.commands.stac_sync.Client') -@patch('distributions.management.commands.stac_sync.get') +@patch("distributions.management.commands.stac_sync.Client") +@patch("distributions.management.commands.stac_sync.get") def test_command_creates_default_dataset(get, stac_client, provider, attribution): stac_client.open.return_value.collection_search.return_value.collections.return_value = [ Collection( - id='ch.bafu.alpweiden-herdenschutzhunde', + id="ch.bafu.alpweiden-herdenschutzhunde", description=None, extent=None, - providers=[StacProvider(name='Federal Office for the Environment')] + providers=[StacProvider(name="Federal Office for the Environment")], ) ] get.return_value.text = '
ch.bafu.hydrologie-hintergrundkarte
' out = StringIO() - call_command("stac_sync", default_dataset='default', verbosity=2, stdout=out) + call_command("stac_sync", default_dataset="default", verbosity=2, stdout=out) out = out.getvalue() assert "Added provider 'default' for default dataset" in out assert "Added attribution 'default' for default dataset" in out assert "Added default dataset 'default'" in out - assert "Added package distribution 'ch.bafu.alpweiden-herdenschutzhunde' (managed)" in out - assert "Added package distribution 'ch.bafu.hydrologie-hintergrundkarte' (unmanaged)" in out + assert ( + "Added package distribution 'ch.bafu.alpweiden-herdenschutzhunde' (managed)" + in out + ) + assert ( + "Added package distribution 'ch.bafu.hydrologie-hintergrundkarte' (unmanaged)" + in out + ) assert "2 package_distribution(s) added" in out - default_dataset = Dataset.objects.get(dataset_id='default') - assert default_dataset.provider.provider_id == 'default' - assert default_dataset.attribution.attribution_id == 'default' + default_dataset = Dataset.objects.get(dataset_id="default") + assert default_dataset.provider.provider_id == "default" + assert default_dataset.attribution.attribution_id == "default" package_distribution = PackageDistribution.objects.get( - package_distribution_id='ch.bafu.alpweiden-herdenschutzhunde' + package_distribution_id="ch.bafu.alpweiden-herdenschutzhunde" ) assert package_distribution.managed_by_stac is True assert package_distribution.dataset == default_dataset package_distribution = PackageDistribution.objects.get( - package_distribution_id='ch.bafu.hydrologie-hintergrundkarte' + package_distribution_id="ch.bafu.hydrologie-hintergrundkarte" ) assert package_distribution.managed_by_stac is False assert package_distribution.dataset == default_dataset -@patch('distributions.management.commands.stac_sync.Client') -@patch('distributions.management.commands.stac_sync.get') +@patch("distributions.management.commands.stac_sync.Client") +@patch("distributions.management.commands.stac_sync.get") def test_command_does_not_need_to_import(get, stac_client, provider, attribution): dataset_1 = Dataset.objects.create( dataset_id="ch.bafu.alpweiden-herdenschutzhunde", @@ -251,13 +273,13 @@ def test_command_does_not_need_to_import(get, stac_client, provider, attribution attribution=attribution, ) PackageDistribution.objects.create( - package_distribution_id='ch.bafu.alpweiden-herdenschutzhunde', + package_distribution_id="ch.bafu.alpweiden-herdenschutzhunde", managed_by_stac=True, dataset=dataset_1, _legacy_imported=True, ) PackageDistribution.objects.create( - package_distribution_id='ch.bafu.hydrologie-hintergrundkarte', + package_distribution_id="ch.bafu.hydrologie-hintergrundkarte", managed_by_stac=False, dataset=dataset_2, _legacy_imported=True, @@ -265,10 +287,10 @@ def test_command_does_not_need_to_import(get, stac_client, provider, attribution stac_client.open.return_value.collection_search.return_value.collections.return_value = [ Collection( - id='ch.bafu.alpweiden-herdenschutzhunde', + id="ch.bafu.alpweiden-herdenschutzhunde", description=None, extent=None, - providers=[StacProvider(name='Federal Office for the Environment')] + providers=[StacProvider(name="Federal Office for the Environment")], ) ] get.return_value.text = '
ch.bafu.hydrologie-hintergrundkarte
' @@ -279,8 +301,8 @@ def test_command_does_not_need_to_import(get, stac_client, provider, attribution assert "nothing to be done, already up to date" in out -@patch('distributions.management.commands.stac_sync.Client') -@patch('distributions.management.commands.stac_sync.get') +@patch("distributions.management.commands.stac_sync.Client") +@patch("distributions.management.commands.stac_sync.get") def test_command_updates(get, stac_client, provider, attribution): dataset_old = Dataset.objects.create( dataset_id="ch.bafu.amphibienwanderung-verkehrskonflikte", @@ -347,19 +369,19 @@ def test_command_updates(get, stac_client, provider, attribution): attribution=attribution, ) PackageDistribution.objects.create( - package_distribution_id='ch.bafu.alpweiden-herdenschutzhunde', + package_distribution_id="ch.bafu.alpweiden-herdenschutzhunde", managed_by_stac=False, dataset=dataset_old, _legacy_imported=True, ) PackageDistribution.objects.create( - package_distribution_id='ch.bafu.hydrologie-hintergrundkarte', + package_distribution_id="ch.bafu.hydrologie-hintergrundkarte", managed_by_stac=True, dataset=dataset_old, _legacy_imported=True, ) PackageDistribution.objects.create( - package_distribution_id='ch.bafu.laerm-bahnlaerm_tag', + package_distribution_id="ch.bafu.laerm-bahnlaerm_tag", managed_by_stac=True, dataset=dataset_old, _legacy_imported=True, @@ -367,10 +389,10 @@ def test_command_updates(get, stac_client, provider, attribution): stac_client.open.return_value.collection_search.return_value.collections.return_value = [ Collection( - id='ch.bafu.alpweiden-herdenschutzhunde', + id="ch.bafu.alpweiden-herdenschutzhunde", description=None, extent=None, - providers=[StacProvider(name='Federal Office for the Environment')] + providers=[StacProvider(name="Federal Office for the Environment")], ) ] get.return_value.text = """ @@ -381,7 +403,7 @@ def test_command_updates(get, stac_client, provider, attribution): """ out = StringIO() - call_command("stac_sync", default_dataset='default', verbosity=2, stdout=out) + call_command("stac_sync", default_dataset="default", verbosity=2, stdout=out) out = out.getvalue() assert "Updated package distribution 'ch.bafu.alpweiden-herdenschutzhunde'" in out assert "Updated package distribution 'ch.bafu.hydrologie-hintergrundkarte'" in out @@ -389,26 +411,26 @@ def test_command_updates(get, stac_client, provider, attribution): assert "package_distribution(s) updated" in out package_distribution = PackageDistribution.objects.get( - package_distribution_id='ch.bafu.alpweiden-herdenschutzhunde' + package_distribution_id="ch.bafu.alpweiden-herdenschutzhunde" ) assert package_distribution.managed_by_stac is True assert package_distribution.dataset == dataset_new_1 package_distribution = PackageDistribution.objects.get( - package_distribution_id='ch.bafu.hydrologie-hintergrundkarte' + package_distribution_id="ch.bafu.hydrologie-hintergrundkarte" ) assert package_distribution.managed_by_stac is False assert package_distribution.dataset == dataset_new_2 package_distribution = PackageDistribution.objects.get( - package_distribution_id='ch.bafu.laerm-bahnlaerm_tag' + package_distribution_id="ch.bafu.laerm-bahnlaerm_tag" ) assert package_distribution.managed_by_stac is False assert package_distribution.dataset == dataset_new_3 -@patch('distributions.management.commands.stac_sync.Client') -@patch('distributions.management.commands.stac_sync.get') +@patch("distributions.management.commands.stac_sync.Client") +@patch("distributions.management.commands.stac_sync.get") def test_command_removes_orphans(get, stac_client, provider, attribution): dataset = Dataset.objects.create( dataset_id="ch.bafu.alpweiden-herdenschutzhunde", @@ -451,63 +473,65 @@ def test_command_removes_orphans(get, stac_client, provider, attribution): attribution=attribution, ) PackageDistribution.objects.create( - package_distribution_id='remove_missing_in_html', + package_distribution_id="remove_missing_in_html", managed_by_stac=False, dataset=dataset, - _legacy_imported=True + _legacy_imported=True, ) PackageDistribution.objects.create( - package_distribution_id='remove_missing_in_stac', + package_distribution_id="remove_missing_in_stac", managed_by_stac=True, dataset=dataset, - _legacy_imported=True + _legacy_imported=True, ) PackageDistribution.objects.create( - package_distribution_id='remove_missing_dataset_html', + package_distribution_id="remove_missing_dataset_html", managed_by_stac=False, dataset=dataset, - _legacy_imported=True + _legacy_imported=True, ) PackageDistribution.objects.create( - package_distribution_id='remove_missing_dataset_stac', + package_distribution_id="remove_missing_dataset_stac", managed_by_stac=True, dataset=dataset, - _legacy_imported=True + _legacy_imported=True, ) PackageDistribution.objects.create( - package_distribution_id='keep_not_legacy', + package_distribution_id="keep_not_legacy", managed_by_stac=True, dataset=dataset, - _legacy_imported=False + _legacy_imported=False, ) PackageDistribution.objects.create( - package_distribution_id='keep_html', + package_distribution_id="keep_html", managed_by_stac=False, dataset=dataset_keep_html, - _legacy_imported=True + _legacy_imported=True, ) PackageDistribution.objects.create( - package_distribution_id='keep_stac', + package_distribution_id="keep_stac", managed_by_stac=True, dataset=dataset_keep_stac, - _legacy_imported=True + _legacy_imported=True, ) stac_client.open.return_value.collection_search.return_value.collections.return_value = [ Collection( - id='remove_missing_dataset_stac', + id="remove_missing_dataset_stac", description=None, extent=None, - providers=[StacProvider(name='remove_missing_dataset_provider')] + providers=[StacProvider(name="remove_missing_dataset_provider")], ), Collection( - id='keep_stac', + id="keep_stac", description=None, extent=None, - providers=[StacProvider(name='remove_missing_dataset_provider')] - ) + providers=[StacProvider(name="remove_missing_dataset_provider")], + ), ] - get.return_value.text = '
remove_missing_dataset_html\nkeep_html
' + get.return_value.text = ( + '
remove_missing_dataset_html\nkeep_html
' + ) out = StringIO() call_command("stac_sync", verbosity=2, stdout=out) @@ -515,13 +539,18 @@ def test_command_removes_orphans(get, stac_client, provider, attribution): assert "4 packagedistribution(s) removed" in out assert PackageDistribution.objects.count() == 3 - assert {p.package_distribution_id for p in PackageDistribution.objects.all() - } == {'keep_html', 'keep_stac', 'keep_not_legacy'} + assert {p.package_distribution_id for p in PackageDistribution.objects.all()} == { + "keep_html", + "keep_stac", + "keep_not_legacy", + } -@patch('distributions.management.commands.stac_sync.Client') -@patch('distributions.management.commands.stac_sync.get') -def test_command_removes_orphans_default_datset(get, stac_client, provider, attribution): +@patch("distributions.management.commands.stac_sync.Client") +@patch("distributions.management.commands.stac_sync.get") +def test_command_removes_orphans_default_datset( + get, stac_client, provider, attribution +): dataset = Dataset.objects.create( dataset_id="ch.bafu.alpweiden-herdenschutzhunde", geocat_id="ab76361f-657d-4705-9053-95f89ecab126", @@ -563,81 +592,83 @@ def test_command_removes_orphans_default_datset(get, stac_client, provider, attr attribution=attribution, ) PackageDistribution.objects.create( - package_distribution_id='remove_missing_in_html', + package_distribution_id="remove_missing_in_html", managed_by_stac=False, dataset=dataset, - _legacy_imported=True + _legacy_imported=True, ) PackageDistribution.objects.create( - package_distribution_id='remove_missing_in_stac', + package_distribution_id="remove_missing_in_stac", managed_by_stac=True, dataset=dataset, - _legacy_imported=True + _legacy_imported=True, ) PackageDistribution.objects.create( - package_distribution_id='keep_missing_dataset_html', + package_distribution_id="keep_missing_dataset_html", managed_by_stac=False, dataset=dataset, - _legacy_imported=True + _legacy_imported=True, ) PackageDistribution.objects.create( - package_distribution_id='keep_missing_dataset_stac', + package_distribution_id="keep_missing_dataset_stac", managed_by_stac=True, dataset=dataset, - _legacy_imported=True + _legacy_imported=True, ) PackageDistribution.objects.create( - package_distribution_id='keep_not_legacy', + package_distribution_id="keep_not_legacy", managed_by_stac=True, dataset=dataset, - _legacy_imported=False + _legacy_imported=False, ) PackageDistribution.objects.create( - package_distribution_id='keep_html', + package_distribution_id="keep_html", managed_by_stac=False, dataset=dataset_keep_html, - _legacy_imported=True + _legacy_imported=True, ) PackageDistribution.objects.create( - package_distribution_id='keep_stac', + package_distribution_id="keep_stac", managed_by_stac=True, dataset=dataset_keep_stac, - _legacy_imported=True + _legacy_imported=True, ) stac_client.open.return_value.collection_search.return_value.collections.return_value = [ Collection( - id='keep_missing_dataset_stac', + id="keep_missing_dataset_stac", description=None, extent=None, - providers=[StacProvider(name='keep_missing_dataset_provider')] + providers=[StacProvider(name="keep_missing_dataset_provider")], ), Collection( - id='keep_stac', + id="keep_stac", description=None, extent=None, - providers=[StacProvider(name='keep_missing_dataset_provider')] - ) + providers=[StacProvider(name="keep_missing_dataset_provider")], + ), ] get.return_value.text = '
keep_missing_dataset_html\nkeep_html
' out = StringIO() - call_command("stac_sync", verbosity=2, stdout=out, default_dataset=dataset.dataset_id) + call_command( + "stac_sync", verbosity=2, stdout=out, default_dataset=dataset.dataset_id + ) out = out.getvalue() assert "2 packagedistribution(s) removed" in out assert PackageDistribution.objects.count() == 5 assert {p.package_distribution_id for p in PackageDistribution.objects.all()} == { - 'keep_html', - 'keep_stac', - 'keep_not_legacy', - 'keep_missing_dataset_html', - 'keep_missing_dataset_stac' + "keep_html", + "keep_stac", + "keep_not_legacy", + "keep_missing_dataset_html", + "keep_missing_dataset_stac", } -@patch('distributions.management.commands.stac_sync.Client') -@patch('distributions.management.commands.stac_sync.get') +@patch("distributions.management.commands.stac_sync.Client") +@patch("distributions.management.commands.stac_sync.get") def test_command_clears(get, stac_client, provider, attribution): dataset = Dataset.objects.create( dataset_id="ch.bafu.alpweiden-herdenschutzhunde", @@ -656,19 +687,19 @@ def test_command_clears(get, stac_client, provider, attribution): attribution=attribution, ) PackageDistribution.objects.create( - package_distribution_id='ch.bafu.alpweiden-herdenschutzhunde.1', + package_distribution_id="ch.bafu.alpweiden-herdenschutzhunde.1", managed_by_stac=False, dataset=dataset, _legacy_imported=True, ) PackageDistribution.objects.create( - package_distribution_id='ch.bafu.alpweiden-herdenschutzhunde.2', + package_distribution_id="ch.bafu.alpweiden-herdenschutzhunde.2", managed_by_stac=True, dataset=dataset, _legacy_imported=True, ) PackageDistribution.objects.create( - package_distribution_id='ch.bafu.alpweiden-herdenschutzhunde.3', + package_distribution_id="ch.bafu.alpweiden-herdenschutzhunde.3", managed_by_stac=True, dataset=dataset, _legacy_imported=False, @@ -684,12 +715,14 @@ def test_command_clears(get, stac_client, provider, attribution): assert "2 packagedistribution(s) cleared" in out assert PackageDistribution.objects.count() == 1 - assert PackageDistribution.objects.first( - ).package_distribution_id == 'ch.bafu.alpweiden-herdenschutzhunde.3' + assert ( + PackageDistribution.objects.first().package_distribution_id + == "ch.bafu.alpweiden-herdenschutzhunde.3" + ) -@patch('distributions.management.commands.stac_sync.Client') -@patch('distributions.management.commands.stac_sync.get') +@patch("distributions.management.commands.stac_sync.Client") +@patch("distributions.management.commands.stac_sync.get") def test_command_runs_dry(get, stac_client, provider, attribution): dataset = Dataset.objects.create( dataset_id="ch.bafu.amphibienwanderung-verkehrskonflikte", @@ -740,13 +773,13 @@ def test_command_runs_dry(get, stac_client, provider, attribution): attribution=attribution, ) PackageDistribution.objects.create( - package_distribution_id='ch.bafu.alpweiden-herdenschutzhunde', + package_distribution_id="ch.bafu.alpweiden-herdenschutzhunde", managed_by_stac=False, dataset=dataset, _legacy_imported=True, ) PackageDistribution.objects.create( - package_distribution_id='ch.bafu.hydrologie-hintergrundkarte', + package_distribution_id="ch.bafu.hydrologie-hintergrundkarte", managed_by_stac=True, dataset=dataset, _legacy_imported=True, @@ -754,10 +787,10 @@ def test_command_runs_dry(get, stac_client, provider, attribution): stac_client.open.return_value.collection_search.return_value.collections.return_value = [ Collection( - id='ch.bafu.alpweiden-herdenschutzhunde', + id="ch.bafu.alpweiden-herdenschutzhunde", description=None, extent=None, - providers=[StacProvider(name='Federal Office for the Environment')] + providers=[StacProvider(name="Federal Office for the Environment")], ) ] get.return_value.text = '
ch.bafu.hydrologie-hintergrundkarte
' @@ -771,20 +804,20 @@ def test_command_runs_dry(get, stac_client, provider, attribution): assert "dry run, aborting transaction" in out package_distribution = PackageDistribution.objects.get( - package_distribution_id='ch.bafu.alpweiden-herdenschutzhunde' + package_distribution_id="ch.bafu.alpweiden-herdenschutzhunde" ) assert package_distribution.managed_by_stac is False assert package_distribution.dataset == dataset package_distribution = PackageDistribution.objects.get( - package_distribution_id='ch.bafu.hydrologie-hintergrundkarte' + package_distribution_id="ch.bafu.hydrologie-hintergrundkarte" ) assert package_distribution.managed_by_stac is True assert package_distribution.dataset == dataset -@patch('distributions.management.commands.stac_sync.Client') -@patch('distributions.management.commands.stac_sync.get') +@patch("distributions.management.commands.stac_sync.Client") +@patch("distributions.management.commands.stac_sync.get") def test_command_warns_about_missing_provider(get, stac_client, provider, attribution): Dataset.objects.create( dataset_id="ch.bafu.alpweiden-herdenschutzhunde", @@ -804,7 +837,10 @@ def test_command_warns_about_missing_provider(get, stac_client, provider, attrib ) stac_client.open.return_value.collection_search.return_value.collections.return_value = [ Collection( - id='ch.bafu.alpweiden-herdenschutzhunde', description=None, extent=None, providers=[] + id="ch.bafu.alpweiden-herdenschutzhunde", + description=None, + extent=None, + providers=[], ) ] get.return_value.text = '
' @@ -818,9 +854,11 @@ def test_command_warns_about_missing_provider(get, stac_client, provider, attrib assert PackageDistribution.objects.first() -@patch('distributions.management.commands.stac_sync.Client') -@patch('distributions.management.commands.stac_sync.get') -def test_command_warns_about_multiple_providers(get, stac_client, provider, attribution): +@patch("distributions.management.commands.stac_sync.Client") +@patch("distributions.management.commands.stac_sync.get") +def test_command_warns_about_multiple_providers( + get, stac_client, provider, attribution +): Dataset.objects.create( dataset_id="ch.bafu.alpweiden-herdenschutzhunde", geocat_id="ab76361f-657d-4705-9053-95f89ecab126", @@ -839,13 +877,13 @@ def test_command_warns_about_multiple_providers(get, stac_client, provider, attr ) stac_client.open.return_value.collection_search.return_value.collections.return_value = [ Collection( - id='ch.bafu.alpweiden-herdenschutzhunde', + id="ch.bafu.alpweiden-herdenschutzhunde", description=None, extent=None, providers=[ - StacProvider(name='Federal Office for the Environment'), - StacProvider(name='Federal Office for the Environment') - ] + StacProvider(name="Federal Office for the Environment"), + StacProvider(name="Federal Office for the Environment"), + ], ), ] get.return_value.text = '
' @@ -855,12 +893,15 @@ def test_command_warns_about_multiple_providers(get, stac_client, provider, attr out = out.getvalue() assert "Added package distribution 'ch.bafu.alpweiden-herdenschutzhunde'" in out assert "1 package_distribution(s) added" in out - assert "Collection 'ch.bafu.alpweiden-herdenschutzhunde' has more than one provider" in out + assert ( + "Collection 'ch.bafu.alpweiden-herdenschutzhunde' has more than one provider" + in out + ) assert PackageDistribution.objects.first() -@patch('distributions.management.commands.stac_sync.Client') -@patch('distributions.management.commands.stac_sync.get') +@patch("distributions.management.commands.stac_sync.Client") +@patch("distributions.management.commands.stac_sync.get") def test_command_warns_about_unknown_provider(get, stac_client, provider, attribution): Dataset.objects.create( dataset_id="ch.bafu.alpweiden-herdenschutzhunde", @@ -880,10 +921,10 @@ def test_command_warns_about_unknown_provider(get, stac_client, provider, attrib ) stac_client.open.return_value.collection_search.return_value.collections.return_value = [ Collection( - id='ch.bafu.alpweiden-herdenschutzhunde', + id="ch.bafu.alpweiden-herdenschutzhunde", description=None, extent=None, - providers=[StacProvider(name='Federal Office of Culture FOC')] + providers=[StacProvider(name="Federal Office of Culture FOC")], ) ] get.return_value.text = '
' @@ -897,9 +938,11 @@ def test_command_warns_about_unknown_provider(get, stac_client, provider, attrib assert PackageDistribution.objects.first() -@patch('distributions.management.commands.stac_sync.Client') -@patch('distributions.management.commands.stac_sync.get') -def test_command_does_not_warn_about_similar_provider(get, stac_client, provider, attribution): +@patch("distributions.management.commands.stac_sync.Client") +@patch("distributions.management.commands.stac_sync.get") +def test_command_does_not_warn_about_similar_provider( + get, stac_client, provider, attribution +): Dataset.objects.create( dataset_id="ch.bafu.alpweiden-herdenschutzhunde", geocat_id="ab76361f-657d-4705-9053-95f89ecab126", @@ -918,10 +961,10 @@ def test_command_does_not_warn_about_similar_provider(get, stac_client, provider ) stac_client.open.return_value.collection_search.return_value.collections.return_value = [ Collection( - id='ch.bafu.alpweiden-herdenschutzhunde', + id="ch.bafu.alpweiden-herdenschutzhunde", description=None, extent=None, - providers=[StacProvider(name='Federal office for the environment')] + providers=[StacProvider(name="Federal office for the environment")], ) ] get.return_value.text = '
' diff --git a/app/tests/provider/test_provider_api.py b/app/tests/provider/test_provider_api.py index 26bfb2b9..0d9e71ba 100644 --- a/app/tests/provider/test_provider_api.py +++ b/app/tests/provider/test_provider_api.py @@ -24,13 +24,15 @@ def test_provider_to_response_returns_response_with_language_as_defined(provider en="FOEN", it="UFAM", rm="UFAM", - ) + ), ) assert actual == expected -def test_provider_to_response_returns_response_with_default_language_if_undefined(provider): +def test_provider_to_response_returns_response_with_default_language_if_undefined( + provider, +): provider.name_it = None provider.name_rm = None provider.acronym_it = None @@ -55,7 +57,7 @@ def test_provider_to_response_returns_response_with_default_language_if_undefine en="FOEN", it=None, rm=None, - ) + ), ) assert actual == expected @@ -64,8 +66,8 @@ def test_provider_to_response_returns_response_with_default_language_if_undefine def test_get_provider_returns_existing_provider_with_default_language( provider, client, django_user_factory ): - django_user_factory('test', 'test', [('provider', 'provider', 'view_provider')]) - client.login(username='test', password='test') + django_user_factory("test", "test", [("provider", "provider", "view_provider")]) + client.login(username="test", password="test") response = client.get(f"/api/v1/providers/{provider.provider_id}") @@ -87,15 +89,15 @@ def test_get_provider_returns_existing_provider_with_default_language( "en": "FOEN", "it": "UFAM", "rm": "UFAM", - } + }, } def test_get_provider_returns_provider_with_language_from_query( provider, client, django_user_factory ): - django_user_factory('test', 'test', [('provider', 'provider', 'view_provider')]) - client.login(username='test', password='test') + django_user_factory("test", "test", [("provider", "provider", "view_provider")]) + client.login(username="test", password="test") response = client.get(f"/api/v1/providers/{provider.provider_id}?lang=de") @@ -117,13 +119,13 @@ def test_get_provider_returns_provider_with_language_from_query( "en": "FOEN", "it": "UFAM", "rm": "UFAM", - } + }, } def test_get_provider_returns_404_for_nonexisting_provider(client, django_user_factory): - django_user_factory('test', 'test', [('provider', 'provider', 'view_provider')]) - client.login(username='test', password='test') + django_user_factory("test", "test", [("provider", "provider", "view_provider")]) + client.login(username="test", password="test") response = client.get("/api/v1/providers/2") @@ -134,8 +136,8 @@ def test_get_provider_returns_404_for_nonexisting_provider(client, django_user_f def test_get_provider_skips_translations_that_are_not_available( provider, client, django_user_factory ): - django_user_factory('test', 'test', [('provider', 'provider', 'view_provider')]) - client.login(username='test', password='test') + django_user_factory("test", "test", [("provider", "provider", "view_provider")]) + client.login(username="test", password="test") provider = Provider.objects.last() provider.name_it = None @@ -160,15 +162,15 @@ def test_get_provider_skips_translations_that_are_not_available( "de": "BAFU", "fr": "OFEV", "en": "FOEN", - } + }, } def test_get_provider_returns_provider_with_language_from_header( provider, client, django_user_factory ): - django_user_factory('test', 'test', [('provider', 'provider', 'view_provider')]) - client.login(username='test', password='test') + django_user_factory("test", "test", [("provider", "provider", "view_provider")]) + client.login(username="test", password="test") response = client.get( f"/api/v1/providers/{provider.provider_id}", headers={"Accept-Language": "de"} @@ -192,18 +194,19 @@ def test_get_provider_returns_provider_with_language_from_header( "en": "FOEN", "it": "UFAM", "rm": "UFAM", - } + }, } def test_get_provider_returns_provider_with_language_from_query_param_even_if_header_set( provider, client, django_user_factory ): - django_user_factory('test', 'test', [('provider', 'provider', 'view_provider')]) - client.login(username='test', password='test') + django_user_factory("test", "test", [("provider", "provider", "view_provider")]) + client.login(username="test", password="test") response = client.get( - f"/api/v1/providers/{provider.provider_id}?lang=fr", headers={"Accept-Language": "de"} + f"/api/v1/providers/{provider.provider_id}?lang=fr", + headers={"Accept-Language": "de"}, ) assert response.status_code == 200 @@ -224,15 +227,15 @@ def test_get_provider_returns_provider_with_language_from_query_param_even_if_he "en": "FOEN", "it": "UFAM", "rm": "UFAM", - } + }, } def test_get_provider_returns_provider_with_default_language_if_header_empty( provider, client, django_user_factory ): - django_user_factory('test', 'test', [('provider', 'provider', 'view_provider')]) - client.login(username='test', password='test') + django_user_factory("test", "test", [("provider", "provider", "view_provider")]) + client.login(username="test", password="test") response = client.get( f"/api/v1/providers/{provider.provider_id}", headers={"Accept-Language": ""} @@ -256,19 +259,19 @@ def test_get_provider_returns_provider_with_default_language_if_header_empty( "en": "FOEN", "it": "UFAM", "rm": "UFAM", - } + }, } def test_get_provider_returns_provider_with_first_known_language_from_header( provider, client, django_user_factory ): - django_user_factory('test', 'test', [('provider', 'provider', 'view_provider')]) - client.login(username='test', password='test') + django_user_factory("test", "test", [("provider", "provider", "view_provider")]) + client.login(username="test", password="test") response = client.get( f"/api/v1/providers/{provider.provider_id}", - headers={"Accept-Language": "cn, *, de-DE, en"} + headers={"Accept-Language": "cn, *, de-DE, en"}, ) assert response.status_code == 200 @@ -289,19 +292,19 @@ def test_get_provider_returns_provider_with_first_known_language_from_header( "en": "FOEN", "it": "UFAM", "rm": "UFAM", - } + }, } def test_get_provider_returns_provider_with_first_known_language_from_header_ignoring_qfactor( provider, client, django_user_factory ): - django_user_factory('test', 'test', [('provider', 'provider', 'view_provider')]) - client.login(username='test', password='test') + django_user_factory("test", "test", [("provider", "provider", "view_provider")]) + client.login(username="test", password="test") response = client.get( f"/api/v1/providers/{provider.provider_id}", - headers={"Accept-Language": "fr;q=0.9, de;q=0.8"} + headers={"Accept-Language": "fr;q=0.9, de;q=0.8"}, ) assert response.status_code == 200 @@ -322,7 +325,7 @@ def test_get_provider_returns_provider_with_first_known_language_from_header_ign "en": "FOEN", "it": "UFAM", "rm": "UFAM", - } + }, } @@ -333,9 +336,11 @@ def test_get_provider_returns_401_if_not_logged_in(provider, client): assert response.json() == {"code": 401, "description": "Unauthorized"} -def test_get_provider_returns_403_if_no_permission(provider, client, django_user_factory): - django_user_factory('test', 'test', []) - client.login(username='test', password='test') +def test_get_provider_returns_403_if_no_permission( + provider, client, django_user_factory +): + django_user_factory("test", "test", []) + client.login(username="test", password="test") response = client.get(f"/api/v1/providers/{provider.provider_id}") @@ -346,40 +351,42 @@ def test_get_provider_returns_403_if_no_permission(provider, client, django_user def test_get_providers_returns_single_provider_with_given_language( provider, client, django_user_factory ): - django_user_factory('test', 'test', [('provider', 'provider', 'view_provider')]) - client.login(username='test', password='test') + django_user_factory("test", "test", [("provider", "provider", "view_provider")]) + client.login(username="test", password="test") response = client.get("/api/v1/providers?lang=fr") assert response.status_code == 200 assert response.json() == { - "items": [{ - "id": "ch.bafu", - "name": "Office fédéral de l'environnement", - "name_translations": { - "de": "Bundesamt für Umwelt", - "fr": "Office fédéral de l'environnement", - "en": "Federal Office for the Environment", - "it": "Ufficio federale dell'ambiente", - "rm": "Uffizi federal per l'ambient", - }, - "acronym": "OFEV", - "acronym_translations": { - "de": "BAFU", - "fr": "OFEV", - "en": "FOEN", - "it": "UFAM", - "rm": "UFAM", + "items": [ + { + "id": "ch.bafu", + "name": "Office fédéral de l'environnement", + "name_translations": { + "de": "Bundesamt für Umwelt", + "fr": "Office fédéral de l'environnement", + "en": "Federal Office for the Environment", + "it": "Ufficio federale dell'ambiente", + "rm": "Uffizi federal per l'ambient", + }, + "acronym": "OFEV", + "acronym_translations": { + "de": "BAFU", + "fr": "OFEV", + "en": "FOEN", + "it": "UFAM", + "rm": "UFAM", + }, } - }] + ] } def test_get_providers_skips_translations_that_are_not_available( provider, client, django_user_factory ): - django_user_factory('test', 'test', [('provider', 'provider', 'view_provider')]) - client.login(username='test', password='test') + django_user_factory("test", "test", [("provider", "provider", "view_provider")]) + client.login(username="test", password="test") provider = Provider.objects.last() provider.name_it = None @@ -392,61 +399,65 @@ def test_get_providers_skips_translations_that_are_not_available( assert response.status_code == 200 assert response.json() == { - "items": [{ - "id": "ch.bafu", - "name": "Federal Office for the Environment", - "name_translations": { - "de": "Bundesamt für Umwelt", - "fr": "Office fédéral de l'environnement", - "en": "Federal Office for the Environment", - }, - "acronym": "FOEN", - "acronym_translations": { - "de": "BAFU", - "fr": "OFEV", - "en": "FOEN", + "items": [ + { + "id": "ch.bafu", + "name": "Federal Office for the Environment", + "name_translations": { + "de": "Bundesamt für Umwelt", + "fr": "Office fédéral de l'environnement", + "en": "Federal Office for the Environment", + }, + "acronym": "FOEN", + "acronym_translations": { + "de": "BAFU", + "fr": "OFEV", + "en": "FOEN", + }, } - }] + ] } def test_get_providers_returns_provider_with_language_from_header( provider, client, django_user_factory ): - django_user_factory('test', 'test', [('provider', 'provider', 'view_provider')]) - client.login(username='test', password='test') + django_user_factory("test", "test", [("provider", "provider", "view_provider")]) + client.login(username="test", password="test") response = client.get("/api/v1/providers", headers={"Accept-Language": "de"}) assert response.status_code == 200 assert response.json() == { - "items": [{ - "id": "ch.bafu", - "name": "Bundesamt für Umwelt", - "name_translations": { - "de": "Bundesamt für Umwelt", - "fr": "Office fédéral de l'environnement", - "en": "Federal Office for the Environment", - "it": "Ufficio federale dell'ambiente", - "rm": "Uffizi federal per l'ambient", - }, - "acronym": "BAFU", - "acronym_translations": { - "de": "BAFU", - "fr": "OFEV", - "en": "FOEN", - "it": "UFAM", - "rm": "UFAM", + "items": [ + { + "id": "ch.bafu", + "name": "Bundesamt für Umwelt", + "name_translations": { + "de": "Bundesamt für Umwelt", + "fr": "Office fédéral de l'environnement", + "en": "Federal Office for the Environment", + "it": "Ufficio federale dell'ambiente", + "rm": "Uffizi federal per l'ambient", + }, + "acronym": "BAFU", + "acronym_translations": { + "de": "BAFU", + "fr": "OFEV", + "en": "FOEN", + "it": "UFAM", + "rm": "UFAM", + }, } - }] + ] } def test_get_providers_returns_all_providers_ordered_by_id_with_given_language( provider, client, django_user_factory ): - django_user_factory('test', 'test', [('provider', 'provider', 'view_provider')]) - client.login(username='test', password='test') + django_user_factory("test", "test", [("provider", "provider", "view_provider")]) + client.login(username="test", password="test") provider = { "provider_id": "ch.bav", @@ -485,7 +496,7 @@ def test_get_providers_returns_all_providers_ordered_by_id_with_given_language( "en": "FOEN", "it": "UFAM", "rm": "UFAM", - } + }, }, { "id": "ch.bav", @@ -504,7 +515,7 @@ def test_get_providers_returns_all_providers_ordered_by_id_with_given_language( "en": "FOT", "it": "UFT", "rm": "UFT", - } + }, }, ] } @@ -518,8 +529,8 @@ def test_get_providers_returns_401_if_not_logged_in(client): def test_get_providers_returns_403_if_no_permission(client, django_user_factory): - django_user_factory('test', 'test', []) - client.login(username='test', password='test') + django_user_factory("test", "test", []) + client.login(username="test", password="test") response = client.get("/api/v1/providers") diff --git a/app/tests/provider/test_provider_model.py b/app/tests/provider/test_provider_model.py index fd31e677..32d72462 100644 --- a/app/tests/provider/test_provider_model.py +++ b/app/tests/provider/test_provider_model.py @@ -81,9 +81,7 @@ def test_raises_exception_when_creating_db_object_with_mandatory_field_null(db): def test_form_valid_for_blank_optional_field(db): - class ProviderForm(ModelForm): - class Meta: model = Provider fields = "__all__" @@ -103,9 +101,7 @@ class Meta: def test_form_invalid_for_blank_mandatory_field(db): - class ProviderForm(ModelForm): - class Meta: model = Provider fields = "__all__" diff --git a/app/tests/support/test_superuser_command.py b/app/tests/support/test_superuser_command.py index 64645e1a..6ab6586c 100644 --- a/app/tests/support/test_superuser_command.py +++ b/app/tests/support/test_superuser_command.py @@ -6,68 +6,69 @@ @patch.dict( - 'os.environ', + "os.environ", { - 'DJANGO_SUPERUSER_USERNAME': 'admin', - 'DJANGO_SUPERUSER_EMAIL': 'admin@admin.ch', - 'DJANGO_SUPERUSER_PASSWORD': 'very-secure' - } + "DJANGO_SUPERUSER_USERNAME": "admin", + "DJANGO_SUPERUSER_EMAIL": "admin@admin.ch", + "DJANGO_SUPERUSER_PASSWORD": "very-secure", + }, ) def test_command_creates(db): out = StringIO() - call_command('manage_superuser', verbosity=2, stdout=out) - assert 'Created the superuser admin' in out.getvalue() + call_command("manage_superuser", verbosity=2, stdout=out) + assert "Created the superuser admin" in out.getvalue() - user = get_user_model().objects.filter(username='admin').first() + user = get_user_model().objects.filter(username="admin").first() assert user - assert user.email == 'admin@admin.ch' - assert user.check_password('very-secure') + assert user.email == "admin@admin.ch" + assert user.check_password("very-secure") assert user.is_staff assert user.is_superuser @patch.dict( - 'os.environ', + "os.environ", { - 'DJANGO_SUPERUSER_USERNAME': 'admin', - 'DJANGO_SUPERUSER_EMAIL': 'admin@admin.ch', - 'DJANGO_SUPERUSER_PASSWORD': 'very-secure' - } + "DJANGO_SUPERUSER_USERNAME": "admin", + "DJANGO_SUPERUSER_EMAIL": "admin@admin.ch", + "DJANGO_SUPERUSER_PASSWORD": "very-secure", + }, ) def test_command_updates(db): user = get_user_model().objects.create( - username='admin', email='amdin@amdin.ch', is_staff=False, is_superuser=False + username="admin", email="amdin@amdin.ch", is_staff=False, is_superuser=False ) - user.set_password('not-secure') + user.set_password("not-secure") out = StringIO() - call_command('manage_superuser', verbosity=2, stdout=out) - assert 'Updated the superuser admin' in out.getvalue() + call_command("manage_superuser", verbosity=2, stdout=out) + assert "Updated the superuser admin" in out.getvalue() - user = get_user_model().objects.filter(username='admin').first() + user = get_user_model().objects.filter(username="admin").first() assert user - assert user.email == 'admin@admin.ch' - assert user.check_password('very-secure') + assert user.email == "admin@admin.ch" + assert user.check_password("very-secure") assert user.is_staff assert user.is_superuser def test_fails_if_undefined(db): out = StringIO() - call_command('manage_superuser', stderr=out) - assert 'Environment variables not set or empty' in out.getvalue() + call_command("manage_superuser", stderr=out) + assert "Environment variables not set or empty" in out.getvalue() assert get_user_model().objects.count() == 0 @patch.dict( - 'os.environ', { - 'DJANGO_SUPERUSER_USERNAME': '', - 'DJANGO_SUPERUSER_EMAIL': '', - 'DJANGO_SUPERUSER_PASSWORD': '' - } + "os.environ", + { + "DJANGO_SUPERUSER_USERNAME": "", + "DJANGO_SUPERUSER_EMAIL": "", + "DJANGO_SUPERUSER_PASSWORD": "", + }, ) def test_fails_if_empty(db): out = StringIO() - call_command('manage_superuser', stderr=out) - assert 'Environment variables not set or empty' in out.getvalue() + call_command("manage_superuser", stderr=out) + assert "Environment variables not set or empty" in out.getvalue() assert get_user_model().objects.count() == 0 diff --git a/app/tests/utils/test_command.py b/app/tests/utils/test_command.py index 9088860b..e50c116e 100644 --- a/app/tests/utils/test_command.py +++ b/app/tests/utils/test_command.py @@ -9,7 +9,6 @@ class Handler(CommandHandler): - def __init__(self, command, options): super().__init__(command, options) self.logger = MagicMock() @@ -46,7 +45,6 @@ def run(self): class Command(CustomBaseCommand): - def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.handler = None @@ -182,15 +180,15 @@ def test_print_to_log_args_kwargs(): command = Command() call_command(command, verbosity=3, logger=True) calls = command.handler.logger.mock_calls - assert call.info('Print %s', 'JohnDoe') in calls - assert call.info('Print', extra={'n': 'JohnDoe'}) in calls - assert call.info('Print %s', 'John', extra={'n': 'Doe'}) in calls - assert call.info('Success JohnDoe') in calls - assert call.info('Success', extra={'n': 'JohnDoe'}) in calls - assert call.info('Success John', extra={'n': 'Doe'}) in calls - assert call.warning('Warning JohnDoe') in calls - assert call.warning('Warning', extra={'n': 'JohnDoe'}) in calls - assert call.warning('Warning John', extra={'n': 'Doe'}) in calls - assert call.error('Error JohnDoe') in calls - assert call.error('Error', extra={'n': 'JohnDoe'}) in calls - assert call.error('Error John', extra={'n': 'Doe'}) in calls + assert call.info("Print %s", "JohnDoe") in calls + assert call.info("Print", extra={"n": "JohnDoe"}) in calls + assert call.info("Print %s", "John", extra={"n": "Doe"}) in calls + assert call.info("Success JohnDoe") in calls + assert call.info("Success", extra={"n": "JohnDoe"}) in calls + assert call.info("Success John", extra={"n": "Doe"}) in calls + assert call.warning("Warning JohnDoe") in calls + assert call.warning("Warning", extra={"n": "JohnDoe"}) in calls + assert call.warning("Warning John", extra={"n": "Doe"}) in calls + assert call.error("Error JohnDoe") in calls + assert call.error("Error", extra={"n": "JohnDoe"}) in calls + assert call.error("Error John", extra={"n": "Doe"}) in calls diff --git a/app/tests/utils/test_database_router.py b/app/tests/utils/test_database_router.py index ce65f3dd..4e75a37d 100644 --- a/app/tests/utils/test_database_router.py +++ b/app/tests/utils/test_database_router.py @@ -4,14 +4,14 @@ def test_database_routing_inside_tests(): - assert BodContactOrganisation.objects.db == 'default' - assert Provider.objects.db == 'default' + assert BodContactOrganisation.objects.db == "default" + assert Provider.objects.db == "default" def test_database_routing_outside_tests(settings): settings.TESTING = False - assert BodContactOrganisation.objects.db == 'bod' - assert Provider.objects.db == 'default' + assert BodContactOrganisation.objects.db == "bod" + assert Provider.objects.db == "default" def test_writing_to_bod_supported_inside_tests(db): diff --git a/app/tests/utils/test_exceptions.py b/app/tests/utils/test_exceptions.py index b0f9ea62..a19f6e86 100644 --- a/app/tests/utils/test_exceptions.py +++ b/app/tests/utils/test_exceptions.py @@ -5,109 +5,116 @@ def test_contains_error_code_returns_true_for_single_error_with_matching_code(): - exception = ValidationError(message=None, code="code1") assert contains_error_code(exception, "code1") def test_contains_error_code_returns_false_for_single_error_without_matching_code(): - exception = ValidationError(message=None, code="code1") assert not contains_error_code(exception, "code2") def test_contains_error_code_returns_true_for_error_dict_where_one_has_matching_code(): - - exception = ValidationError({ - "field1": ValidationError(message=None, code="code1"), - "field2": ValidationError(message=None, code="code2"), - }) + exception = ValidationError( + { + "field1": ValidationError(message=None, code="code1"), + "field2": ValidationError(message=None, code="code2"), + } + ) assert contains_error_code(exception, "code2") def test_contains_error_code_returns_false_for_error_dict_without_matching_code(): - - exception = ValidationError({ - "field1": ValidationError(message=None, code="code1"), - "field2": ValidationError(message=None, code="code2"), - }) + exception = ValidationError( + { + "field1": ValidationError(message=None, code="code1"), + "field2": ValidationError(message=None, code="code2"), + } + ) assert not contains_error_code(exception, "code3") def test_contains_error_code_returns_true_for_multiple_errors_per_field(): - - exception = ValidationError({ - "field1": [ - ValidationError(message=None, code="code1"), - ValidationError(message=None, code="code2") - ], - "field2": [ValidationError(message=None, code="code3")], - }) + exception = ValidationError( + { + "field1": [ + ValidationError(message=None, code="code1"), + ValidationError(message=None, code="code2"), + ], + "field2": [ValidationError(message=None, code="code3")], + } + ) assert contains_error_code(exception, "code2") def test_contains_error_code_returns_false_for_multiple_errors_per_field_without_matching(): - - exception = ValidationError({ - "field1": [ - ValidationError(message=None, code="code1"), - ValidationError(message=None, code="code2") - ], - "field2": [ValidationError(message=None, code="code3")], - }) + exception = ValidationError( + { + "field1": [ + ValidationError(message=None, code="code1"), + ValidationError(message=None, code="code2"), + ], + "field2": [ValidationError(message=None, code="code3")], + } + ) assert not contains_error_code(exception, "code4") def test_contains_error_code_returns_true_for_list_of_errors(): - - exception = ValidationError([ - ValidationError(message=None, code="code1"), ValidationError(message=None, code="code2") - ]) + exception = ValidationError( + [ + ValidationError(message=None, code="code1"), + ValidationError(message=None, code="code2"), + ] + ) assert contains_error_code(exception, "code2") def test_contains_error_code_returns_false_for_list_of_errors_without_matching(): - - exception = ValidationError([ - ValidationError(message=None, code="code1"), ValidationError(message=None, code="code2") - ]) + exception = ValidationError( + [ + ValidationError(message=None, code="code1"), + ValidationError(message=None, code="code2"), + ] + ) assert not contains_error_code(exception, "code3") def test_extract_error_messages_results_in_single_element_for_single_message(): - exception = ValidationError(message="XXX") assert extract_error_messages(exception) == ["XXX"] def test_extract_error_messages_results_in_empty_list_when_no_message(): - exception = ValidationError(message=None) assert not extract_error_messages(exception) def test_extract_error_messages_finds_all_messages_in_dict(): - - exception = ValidationError({ - "field1": ValidationError(message="m1"), - "field2": ValidationError(message="m2"), - }) + exception = ValidationError( + { + "field1": ValidationError(message="m1"), + "field2": ValidationError(message="m2"), + } + ) assert extract_error_messages(exception) == ["m1", "m2"] def test_extract_error_messages_skips_undefined_messages_in_dict(): - - exception = ValidationError({ - "field1": ValidationError(message="m1"), - "field2": ValidationError(message=None), - }) + exception = ValidationError( + { + "field1": ValidationError(message="m1"), + "field2": ValidationError(message=None), + } + ) assert extract_error_messages(exception) == ["m1"] def test_extract_error_messages_finds_all_messages_in_dict_with_list(): - - exception = ValidationError({ - "field1": [ValidationError(message="m1"), ValidationError(message="m2")], - "field2": [ValidationError(message="m3")], - }) + exception = ValidationError( + { + "field1": [ValidationError(message="m1"), ValidationError(message="m2")], + "field2": [ValidationError(message="m3")], + } + ) assert extract_error_messages(exception) == ["m1", "m2", "m3"] diff --git a/app/tests/utils/test_fields.py b/app/tests/utils/test_fields.py index 16caa767..b55fca26 100644 --- a/app/tests/utils/test_fields.py +++ b/app/tests/utils/test_fields.py @@ -7,9 +7,9 @@ def test_custom_slug_field(): field = CustomSlugField() - valid = 'ch.astra.projektierungszonen-nationalstrassen_v2_0.oereb' + valid = "ch.astra.projektierungszonen-nationalstrassen_v2_0.oereb" assert field.clean(valid, None) == valid - for invalid in ('CH', '?', '!', 'ü'): + for invalid in ("CH", "?", "!", "ü"): with raises(ValidationError): field.clean(invalid, None) diff --git a/app/tests/utils/test_language.py b/app/tests/utils/test_language.py index a115b323..69b37f4b 100644 --- a/app/tests/utils/test_language.py +++ b/app/tests/utils/test_language.py @@ -5,35 +5,41 @@ def test_get_language_returns_value_of_query_param_if_defined(): - assert get_language( - lang_query_param="de", headers={"Accept-Language": "fr"} - ) == LanguageCode.GERMAN + assert ( + get_language(lang_query_param="de", headers={"Accept-Language": "fr"}) + == LanguageCode.GERMAN + ) def test_get_language_returns_value_of_header_if_query_param_undefined(): - assert get_language( - lang_query_param=None, headers={"Accept-Language": "fr"} - ) == LanguageCode.FRENCH + assert ( + get_language(lang_query_param=None, headers={"Accept-Language": "fr"}) + == LanguageCode.FRENCH + ) def test_get_language_returns_value_of_header_if_query_param_invalid_language(): - assert get_language( - lang_query_param="es", headers={"Accept-Language": "rm"} - ) == LanguageCode.ROMANSH + assert ( + get_language(lang_query_param="es", headers={"Accept-Language": "rm"}) + == LanguageCode.ROMANSH + ) def test_get_language_returns_default_if_header_empty(): - assert get_language( - lang_query_param=None, headers={"Accept-Language": ""} - ) == LanguageCode.ENGLISH + assert ( + get_language(lang_query_param=None, headers={"Accept-Language": ""}) + == LanguageCode.ENGLISH + ) def test_get_language_returns_default_if_language_header_absent(): - assert get_language(lang_query_param=None, headers={"test": ""}) == LanguageCode.ENGLISH + assert ( + get_language(lang_query_param=None, headers={"test": ""}) + == LanguageCode.ENGLISH + ) def test_get_translation_returns_correct_field_if_present(): - class TestClass: field1_de = None field1_en = "my field" @@ -47,45 +53,47 @@ class TestClass: def test_get_translation_returns_default_if_field_absent(): - class TestClass: field1_en = "my field" test = TestClass() - actual = get_translation(obj=test, field_name="field1", lang="de", default_lang="en") + actual = get_translation( + obj=test, field_name="field1", lang="de", default_lang="en" + ) assert actual == "my field" def test_get_translation_returns_default_if_field_empty(): - class TestClass: field1_de = "" field1_en = "my field" test = TestClass() - actual = get_translation(obj=test, field_name="field1", lang="de", default_lang="en") + actual = get_translation( + obj=test, field_name="field1", lang="de", default_lang="en" + ) assert actual == "my field" def test_get_translation_returns_default_if_field_none(): - class TestClass: field1_de = None field1_en = "my field" test = TestClass() - actual = get_translation(obj=test, field_name="field1", lang="de", default_lang="en") + actual = get_translation( + obj=test, field_name="field1", lang="de", default_lang="en" + ) assert actual == "my field" def test_get_translation_raises_exception_if_even_default_field_absent(): - class TestClass: field1_it = None @@ -96,7 +104,6 @@ class TestClass: def test_get_translation_raises_exception_if_even_default_field_empty(): - class TestClass: field1_en = "" @@ -107,7 +114,6 @@ class TestClass: def test_get_translation_raises_exception_if_even_default_field_none(): - class TestClass: field1_en = None diff --git a/app/tests/utils/test_short_id.py b/app/tests/utils/test_short_id.py index a4c758eb..bd6d58e1 100644 --- a/app/tests/utils/test_short_id.py +++ b/app/tests/utils/test_short_id.py @@ -11,6 +11,6 @@ def test_generate_short_id_default(): def test_generate_short_id_settings(settings): settings.SHORT_ID_SIZE = 4 - settings.SHORT_ID_ALPHABET = 'a' + settings.SHORT_ID_ALPHABET = "a" short_id = generate_short_id() - assert short_id == 'aaaa' + assert short_id == "aaaa" diff --git a/app/utils/authentication.py b/app/utils/authentication.py index 57c17979..a8b9bf6e 100644 --- a/app/utils/authentication.py +++ b/app/utils/authentication.py @@ -7,7 +7,7 @@ class PermissionAuth(SessionAuth): - """ Ninja authentication that extends the session authentication with permission checks. """ + """Ninja authentication that extends the session authentication with permission checks.""" def __init__(self, permission: str, csrf: bool = True) -> None: super().__init__(csrf) diff --git a/app/utils/command.py b/app/utils/command.py index 80cb86d3..38c5dc5a 100644 --- a/app/utils/command.py +++ b/app/utils/command.py @@ -10,31 +10,34 @@ class CustomBaseCommand(BaseCommand): - def handle(self, *args: Any, **options: Any) -> None: """ The actual logic of the command. Subclasses must implement this method. """ - raise NotImplementedError("subclasses of CustomBaseCommand must provide a handle() method") + raise NotImplementedError( + "subclasses of CustomBaseCommand must provide a handle() method" + ) def add_arguments(self, parser: CommandParser) -> None: - parser.add_argument('--logger', action='store_true', help='use logger configuration') + parser.add_argument( + "--logger", action="store_true", help="use logger configuration" + ) -class CommandHandler(): +class CommandHandler: """Base class for management command handler This class add proper support for printing to the console for management command """ - def __init__(self, command: CustomBaseCommand, options: dict['str', Any]): + def __init__(self, command: CustomBaseCommand, options: dict["str", Any]): frm = inspect.stack()[1] mod = inspect.getmodule(frm[0]) self.logger = logging.getLogger(mod.__name__ if mod else None) self.options = options - self.verbosity = options['verbosity'] - self.use_logger = options.get('logger') + self.verbosity = options["verbosity"] + self.use_logger = options.get("logger") self.stdout = command.stdout self.stderr = command.stderr self.style = command.style @@ -46,30 +49,40 @@ def print(self, message: str, *args: Any, level: int = 2, **kwargs: Any) -> None self.logger.info(message, *args, **kwargs) else: if len(kwargs) > 0: - message = message + " " + ", ".join( - f"{key}={value}" for key, value in kwargs.items() + message = ( + message + + " " + + ", ".join(f"{key}={value}" for key, value in kwargs.items()) ) self.stdout.write(message % (args)) - def print_warning(self, message: str, *args: Any, level: int = 1, **kwargs: Any) -> None: + def print_warning( + self, message: str, *args: Any, level: int = 1, **kwargs: Any + ) -> None: if self.verbosity >= level: if self.use_logger: self.logger.warning(self.style.WARNING(message % (args)), **kwargs) else: if len(kwargs) > 0: - message = message + " " + ", ".join( - f"{key}={value}" for key, value in kwargs.items() + message = ( + message + + " " + + ", ".join(f"{key}={value}" for key, value in kwargs.items()) ) self.stdout.write(self.style.WARNING(message % (args))) - def print_success(self, message: str, *args: Any, level: int = 1, **kwargs: Any) -> None: + def print_success( + self, message: str, *args: Any, level: int = 1, **kwargs: Any + ) -> None: if self.verbosity >= level: if self.use_logger: self.logger.info(self.style.SUCCESS(message % (args)), **kwargs) else: if len(kwargs) > 0: - message = message + " " + ", ".join( - f"{key}={value}" for key, value in kwargs.items() + message = ( + message + + " " + + ", ".join(f"{key}={value}" for key, value in kwargs.items()) ) self.stdout.write(self.style.SUCCESS(message % (args))) @@ -78,7 +91,9 @@ def print_error(self, message: str, *args: Any, **kwargs: Any) -> None: self.logger.error(self.style.ERROR(message % (args)), **kwargs) else: if len(kwargs) > 0: - message = message + "\n" + ", ".join( - f"{key}={value}" for key, value in kwargs.items() + message = ( + message + + "\n" + + ", ".join(f"{key}={value}" for key, value in kwargs.items()) ) self.stderr.write(self.style.ERROR(message % (args))) diff --git a/app/utils/database_router.py b/app/utils/database_router.py index 1cd73d66..7aca608a 100644 --- a/app/utils/database_router.py +++ b/app/utils/database_router.py @@ -13,36 +13,36 @@ class CustomRouter: """ def db_for_read(self, model: Model, **hints: Any) -> str | None: - """ Use BOD for reading BOD models. """ + """Use BOD for reading BOD models.""" if settings.TESTING: return None - if model._meta.app_label == 'bod': - return 'bod' + if model._meta.app_label == "bod": + return "bod" return None def db_for_write(self, model: Model, **hints: Any) -> str | None: - """ Use BOD for writing BOD models during tests. """ + """Use BOD for writing BOD models during tests.""" if settings.TESTING: return None - if model._meta.app_label == 'bod': - raise RuntimeError('Writing to the BOD not supported') + if model._meta.app_label == "bod": + raise RuntimeError("Writing to the BOD not supported") return None def allow_migrate( self, db: Any, app_label: str, model_name: Any = None, **hints: Any ) -> bool | None: - """ Allow BOD migrations only during tests. """ + """Allow BOD migrations only during tests.""" if settings.TESTING: return None - if app_label == 'bod': + if app_label == "bod": return False return None diff --git a/app/utils/language.py b/app/utils/language.py index 042525bd..7a843ca6 100644 --- a/app/utils/language.py +++ b/app/utils/language.py @@ -11,6 +11,7 @@ class LanguageCode(StrEnum): """ Two-letter language codes """ + GERMAN = "de" FRENCH = "fr" ITALIAN = "it" @@ -44,7 +45,7 @@ def get_translation( obj: Any, field_name: str, lang: LanguageCode, - default_lang: LanguageCode = DEFAULT_LANGUAGE + default_lang: LanguageCode = DEFAULT_LANGUAGE, ) -> str: """ Return the field `obj.{field_name}_{lang}` as a string if it has a value. diff --git a/app/utils/short_id.py b/app/utils/short_id.py index 2c8a6dc5..69305990 100644 --- a/app/utils/short_id.py +++ b/app/utils/short_id.py @@ -4,7 +4,7 @@ def generate_short_id() -> str: - """ Generate a Short ID + """Generate a Short ID By default, a short ID consists of 12 lowercase letters or digits. This format is identical to the one used in the service-shortlink. diff --git a/app/utils/testing.py b/app/utils/testing.py index 7af20937..af1e0d15 100644 --- a/app/utils/testing.py +++ b/app/utils/testing.py @@ -11,6 +11,8 @@ def create_user_with_permissions( user = get_user_model().objects.create_user(username=username, password=password) for app_label, model, codename in permissions: content_type = ContentType.objects.get(app_label=app_label, model=model) - permission = Permission.objects.get(content_type=content_type, codename=codename) + permission = Permission.objects.get( + content_type=content_type, codename=codename + ) user.user_permissions.add(permission) return user diff --git a/app/wsgi.py b/app/wsgi.py index cc9105ef..88d9feb3 100755 --- a/app/wsgi.py +++ b/app/wsgi.py @@ -16,9 +16,11 @@ isort:skip_file """ + # pylint: disable=wrong-import-position -if __name__ == '__main__': +if __name__ == "__main__": import gevent.monkey + gevent.monkey.patch_all() """ WSGI config for project project. @@ -39,15 +41,16 @@ from config.settings_prod import get_logging_config # default to the setting that's being created in DOCKERFILE -os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings') +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings") application = get_wsgi_application() class StandaloneApplication(BaseApplication): # pylint: disable=abstract-method - cfg: Config - def __init__(self, app: WSGIHandler, options: dict[str, object] | None = None) -> None: # pylint: disable=redefined-outer-name + def __init__( + self, app: WSGIHandler, options: dict[str, object] | None = None + ) -> None: # pylint: disable=redefined-outer-name self.options = options or {} self.application = app super().__init__() @@ -66,16 +69,17 @@ def load(self) -> WSGIHandler: # We use the port 5000 as default, otherwise we set the HTTP_PORT env variable within the container. -if __name__ == '__main__': - HTTP_PORT = str(os.environ.get('HTTP_PORT', "8000")) +if __name__ == "__main__": + HTTP_PORT = str(os.environ.get("HTTP_PORT", "8000")) # Bind to 0.0.0.0 to let your app listen to all network interfaces. options = { - 'bind': f"{'0.0.0.0'}:{HTTP_PORT}", # nosec B104 - 'worker_class': 'gevent', - 'workers': int(os.environ.get('GUNICORN_WORKERS', - '2')), # scaling horizontally is left to Kubernetes - 'worker_tmp_dir': os.environ.get('GUNICORN_WORKER_TMP_DIR', None), - 'timeout': 60, - 'logconfig_dict': get_logging_config() + "bind": f"{'0.0.0.0'}:{HTTP_PORT}", # nosec B104 + "worker_class": "gevent", + "workers": int( + os.environ.get("GUNICORN_WORKERS", "2") + ), # scaling horizontally is left to Kubernetes + "worker_tmp_dir": os.environ.get("GUNICORN_WORKER_TMP_DIR", None), + "timeout": 60, + "logconfig_dict": get_logging_config(), } StandaloneApplication(application, options).run() # type:ignore[no-untyped-call]