Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • arkindex/backend
1 result
Show changes
Commits on Source (3)
......@@ -11,7 +11,7 @@ include:
# For jobs that run backend scripts directly
.backend-setup:
image: registry.gitlab.com/teklia/arkindex/backend/base:django-4.1.4
image: registry.gitlab.com/teklia/arkindex/backend/base:django-4.1.5
cache:
paths:
......
FROM registry.gitlab.com/teklia/arkindex/backend/base:django-4.1.4 as build
FROM registry.gitlab.com/teklia/arkindex/backend/base:django-4.1.5 as build
RUN mkdir build
ADD . build
RUN cd build && python3 setup.py sdist
FROM registry.gitlab.com/teklia/arkindex/backend/base:django-4.1.4
FROM registry.gitlab.com/teklia/arkindex/backend/base:django-4.1.5
ARG PONOS_BRANCH=master
ARG PONOS_ID=10017043
ARG TRANSKRIBUS_BRANCH=master
......
......@@ -60,7 +60,7 @@ RUN python -m nuitka \
arkindex/manage.py
# Start over from a clean setup
FROM registry.gitlab.com/teklia/arkindex/backend/base:django-4.1.4 as build
FROM registry.gitlab.com/teklia/arkindex/backend/base:django-4.1.5 as build
# Import files from compilation
RUN mkdir /usr/share/arkindex
......
......@@ -424,7 +424,10 @@ class ClassificationCreate(CreateAPIView):
@extend_schema(tags=['classifications'])
class ClassificationBulk(CreateAPIView):
"""
Create multiple classifications at once on the same element with the same classifier.
Create multiple classifications at once on the same element.
Exactly one of `worker_version` or `worker_run_id` fields must be set.
If `worker_run_id` is set, the worker version will be deduced from it.
"""
serializer_class = ClassificationsSerializer
permission_classes = (IsVerified, )
......
......@@ -653,6 +653,14 @@ class ClassificationsSerializer(serializers.Serializer):
if len(ml_class_ids) != len(set(ml_class_ids)):
errors['classifications'].append('Duplicated ML classes are not allowed from the same worker version or worker run.')
worker_run = data.get('worker_run', None)
if not worker_run and not data.get('worker_version'):
errors['non_field_errors'] = [
'Exactly one of `worker_version` or `worker_run_id` must be set.'
]
elif worker_run:
data['worker_version'] = WorkerVersion(id=worker_run.version_id)
# Check that all ML classes exist in one query
ml_class_count = (
MLClass
......
......@@ -114,7 +114,31 @@ class TestBulkClassification(FixtureAPITestCase):
],
)
def test_worker_version_xor_worker_run(self):
def test_worker_version_or_worker_run(self):
"""Either a worker run or a worker version is required"""
self.client.force_login(self.user)
with self.assertNumQueries(6):
response = self.client.post(
reverse('api:classification-bulk'),
format='json',
data={
"parent": str(self.page.id),
"classifications": [
{
'ml_class': str(self.cat_class.id),
"confidence": 0.42,
}
]
}
)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertDictEqual(response.json(), {
'non_field_errors': ['Exactly one of `worker_version` or `worker_run_id` must be set.'],
})
def test_worker_version_and_worker_run(self):
"""Worker run and worker version cannot be set at the same time"""
self.client.force_login(self.user)
with self.assertNumQueries(7):
response = self.client.post(
......@@ -162,21 +186,21 @@ class TestBulkClassification(FixtureAPITestCase):
)
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
first_cl, second_cl = self.page.classifications.order_by('-confidence').values_list('id', flat=True)
first_cl, second_cl = self.page.classifications.order_by('-confidence').all()
self.assertEqual(response.json(), {
'parent': str(self.page.id),
'worker_version': None,
'worker_version': str(self.worker_version.id),
'worker_run_id': str(self.worker_run.id),
'classifications': [
{
'id': str(first_cl),
'id': str(first_cl.id),
'ml_class': str(self.dog_class.id),
'confidence': 0.99,
'high_confidence': True,
'state': 'pending',
},
{
'id': str(second_cl),
'id': str(second_cl.id),
'ml_class': str(self.cat_class.id),
'confidence': 0.42,
'high_confidence': False,
......@@ -198,6 +222,11 @@ class TestBulkClassification(FixtureAPITestCase):
('cat', 0.42, False, self.worker_version.id, self.worker_run.id),
],
)
# Worker run is set, and worker version is deduced from it
self.assertEqual(first_cl.worker_version, self.worker_version)
self.assertEqual(second_cl.worker_version, self.worker_version)
self.assertEqual(first_cl.worker_run, self.worker_run)
self.assertEqual(second_cl.worker_run, self.worker_run)
def test_worker_run_not_found(self):
self.client.force_login(self.user)
......
......@@ -94,6 +94,10 @@ class UserConfigurationFieldSerializer(serializers.Serializer):
UserConfigurationFieldType.List: [serializers.ListField(), list]
}
if not isinstance(data, dict):
errors['__all__'] = [f'User configuration field definitions should be of type dict, not {type(data).__name__}.']
raise ValidationError(errors)
for field in data:
if field not in allowed_fields:
errors[field].append(
......
......@@ -1013,6 +1013,29 @@ class TestWorkersWorkerVersions(FixtureAPITestCase):
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertEqual(response.json(), {"configuration": {"user_configuration": [["Expected a dictionary of items but got type \"str\"."]]}})
def test_invalid_user_configuration_item_not_dict(self):
self.client.force_login(self.internal_user)
response = self.client.post(
reverse("api:worker-versions", kwargs={"pk": str(self.worker_2.id)}),
data={
"revision_id": str(self.rev2.id),
"configuration": {
"user_configuration": {
"secrets": ["aaaaaa"]
}
},
"gpu_usage": "disabled",
},
format="json",
)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertEqual(response.json(), {"configuration": {
"user_configuration": [{
"secrets":
{'__all__': ['User configuration field definitions should be of type dict, not list.']}
}]
}})
def test_invalid_user_configuration_wrong_field_type(self):
self.client.force_login(self.internal_user)
response = self.client.post(
......
boto3==1.18.13
cryptography==3.4.7
Django==4.1.4
Django==4.1.5
lxml==4.6.3
psycopg2-binary==2.9.1