diff --git a/arkindex/process/serializers/imports.py b/arkindex/process/serializers/imports.py index 2c659da43c4d91e865e0bd9bdea8d21f933ecdd4..2e01179db67c303ef789998bd1e7ece990400e3e 100644 --- a/arkindex/process/serializers/imports.py +++ b/arkindex/process/serializers/imports.py @@ -18,6 +18,7 @@ from arkindex.process.models import ( WorkerRun, WorkerVersionState, ) +from arkindex.process.utils import get_default_farm from arkindex.project.mixins import ProcessACLMixin from arkindex.project.serializer_fields import EnumField, LinearRingField from arkindex.project.validators import MaxValueValidator @@ -26,7 +27,6 @@ from arkindex.users.models import Role from arkindex.users.utils import get_max_level ProcessFarmField = import_string(getattr(settings, "PROCESS_FARM_FIELD", None) or "arkindex.project.serializer_fields.NullField") -get_default_farm = import_string(getattr(settings, "GET_DEFAULT_FARM", None) or "arkindex.process.utils.get_default_farm") class ProcessLightSerializer(serializers.ModelSerializer): diff --git a/arkindex/process/tests/test_create_s3_import.py b/arkindex/process/tests/test_create_s3_import.py index 5d95c18a68f80b10aa3c474f4efda981e03dde23..fcd875b64d0180f88287eff07514247590036c26 100644 --- a/arkindex/process/tests/test_create_s3_import.py +++ b/arkindex/process/tests/test_create_s3_import.py @@ -1,4 +1,3 @@ -from unittest import expectedFailure from unittest.mock import call, patch from django.test import override_settings @@ -19,8 +18,6 @@ class TestCreateS3Import(FixtureTestCase): def setUpTestData(cls): super().setUpTestData() cls.import_worker_version = WorkerVersion.objects.get(worker__slug="file_import") - cls.default_farm = Farm.objects.create(name="Crypto farm") - cls.default_farm.memberships.create(user=cls.user, level=Role.Guest.value) def test_requires_login(self): with self.assertNumQueries(0): @@ -138,8 +135,9 @@ class TestCreateS3Import(FixtureTestCase): self.client.force_login(self.user) ImageServer.objects.create(id=999, display_name="Ingest image server", url="https://dev.null.teklia.com") element = self.corpus.elements.get(name="Volume 1") + farm = Farm.objects.create(name="Crypto farm") - with self.assertNumQueries(22), self.settings(IMPORTS_WORKER_VERSION=str(self.import_worker_version.id)): + with self.assertNumQueries(23), self.settings(IMPORTS_WORKER_VERSION=str(self.import_worker_version.id)): response = self.client.post(reverse("api:s3-import-create"), { "corpus_id": str(self.corpus.id), "element_id": str(element.id), @@ -147,6 +145,7 @@ class TestCreateS3Import(FixtureTestCase): "element_type": "page", "bucket_name": "blah", "prefix": "a/b/c", + "farm_id": str(farm.id), }) self.assertEqual(response.status_code, status.HTTP_201_CREATED, response.json()) data = response.json() @@ -161,6 +160,7 @@ class TestCreateS3Import(FixtureTestCase): self.assertEqual(process.element_type, self.corpus.types.get(slug="page")) self.assertEqual(process.bucket_name, "blah") self.assertEqual(process.prefix, "a/b/c") + self.assertEqual(process.farm, farm) worker_run = process.worker_runs.get() self.assertEqual(worker_run.version, self.import_worker_version) @@ -245,19 +245,21 @@ class TestCreateS3Import(FixtureTestCase): "INGEST_S3_SECRET_KEY": "its-secret-i-wont-tell-you", }) - @expectedFailure @override_settings(INGEST_IMAGESERVER_ID=999) - @patch("arkindex.users.utils.get_max_level", return_value=None) - def test_farm_guest(self, get_max_level_mock): + @patch("arkindex.ponos.models.Farm.is_available", return_value=False) + def test_farm_guest(self, is_available_mock): self.user.user_scopes.create(scope=Scope.S3Ingest) self.client.force_login(self.user) self.corpus.types.create(slug="folder", display_name="Folder", folder=True) ImageServer.objects.create(id=999, display_name="Ingest image server", url="https://dev.null.teklia.com") + farm = Farm.objects.create(name="Crypto farm") + with self.assertNumQueries(5), self.settings(IMPORTS_WORKER_VERSION=str(self.import_worker_version.id)): response = self.client.post(reverse("api:s3-import-create"), { "corpus_id": str(self.corpus.id), "bucket_name": "blah", + "farm_id": str(farm.id), }) self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) @@ -266,20 +268,43 @@ class TestCreateS3Import(FixtureTestCase): }) self.assertFalse(Process.objects.filter(mode=ProcessMode.S3).exists()) - self.assertEqual(get_max_level_mock.call_count, 1) + self.assertEqual(is_available_mock.call_count, 1) + self.assertEqual(is_available_mock.call_args, call(self.user)) - @expectedFailure @override_settings(INGEST_IMAGESERVER_ID=999) - @patch("arkindex.users.utils.get_max_level", return_value=None) - def test_default_farm_guest(self, get_max_level_mock): + @patch("arkindex.process.serializers.ingest.get_default_farm") + def test_default_farm(self, get_default_farm_mock): self.user.user_scopes.create(scope=Scope.S3Ingest) self.client.force_login(self.user) self.corpus.types.create(slug="folder", display_name="Folder", folder=True) ImageServer.objects.create(id=999, display_name="Ingest image server", url="https://dev.null.teklia.com") - self.default_farm.memberships.filter(user=self.user).delete() + default_farm = Farm.objects.create(name="Crypto farm") + get_default_farm_mock.return_value = default_farm - with self.assertNumQueries(5), self.settings(IMPORTS_WORKER_VERSION=str(self.import_worker_version.id)): + with self.assertNumQueries(21), self.settings(IMPORTS_WORKER_VERSION=str(self.import_worker_version.id)): + response = self.client.post(reverse("api:s3-import-create"), { + "corpus_id": str(self.corpus.id), + "bucket_name": "blah", + }) + self.assertEqual(response.status_code, status.HTTP_201_CREATED) + + process = Process.objects.get(id=response.json()["id"]) + self.assertEqual(process.farm, default_farm) + + @override_settings(INGEST_IMAGESERVER_ID=999) + @patch("arkindex.ponos.models.Farm.is_available", return_value=False) + @patch("arkindex.process.serializers.ingest.get_default_farm") + def test_default_farm_guest(self, get_default_farm_mock, is_available_mock): + self.user.user_scopes.create(scope=Scope.S3Ingest) + self.client.force_login(self.user) + self.corpus.types.create(slug="folder", display_name="Folder", folder=True) + ImageServer.objects.create(id=999, display_name="Ingest image server", url="https://dev.null.teklia.com") + + default_farm = Farm.objects.create(name="Crypto farm") + get_default_farm_mock.return_value = default_farm + + with self.assertNumQueries(4), self.settings(IMPORTS_WORKER_VERSION=str(self.import_worker_version.id)): response = self.client.post(reverse("api:s3-import-create"), { "corpus_id": str(self.corpus.id), "bucket_name": "blah", @@ -291,5 +316,5 @@ class TestCreateS3Import(FixtureTestCase): }) self.assertFalse(Process.objects.filter(mode=ProcessMode.S3).exists()) - self.assertEqual(get_max_level_mock.call_count, 1) - self.assertEqual(get_max_level_mock.call_args, call(self.user, self.default_farm)) + self.assertEqual(is_available_mock.call_count, 1) + self.assertEqual(is_available_mock.call_args, call(self.user)) diff --git a/arkindex/process/tests/test_processes.py b/arkindex/process/tests/test_processes.py index f216464246d01b1186545f790a11a2cbfb39f3c0..5853582db3ed547b25e165dc7db181f6be4bd988 100644 --- a/arkindex/process/tests/test_processes.py +++ b/arkindex/process/tests/test_processes.py @@ -2076,6 +2076,28 @@ class TestProcesses(FixtureAPITestCase): process = Process.objects.get(id=data["id"]) self.assertEqual(process.farm, farm) + @patch("arkindex.process.serializers.imports.get_default_farm") + def test_from_files_default_farm(self, get_default_farm_mock): + farm = Farm.objects.get(name="Wheat farm") + get_default_farm_mock.return_value = farm + self.client.force_login(self.user) + self.assertEqual(self.version_with_model.worker_runs.count(), 0) + + with ( + self.settings(IMPORTS_WORKER_VERSION=str(self.version_with_model.id)), + self.assertNumQueries(25), + ): + response = self.client.post(reverse("api:files-process"), { + "files": [str(self.img_df.id)], + "folder_type": "volume", + "element_type": "page", + }, format="json") + self.assertEqual(response.status_code, status.HTTP_201_CREATED) + + data = response.json() + process = Process.objects.get(id=data["id"]) + self.assertEqual(process.farm, farm) + @patch("arkindex.ponos.models.Farm.is_available", return_value=False) def test_from_files_farm_guest(self, is_available_mock): self.client.force_login(self.user) @@ -2472,6 +2494,28 @@ class TestProcesses(FixtureAPITestCase): self.assertEqual(workers_process.state, State.Unscheduled) self.assertEqual(workers_process.farm_id, farm.id) + @patch("arkindex.process.serializers.imports.get_default_farm") + def test_start_process_default_farm(self, get_default_farm_mock): + farm = Farm.objects.get(name="Wheat farm") + get_default_farm_mock.return_value = farm + + process2 = self.corpus.processes.create(creator=self.user, mode=ProcessMode.Workers) + process2.worker_runs.create(version=self.recognizer, parents=[], configuration=None) + self.assertFalse(process2.tasks.exists()) + + self.client.force_login(self.user) + + with self.assertNumQueries(15): + response = self.client.post( + reverse("api:process-start", kwargs={"pk": str(process2.id)}) + ) + self.assertEqual(response.status_code, status.HTTP_201_CREATED) + + self.assertEqual(response.json()["id"], str(process2.id)) + process2.refresh_from_db() + self.assertEqual(process2.state, State.Unscheduled) + self.assertEqual(process2.farm, farm) + @patch("arkindex.ponos.models.Farm.is_available", return_value=False) @patch("arkindex.process.serializers.imports.get_default_farm") def test_start_process_default_farm_guest(self, get_default_farm_mock, is_available_mock): diff --git a/arkindex/process/utils.py b/arkindex/process/utils.py index 6928d65b8c42a0b0738be8a8ed0c29e4ccdf3485..ef58ba9c33eff9aa2d6be4912a4c6cea74013a87 100644 --- a/arkindex/process/utils.py +++ b/arkindex/process/utils.py @@ -1,16 +1,18 @@ import json from hashlib import md5 +from django.conf import settings from django.db.models import CharField, Value from django.db.models.functions import Cast, Concat, NullIf +from django.utils.module_loading import import_string from arkindex.project.tools import RTrimChr -__default_farm = None - - -def get_default_farm(): - return None +get_default_farm = ( + import_string(settings.GET_DEFAULT_FARM) + if getattr(settings, "GET_DEFAULT_FARM", None) + else lambda: None +) def hash_object(object): diff --git a/arkindex/project/tests/__init__.py b/arkindex/project/tests/__init__.py index c918434474b9401aa9157596ee952a5c80d44ea3..cca5e05eb7ad2ffcf75194ba50cda1c6c01183cb 100644 --- a/arkindex/project/tests/__init__.py +++ b/arkindex/project/tests/__init__.py @@ -116,11 +116,6 @@ class FixtureMixin(object): # Clean content type cache for SQL requests checks consistency ContentType.objects.clear_cache() - # Force clean the default farm global variable in `arkindex.process.utils` module - # This is required not to alter query counts and avoid caching a farm that does not exist in the fixture - from arkindex.process import utils - setattr(utils, "__default_farm", None) - # Clear the local cached properties so that it is re-fetched on each test # to avoid intermittently changing query counts. # Using `del` on a cached property that has not been accessed yet can cause an AttributeError.