From d69440cc525c5120eaa822d984f0a315da5ed246 Mon Sep 17 00:00:00 2001 From: Erwan Rouchet <rouchet@teklia.com> Date: Fri, 27 Nov 2020 17:21:00 +0100 Subject: [PATCH] Drop DataSource --- README.md | 1 - .../dataimport/tests/test_process_elements.py | 38 +- arkindex/documents/admin.py | 11 +- arkindex/documents/api/elements.py | 5 +- arkindex/documents/api/ml.py | 138 +- arkindex/documents/api/search.py | 2 +- arkindex/documents/fixtures/data.json | 1445 ++++++++--------- .../management/commands/build_fixtures.py | 113 +- .../migrations/0024_drop_datasource.py | 64 + arkindex/documents/models.py | 59 +- arkindex/documents/search.py | 4 +- arkindex/documents/serializers/elements.py | 5 +- arkindex/documents/serializers/entities.py | 4 - arkindex/documents/serializers/ml.py | 49 +- arkindex/documents/tasks.py | 78 - .../documents/tests/commands/test_reindex.py | 7 +- .../tests/tasks/test_corpus_delete.py | 25 +- .../tests/tasks/test_ml_results_delete.py | 169 -- .../documents/tests/tasks/test_reindex.py | 13 +- .../tests/test_bulk_classification.py | 21 +- .../tests/test_bulk_element_transcriptions.py | 12 +- arkindex/documents/tests/test_classes.py | 71 +- .../documents/tests/test_create_elements.py | 4 - .../tests/test_create_transcriptions.py | 6 +- arkindex/documents/tests/test_datasource.py | 17 - .../tests/test_edit_transcriptions.py | 32 +- arkindex/documents/tests/test_entities.py | 14 +- arkindex/documents/tests/test_entities_api.py | 136 +- arkindex/documents/tests/test_indexer.py | 10 +- arkindex/documents/tests/test_manifest.py | 10 +- arkindex/documents/tests/test_metadata.py | 36 +- arkindex/documents/tests/test_ml_results.py | 225 --- arkindex/documents/tests/test_moderation.py | 159 +- .../documents/tests/test_parents_elements.py | 4 +- .../documents/tests/test_patch_elements.py | 4 +- .../documents/tests/test_retrieve_elements.py | 21 +- arkindex/documents/tests/test_search.py | 9 +- .../documents/tests/test_transcriptions.py | 29 +- arkindex/project/api_v1.py | 4 - arkindex/project/openapi/patch.yml | 10 - arkindex/project/tests/test_elastic.py | 5 +- arkindex/project/triggers.py | 40 - .../sql_validation/element_trash_children.sql | 3 - .../sql_validation/element_trash_deep.sql | 5 - .../sql_validation/element_trash_ml_class.sql | 4 +- .../element_trash_no_children.sql | 1 - 46 files changed, 1038 insertions(+), 2084 deletions(-) create mode 100644 arkindex/documents/migrations/0024_drop_datasource.py delete mode 100644 arkindex/documents/tests/tasks/test_ml_results_delete.py delete mode 100644 arkindex/documents/tests/test_datasource.py delete mode 100644 arkindex/documents/tests/test_ml_results.py diff --git a/README.md b/README.md index 6173d8b887..715d73f69d 100644 --- a/README.md +++ b/README.md @@ -177,7 +177,6 @@ You may want to also uninstall `django-nose`, as it is an optional test runner t We use [rq](https://python-rq.org/), integrated via [django-rq](https://pypi.org/project/django-rq/), to run tasks without blocking an API request or causing timeouts. To call them in Python code, you should use the trigger methods in `arkindex.project.triggers`; those will do some safety checks to make catching some errors easier in dev. The actual tasks are in `arkindex.documents.tasks`. The following tasks exist: -* Delete ML results from a corpus or an element and its children: `ml_results_delete` * Delete a corpus: `corpus_delete` * Reindex elements, transcriptions or entities into ElasticSearch: `reindex_start` diff --git a/arkindex/dataimport/tests/test_process_elements.py b/arkindex/dataimport/tests/test_process_elements.py index c0bc419ebe..bfc367349c 100644 --- a/arkindex/dataimport/tests/test_process_elements.py +++ b/arkindex/dataimport/tests/test_process_elements.py @@ -3,8 +3,8 @@ import uuid from django.urls import reverse from rest_framework import status -from arkindex.dataimport.models import DataImport, DataImportMode -from arkindex.documents.models import Classification, ClassificationState, Corpus, DataSource, Element, MLClass +from arkindex.dataimport.models import DataImport, DataImportMode, WorkerVersion +from arkindex.documents.models import Classification, ClassificationState, Corpus, Element, MLClass from arkindex.project.tests import FixtureAPITestCase @@ -127,44 +127,44 @@ class TestProcessElements(FixtureAPITestCase): cls.line_5.add_parent(cls.page_5) # Create best classes - source = DataSource.objects.first() - cls.coffee_source = MLClass.objects.create(name='C0FFEE', corpus=cls.private_corpus) - cls.food_source = MLClass.objects.create(name='F00D', corpus=cls.private_corpus) + worker_version = WorkerVersion.objects.get(worker__slug='reco') + cls.coffee_class = MLClass.objects.create(name='C0FFEE', corpus=cls.private_corpus) + cls.food_class = MLClass.objects.create(name='F00D', corpus=cls.private_corpus) Classification.objects.create( element=cls.folder_2, state=ClassificationState.Validated, - ml_class=cls.food_source, - source=source + ml_class=cls.food_class, + worker_version=worker_version, ) Classification.objects.create( element=cls.page_1, state=ClassificationState.Validated, - ml_class=cls.coffee_source, - source=source + ml_class=cls.coffee_class, + worker_version=worker_version, ) Classification.objects.create( element=cls.page_2, high_confidence=True, - ml_class=cls.food_source, - source=source + ml_class=cls.food_class, + worker_version=worker_version, ) Classification.objects.create( element=cls.page_3, state=ClassificationState.Validated, - ml_class=cls.food_source, - source=source + ml_class=cls.food_class, + worker_version=worker_version, ) Classification.objects.create( element=cls.page_5, high_confidence=True, - ml_class=cls.food_source, - source=source + ml_class=cls.food_class, + worker_version=worker_version, ) Classification.objects.create( element=cls.page_5, state=ClassificationState.Validated, - ml_class=cls.coffee_source , - source=source + ml_class=cls.coffee_class, + worker_version=worker_version, ) def setUp(self): @@ -323,7 +323,7 @@ class TestProcessElements(FixtureAPITestCase): ]) def test_filter_best_class_by_id(self): - self.dataimport.best_class = self.food_source.id + self.dataimport.best_class = self.food_class.id self.dataimport.save() elements = [self.page_5, self.page_3, self.folder_2, self.page_2] @@ -472,7 +472,7 @@ class TestProcessElements(FixtureAPITestCase): ]) def test_load_children_and_filter_best_class_by_id(self): - self.dataimport.best_class = self.food_source.id + self.dataimport.best_class = self.food_class.id self.dataimport.load_children = True self.dataimport.save() elements = [self.folder_2, self.page_2, self.page_3, self.page_5] diff --git a/arkindex/documents/admin.py b/arkindex/documents/admin.py index 3286682ba9..d4aa5ebdf0 100644 --- a/arkindex/documents/admin.py +++ b/arkindex/documents/admin.py @@ -7,7 +7,6 @@ from arkindex.documents.models import ( AllowedMetaData, Classification, Corpus, - DataSource, Element, ElementType, Entity, @@ -34,12 +33,6 @@ class CorpusAdmin(admin.ModelAdmin): return False -class DataSourceAdmin(admin.ModelAdmin): - list_display = ('id', 'type', 'slug', 'revision', 'internal') - list_filter = [('type', EnumFieldListFilter), 'internal'] - readonly_fields = ('id', ) - - class ClassificationInline(admin.TabularInline): model = Classification readonly_fields = ('confidence', 'high_confidence', ) @@ -85,8 +78,7 @@ class ElementAdmin(admin.ModelAdmin): class TranscriptionAdmin(admin.ModelAdmin): list_display = ('id', 'text', 'score', 'element', ) - list_filter = ['source'] - fields = ('id', 'text', 'score', 'element', 'source', ) + fields = ('id', 'text', 'score', 'element', ) readonly_fields = ('id', ) raw_id_fields = ('element', ) @@ -125,7 +117,6 @@ class EntityRoleAdmin(admin.ModelAdmin): admin.site.register(Corpus, CorpusAdmin) -admin.site.register(DataSource, DataSourceAdmin) admin.site.register(Element, ElementAdmin) admin.site.register(Transcription, TranscriptionAdmin) admin.site.register(MLClass, MLClassAdmin) diff --git a/arkindex/documents/api/elements.py b/arkindex/documents/api/elements.py index 77453969b0..ee89125b9c 100644 --- a/arkindex/documents/api/elements.py +++ b/arkindex/documents/api/elements.py @@ -59,7 +59,7 @@ from arkindex.project.permissions import IsAuthenticated, IsVerified, IsVerified from arkindex.project.tools import BulkMap from arkindex.project.triggers import corpus_delete, element_trash -classifications_queryset = Classification.objects.select_related('ml_class', 'source').order_by('-confidence') +classifications_queryset = Classification.objects.select_related('ml_class', 'worker_version').order_by('-confidence') best_classifications_prefetch = Prefetch( 'classifications', @@ -885,8 +885,9 @@ class ElementTranscriptions(ListAPIView): self.check_object_permissions(self.request, element) # ORDER BY casting IDs as char to avoid the PostgreSQL optimizer's inefficient scan + # TODO: See if select_related is faster than a prefetch on this endpoint queryset = Transcription.objects \ - .prefetch_related('element__zone__image__server', 'source') \ + .prefetch_related('element__zone__image__server', 'worker_version') \ .annotate(char_id=Cast('id', output_field=CharField())) \ .order_by('char_id') diff --git a/arkindex/documents/api/ml.py b/arkindex/documents/api/ml.py index bd46eb8a71..ce49c4f205 100644 --- a/arkindex/documents/api/ml.py +++ b/arkindex/documents/api/ml.py @@ -11,7 +11,6 @@ from rest_framework.generics import ( GenericAPIView, ListAPIView, ListCreateAPIView, - RetrieveDestroyAPIView, RetrieveUpdateDestroyAPIView, ) from rest_framework.response import Response @@ -20,10 +19,8 @@ from arkindex.documents.models import ( Classification, ClassificationState, Corpus, - DataSource, Element, ElementPath, - Entity, MLClass, Right, Transcription, @@ -36,7 +33,6 @@ from arkindex.documents.serializers.ml import ( ClassificationsSelectionSerializer, ClassificationsSerializer, CountMLClassSerializer, - DataSourceStatsSerializer, ElementTranscriptionsBulkSerializer, TranscriptionBulkSerializer, TranscriptionCreateSerializer, @@ -45,9 +41,8 @@ from arkindex.documents.serializers.ml import ( from arkindex.images.models import Zone from arkindex.project.filters import SafeSearchFilter from arkindex.project.mixins import CorpusACLMixin, DeprecatedMixin, SelectionMixin -from arkindex.project.permissions import IsAdminUser, IsVerified, IsVerifiedOrReadOnly -from arkindex.project.triggers import ml_results_delete, reindex_start -from arkindex_common.ml_tool import MLToolType +from arkindex.project.permissions import IsVerified, IsVerifiedOrReadOnly +from arkindex.project.triggers import reindex_start logger = logging.getLogger(__name__) @@ -134,10 +129,9 @@ class TranscriptionEdit(RetrieveUpdateDestroyAPIView): rights = transcription.element.corpus.get_acl_rights(request.user) errors = defaultdict(list) - non_manual_transcription = bool(transcription.worker_version or transcription.source and transcription.source.slug != 'manual') if Right.Write not in rights: errors['__all__'].append('A write access to transcription element corpus is required.') - if Right.Admin not in rights and non_manual_transcription: + if Right.Admin not in rights and transcription.worker_version_id: errors['__all__'].append('Only admins can edit non-manual transcription.') if (errors): raise PermissionDenied(errors) @@ -460,20 +454,12 @@ class ManageClassificationsSelection(SelectionMixin, CorpusACLMixin, CreateAPIVi def create(self, corpus, request, *args, **kwargs): ml_class = MLClass.objects.filter(id=request.data['ml_class']).first() - data_source, _ = DataSource.objects.get_or_create( - type=MLToolType.Classifier, - slug='manual', - defaults={ - 'revision': '', - 'internal': False, - } - ) elements = self.get_selection(corpus.id) existing_element_ids = set(Classification.objects.filter( element_id__in=elements, - source_id=data_source.id, - ml_class_id=ml_class.id + ml_class_id=ml_class.id, + worker_version_id=None, ).values_list('element_id', flat=True)) classifications = [] @@ -481,7 +467,6 @@ class ManageClassificationsSelection(SelectionMixin, CorpusACLMixin, CreateAPIVi classifications.append(Classification( element=element, ml_class=ml_class, - source=data_source, moderator=self.request.user, state=ClassificationState.Validated, high_confidence=False, @@ -535,11 +520,7 @@ class ClassificationReject(ClassificationModerationActionsMixin): def put(self, request, *args, **kwargs): instance = self.get_object() - manual = ( - instance.source and instance.source.slug == 'manual' - or not instance.source and not instance.worker_version - ) - if manual: + if not instance.worker_version_id: # Delete manual classifications upon rejection instance.delete() return Response(None, status=status.HTTP_204_NO_CONTENT) @@ -549,110 +530,3 @@ class ClassificationReject(ClassificationModerationActionsMixin): instance.save(update_fields=['moderator', 'state']) serializer = self.get_serializer(instance) return Response(serializer.data, status=status.HTTP_200_OK) - - -class MLStatsBase(object): - serializer_class = DataSourceStatsSerializer - permission_classes = (IsAdminUser, ) - # Make DRF understand we return an unpaginated list, for OpenAPI schema generation - action = 'list' - pagination_class = None - - def get_count_querysets(self, instance): - """ - Given an object returned by Django REST Framework's get_object, - should return a dict mapping attribute names to querysets - """ - if isinstance(instance, Element): - if not instance.type.folder: - return { - 'transcriptions_count': Transcription.objects.filter(element_id=instance.id), - 'entities_count': Entity.objects.filter( - Q(transcriptions__element_id=instance.id) - | Q(metadatas__element_id=instance.id) - ), - 'classifications_count': Classification.objects.filter(element_id=instance.id), - } - # The folder AND its children - elements = Element.objects.filter(id=instance.id).values('id').union( - # Disable ordering here because we do not need it and it adds an extra column, - # causing the UNION to fail - Element.objects.get_descending(instance.id).order_by().values('id') - ) - elif isinstance(instance, Corpus): - elements = instance.elements.all() - else: - raise ValueError('Instance is not a corpus or an element: {}'.format(instance)) - - return { - 'transcriptions_count': Transcription.objects.filter(element__in=elements), - 'entities_count': Entity.objects.filter( - Q(transcriptions__element__in=elements) - | Q(metadatas__element__in=elements) - ), - 'classifications_count': Classification.objects.filter(element__in=elements), - } - - def get_counts(self): - count_querysets = self.get_count_querysets(self.get_object()) - - # A dict that links source IDs to another dict holding their stats: - # {id: {transcriptions_count: 42, …}, …} - counts = defaultdict(dict) - - # Request statistics for each kind of ML result, grouped by source ID: lowers the need for any joins or unions - for field_name, queryset in count_querysets.items(): - queryset = queryset.values('source_id').annotate(count=Count('id')).values_list('source_id', 'count') - for source_id, count in queryset: - counts[source_id][field_name] = count - - # Fetch the source IDs returned by the previous queries, - # and set the counts as if they were usual queryset annotations. - sources = list(DataSource.objects.filter(id__in=counts.keys()).order_by('name')) - for source in sources: - for name, value in counts[source.id].items(): - setattr(source, name, value) - - return sources - - def get_serializer(self, *args, **kwargs): - # Force the serializer to work as a list, except when get_serializer is called without arguments - # because the OpenAPI schema generator needs a normal serializer - if args or kwargs: - kwargs['many'] = True - return super().get_serializer(*args, **kwargs) - - def retrieve(self, *args, **kwargs): - serializer = self.get_serializer(self.get_counts()) - return Response(serializer.data) - - -class ElementMLStats(MLStatsBase, RetrieveDestroyAPIView): - openapi_overrides = { - 'operationId': 'RetrieveElementMLStats', - 'description': 'List machine learning result sources along with their result counts for an element', - 'tags': ['ml'], - } - - def get_queryset(self): - return Element.objects.filter(corpus__in=Corpus.objects.readable(self.request.user)).select_related('type') - - def destroy(self, *args, **kwargs): - ml_results_delete(element=self.get_object(), user_id=self.request.user.id) - return Response(status=status.HTTP_204_NO_CONTENT) - - -class CorpusMLStats(MLStatsBase, RetrieveDestroyAPIView): - openapi_overrides = { - 'operationId': 'RetrieveCorpusMLStats', - 'description': 'List machine learning results sources along with their result counts ' - 'for all elements in a corpus', - 'tags': ['ml'], - } - - def get_queryset(self): - return Corpus.objects.readable(self.request.user).only('id') - - def destroy(self, *args, **kwargs): - ml_results_delete(corpus=self.get_object(), user_id=self.request.user.id) - return Response(status=status.HTTP_204_NO_CONTENT) diff --git a/arkindex/documents/api/search.py b/arkindex/documents/api/search.py index 756f020c0c..eeda073222 100644 --- a/arkindex/documents/api/search.py +++ b/arkindex/documents/api/search.py @@ -25,7 +25,7 @@ class ElementSearch(SearchAPIView): 'operationId': 'SearchElements', 'security': [], 'description': 'Get a list of elements with their parents, the total number of transcriptions ' - 'in each element, and a few (not all) of their transcriptions, with their source, ' + 'in each element, and a few (not all) of their transcriptions, with their worker version, ' 'type, zone and image, for a given query.', 'tags': ['search'], } diff --git a/arkindex/documents/fixtures/data.json b/arkindex/documents/fixtures/data.json index e2391f3369..c556854e20 100644 --- a/arkindex/documents/fixtures/data.json +++ b/arkindex/documents/fixtures/data.json @@ -1,125 +1,125 @@ [ { "model": "dataimport.repository", - "pk": "1a1bb004-df74-44ea-af0f-b21854923882", + "pk": "8172dc08-bfbf-47e2-923f-60b259310a60", "fields": { "url": "http://gitlab/repo", "type": "iiif", "hook_token": "hook-token", - "credentials": "c3a461a3-01f8-4c69-905e-0ca37f244d7e", + "credentials": "e87ff20b-d28f-42f9-b846-b3de14ac0e27", "provider_name": "GitLabProvider" } }, { "model": "dataimport.repository", - "pk": "1d8d3e70-bfa0-4fb6-8000-3b10757e3705", + "pk": "dae36658-61c8-4f30-a7d6-87131b82c81a", "fields": { "url": "http://my_repo.fake/workers/worker", "type": "worker", "hook_token": "worker-hook-token", - "credentials": "c3a461a3-01f8-4c69-905e-0ca37f244d7e", + "credentials": "e87ff20b-d28f-42f9-b846-b3de14ac0e27", "provider_name": "GitLabProvider" } }, { "model": "dataimport.revision", - "pk": "8d17b4c4-f003-4433-b76b-ee9bd08f94db", + "pk": "5a4abec7-46ea-474d-a8be-1186408eb36b", "fields": { "created": "2020-02-02T01:23:45.678Z", "updated": "2020-02-02T01:23:45.678Z", - "repo": "1d8d3e70-bfa0-4fb6-8000-3b10757e3705", - "hash": "1337", - "message": "My w0rk3r", - "author": "Test user" + "repo": "8172dc08-bfbf-47e2-923f-60b259310a60", + "hash": "42", + "message": "a", + "author": "me" } }, { "model": "dataimport.revision", - "pk": "e30f854c-25da-4694-8fb7-e4c76767ab36", + "pk": "ee7e38b6-386e-44fb-92cb-e6ac737d9843", "fields": { "created": "2020-02-02T01:23:45.678Z", "updated": "2020-02-02T01:23:45.678Z", - "repo": "1a1bb004-df74-44ea-af0f-b21854923882", - "hash": "42", - "message": "a", - "author": "me" + "repo": "dae36658-61c8-4f30-a7d6-87131b82c81a", + "hash": "1337", + "message": "My w0rk3r", + "author": "Test user" } }, { "model": "dataimport.worker", - "pk": "48f5e1bb-e9b1-4178-aa9a-4e44b2ceb531", + "pk": "16e73ec0-288b-4e26-82f6-4993a1121a7a", "fields": { - "name": "Document layout analyser", - "slug": "dla", - "type": "dla", - "repository": "1d8d3e70-bfa0-4fb6-8000-3b10757e3705" + "name": "Recognizer", + "slug": "reco", + "type": "recognizer", + "repository": "dae36658-61c8-4f30-a7d6-87131b82c81a" } }, { "model": "dataimport.worker", - "pk": "8fa8be15-58db-4504-9bb4-47c65b833015", + "pk": "4342a4b8-b34e-4f9b-8ab3-a946bc7f4b65", "fields": { - "name": "Recognizer", - "slug": "reco", - "type": "recognizer", - "repository": "1d8d3e70-bfa0-4fb6-8000-3b10757e3705" + "name": "Document layout analyser", + "slug": "dla", + "type": "dla", + "repository": "dae36658-61c8-4f30-a7d6-87131b82c81a" } }, { "model": "dataimport.workerversion", - "pk": "84a86b51-82a8-4c39-9d70-52307a6aab35", + "pk": "9105c9e3-bf64-4c28-b50c-5307c32aa17b", "fields": { - "worker": "8fa8be15-58db-4504-9bb4-47c65b833015", - "revision": "8d17b4c4-f003-4433-b76b-ee9bd08f94db", + "worker": "16e73ec0-288b-4e26-82f6-4993a1121a7a", + "revision": "ee7e38b6-386e-44fb-92cb-e6ac737d9843", "configuration": { "test": 42 }, "state": "available", - "docker_image": "e55e4fb8-65cc-41f3-a464-2e58103d66f4", + "docker_image": "c291272c-ef11-4c27-a882-099fd0c0e1e8", "docker_image_iid": null } }, { "model": "dataimport.workerversion", - "pk": "f2528da0-4fa7-4984-8482-59d270ab557c", + "pk": "9a8ace04-48f3-4a45-bac8-504924ba886e", "fields": { - "worker": "48f5e1bb-e9b1-4178-aa9a-4e44b2ceb531", - "revision": "8d17b4c4-f003-4433-b76b-ee9bd08f94db", + "worker": "4342a4b8-b34e-4f9b-8ab3-a946bc7f4b65", + "revision": "ee7e38b6-386e-44fb-92cb-e6ac737d9843", "configuration": { "test": 42 }, "state": "available", - "docker_image": "e55e4fb8-65cc-41f3-a464-2e58103d66f4", + "docker_image": "c291272c-ef11-4c27-a882-099fd0c0e1e8", "docker_image_iid": null } }, { "model": "documents.corpus", - "pk": "951ed1f8-9c0d-47ec-b406-79982dcbbce9", + "pk": "61038d3b-9ab5-4bb8-bc1a-7ab296c6d00b", "fields": { "created": "2020-02-02T01:23:45.678Z", "updated": "2020-02-02T01:23:45.678Z", "name": "Unit Tests", "description": "", - "repository": "1a1bb004-df74-44ea-af0f-b21854923882", + "repository": "8172dc08-bfbf-47e2-923f-60b259310a60", "public": true } }, { "model": "documents.elementtype", - "pk": "0d3157fe-5673-4ecb-83dd-ac7151080d8c", + "pk": "19a91530-bb60-4055-b959-93bb4e08629b", "fields": { - "corpus": "951ed1f8-9c0d-47ec-b406-79982dcbbce9", - "slug": "page", - "display_name": "Page", + "corpus": "61038d3b-9ab5-4bb8-bc1a-7ab296c6d00b", + "slug": "text_line", + "display_name": "Line", "folder": false } }, { "model": "documents.elementtype", - "pk": "361fcb21-5c58-4d4f-b148-233e5087926a", + "pk": "770588c7-ff61-480a-8987-498d5f9da9f4", "fields": { - "corpus": "951ed1f8-9c0d-47ec-b406-79982dcbbce9", + "corpus": "61038d3b-9ab5-4bb8-bc1a-7ab296c6d00b", "slug": "act", "display_name": "Act", "folder": false @@ -127,29 +127,29 @@ }, { "model": "documents.elementtype", - "pk": "3b9e0ba4-17ae-4881-a0e0-154bddeda913", + "pk": "7a9610bd-1bf5-4215-aa06-9b5b281a6a8b", "fields": { - "corpus": "951ed1f8-9c0d-47ec-b406-79982dcbbce9", - "slug": "word", - "display_name": "Word", - "folder": false + "corpus": "61038d3b-9ab5-4bb8-bc1a-7ab296c6d00b", + "slug": "volume", + "display_name": "Volume", + "folder": true } }, { "model": "documents.elementtype", - "pk": "a1a545f3-e4d0-47b0-a56e-6703907dca7f", + "pk": "907d38a6-1d13-4c06-8c3d-7855207296b6", "fields": { - "corpus": "951ed1f8-9c0d-47ec-b406-79982dcbbce9", - "slug": "text_line", - "display_name": "Line", + "corpus": "61038d3b-9ab5-4bb8-bc1a-7ab296c6d00b", + "slug": "word", + "display_name": "Word", "folder": false } }, { "model": "documents.elementtype", - "pk": "b53547d0-3a8c-4ad8-b576-f8e539d1d485", + "pk": "93f4311c-34dd-4642-849b-29ae386649c9", "fields": { - "corpus": "951ed1f8-9c0d-47ec-b406-79982dcbbce9", + "corpus": "61038d3b-9ab5-4bb8-bc1a-7ab296c6d00b", "slug": "surface", "display_name": "Surface", "folder": false @@ -157,830 +157,769 @@ }, { "model": "documents.elementtype", - "pk": "e05821fd-af1c-4ec6-afec-fc2f6a0fd722", + "pk": "a0f4ce71-005e-4b9a-ab2c-dd1b3a82d543", "fields": { - "corpus": "951ed1f8-9c0d-47ec-b406-79982dcbbce9", - "slug": "volume", - "display_name": "Volume", - "folder": true + "corpus": "61038d3b-9ab5-4bb8-bc1a-7ab296c6d00b", + "slug": "page", + "display_name": "Page", + "folder": false } }, { "model": "documents.elementpath", - "pk": "0042c780-4576-4afb-bf59-575cc879abe6", + "pk": "020e7620-2765-4dca-ad48-d953762b6289", "fields": { - "element": "195f1fe7-4abd-4582-a2d0-900254d71ecf", - "path": "[\"9afec968-86bd-4c75-93e9-bf12a463e9df\"]", - "ordering": 2 + "element": "21368a9b-b2a3-4146-b497-537256f7c6d1", + "path": "[\"40e4a6b1-349a-4bd4-a4e1-bcee05c3c1f6\", \"820176ad-1230-4663-8e54-3b97556f927b\"]", + "ordering": 0 } }, { "model": "documents.elementpath", - "pk": "0234cd81-f923-44af-adae-5af0f0ff6f72", + "pk": "107ff68a-5ec8-474c-9e22-4b753498127d", "fields": { - "element": "e1020d75-d956-45a0-b933-909f3fc3d91c", - "path": "[\"9afec968-86bd-4c75-93e9-bf12a463e9df\", \"dc25f77d-167d-4762-b1ac-36ead49181f9\"]", - "ordering": 2 + "element": "20a97319-35d1-47d4-ad6f-c17278218c7f", + "path": "[\"40e4a6b1-349a-4bd4-a4e1-bcee05c3c1f6\", \"7ea0e102-a84d-4635-96ce-248ec0253867\"]", + "ordering": 1 } }, { "model": "documents.elementpath", - "pk": "0de665cf-d830-4635-b08a-421a1cae04f8", + "pk": "20d850cb-4be8-4a2f-bb09-eaec5cceeef5", "fields": { - "element": "f8ec061e-1482-4653-aeb1-5db22f286553", - "path": "[\"9afec968-86bd-4c75-93e9-bf12a463e9df\"]", - "ordering": 3 + "element": "a6157f7c-38c9-4d01-b81f-9e8f995140b3", + "path": "[\"40e4a6b1-349a-4bd4-a4e1-bcee05c3c1f6\", \"fce8eb2a-aa2e-49d6-b875-514e6aae3c84\"]", + "ordering": 0 } }, { "model": "documents.elementpath", - "pk": "0ed4caf0-1d15-442f-9fa7-8abda649d34d", + "pk": "21e49f8e-4ccc-41d3-8fe7-4ee723bb8a04", "fields": { - "element": "ce6f78ce-0cfc-4cc3-8d4c-6a7b4fe68530", - "path": "[\"585a6b5e-b5bc-4632-ad04-e66c0168dab9\"]", - "ordering": 0 + "element": "83ad127c-261f-4ca6-8f4e-fdbbeed0d0c8", + "path": "[\"40e4a6b1-349a-4bd4-a4e1-bcee05c3c1f6\", \"c1ad5513-14f5-46de-9496-abf0645a7f20\"]", + "ordering": 1 } }, { "model": "documents.elementpath", - "pk": "10a2fc43-bfd3-4ccd-ad82-1838a908fe71", + "pk": "23824f27-c4fb-488a-94dc-08553fcf9682", "fields": { - "element": "9c80ae84-94a3-4ddd-97d8-a80fd73af9b2", - "path": "[\"9afec968-86bd-4c75-93e9-bf12a463e9df\", \"3b9c805e-d190-4bc7-a8b0-2fdae4b6ab9f\"]", + "element": "55063406-e419-4f9a-9beb-38a9cca22e11", + "path": "[\"40e4a6b1-349a-4bd4-a4e1-bcee05c3c1f6\", \"c1ad5513-14f5-46de-9496-abf0645a7f20\"]", "ordering": 0 } }, { "model": "documents.elementpath", - "pk": "15cfde8c-3dec-4bd9-a899-d9be192c5102", + "pk": "2d553b15-6c8a-4bd5-b5cc-a77582aa650a", "fields": { - "element": "3b9c805e-d190-4bc7-a8b0-2fdae4b6ab9f", - "path": "[\"9afec968-86bd-4c75-93e9-bf12a463e9df\"]", + "element": "1f45bf2f-5521-409f-af43-5cf3632ee5ec", + "path": "[\"40e4a6b1-349a-4bd4-a4e1-bcee05c3c1f6\", \"d6565d2c-5fa1-43e8-ab27-08e8466036c2\"]", "ordering": 0 } }, { "model": "documents.elementpath", - "pk": "23726b55-ca70-4f4b-895d-d6d879853f75", + "pk": "4a7e44c0-3e3b-48e1-98ad-4c439f458151", "fields": { - "element": "e8dba6c7-7aa1-4cea-b12f-8aeb58c19da1", - "path": "[\"9afec968-86bd-4c75-93e9-bf12a463e9df\", \"dc25f77d-167d-4762-b1ac-36ead49181f9\"]", - "ordering": 1 + "element": "fce8eb2a-aa2e-49d6-b875-514e6aae3c84", + "path": "[\"40e4a6b1-349a-4bd4-a4e1-bcee05c3c1f6\"]", + "ordering": 0 } }, { "model": "documents.elementpath", - "pk": "28360113-4374-4b33-a912-c324827ba3b8", + "pk": "4bbb2ea0-3c9e-41e5-8d98-4256c0bc8930", "fields": { - "element": "eccfe352-bb4f-4dcd-8af5-cbf20ffb7c93", - "path": "[\"9afec968-86bd-4c75-93e9-bf12a463e9df\", \"4168bf0e-e105-4597-82fb-d44f5f601c42\"]", - "ordering": 0 + "element": "6623e7e4-e41f-427b-936f-1308c4a2aac3", + "path": "[\"40e4a6b1-349a-4bd4-a4e1-bcee05c3c1f6\", \"fce8eb2a-aa2e-49d6-b875-514e6aae3c84\"]", + "ordering": 1 } }, { "model": "documents.elementpath", - "pk": "297a7af8-40c3-4d3a-b22b-3f69876b07d3", + "pk": "542509b3-c942-4bfe-bc8c-4e18f1532f52", "fields": { - "element": "82e3e1de-3498-462e-b0fa-294704e09871", - "path": "[\"9afec968-86bd-4c75-93e9-bf12a463e9df\", \"195f1fe7-4abd-4582-a2d0-900254d71ecf\"]", - "ordering": 1 + "element": "e449d58d-e5ec-43e5-bc71-f57aa9c31fdb", + "path": "[\"40e4a6b1-349a-4bd4-a4e1-bcee05c3c1f6\", \"1edce450-7423-45dc-ae59-8c0e880b437b\"]", + "ordering": 0 } }, { "model": "documents.elementpath", - "pk": "36208527-e8a2-43c9-8e3b-3901d847fd59", + "pk": "54c3ced1-9a33-458e-9e11-ce57e29460e8", "fields": { - "element": "dc25f77d-167d-4762-b1ac-36ead49181f9", - "path": "[\"9afec968-86bd-4c75-93e9-bf12a463e9df\"]", - "ordering": 1 + "element": "bc9eee31-f209-474f-b9bf-403735f4b009", + "path": "[\"e59f1432-9d0d-4161-a516-a31c41f02385\"]", + "ordering": 0 } }, { "model": "documents.elementpath", - "pk": "44cb8157-22b9-45c8-b40f-177c4c221ccb", + "pk": "54c70766-1335-4e59-b125-69a27fe61236", "fields": { - "element": "000f3a0b-0d42-44db-a120-a626f3a1e73b", - "path": "[\"9afec968-86bd-4c75-93e9-bf12a463e9df\", \"1d8decb9-a27d-4e8c-82a9-e9fe3379790f\"]", + "element": "89d2dc9d-75c1-4d7d-8fb1-f25b3da597c7", + "path": "[\"40e4a6b1-349a-4bd4-a4e1-bcee05c3c1f6\", \"f5144363-f6c7-4dfd-949b-5141fc5c50b6\"]", "ordering": 0 } }, { "model": "documents.elementpath", - "pk": "4dc656b4-d9e0-491a-b74e-c50d910ef5c5", + "pk": "70d6fab8-a3c5-4828-84b6-6f0312574a0a", "fields": { - "element": "0459e51f-772a-409a-9d30-f2a3d01ab894", - "path": "[\"9afec968-86bd-4c75-93e9-bf12a463e9df\", \"144551e9-cce8-42e8-bf58-636e7694ed87\"]", - "ordering": 0 + "element": "b210339b-aa36-40ae-8863-5f56904145ec", + "path": "[\"40e4a6b1-349a-4bd4-a4e1-bcee05c3c1f6\", \"820176ad-1230-4663-8e54-3b97556f927b\"]", + "ordering": 1 } }, { "model": "documents.elementpath", - "pk": "544cd902-48f9-4b0c-9451-1aec0be8a694", + "pk": "872c1a51-4a4a-493d-8f1c-101547d7af9c", "fields": { - "element": "d258e27f-ebff-40d2-984b-be7a44ce829e", - "path": "[\"585a6b5e-b5bc-4632-ad04-e66c0168dab9\"]", + "element": "b4a582ec-6084-4eff-961b-d4c37184fbe1", + "path": "[\"40e4a6b1-349a-4bd4-a4e1-bcee05c3c1f6\"]", "ordering": 2 } }, { "model": "documents.elementpath", - "pk": "642bc3e4-c38b-4440-bda6-2aae2b051e0e", + "pk": "95a724fd-ebc1-44af-ad56-60ad6a180ff6", "fields": { - "element": "144551e9-cce8-42e8-bf58-636e7694ed87", - "path": "[\"9afec968-86bd-4c75-93e9-bf12a463e9df\"]", - "ordering": 2 + "element": "2269701c-d4b5-4947-8bd0-ec65d4658734", + "path": "[\"40e4a6b1-349a-4bd4-a4e1-bcee05c3c1f6\", \"b4a582ec-6084-4eff-961b-d4c37184fbe1\"]", + "ordering": 0 } }, { "model": "documents.elementpath", - "pk": "72404345-f314-47a4-ac63-53fb950dbc9f", + "pk": "99df9fa3-9e32-4cac-b108-95b08ce442db", "fields": { - "element": "7392707d-0b72-4f6b-9fb8-943b95ce197b", - "path": "[\"9afec968-86bd-4c75-93e9-bf12a463e9df\", \"1d8decb9-a27d-4e8c-82a9-e9fe3379790f\"]", + "element": "194e5073-57a3-4932-a5e2-74164c0fe5cf", + "path": "[\"40e4a6b1-349a-4bd4-a4e1-bcee05c3c1f6\", \"820176ad-1230-4663-8e54-3b97556f927b\"]", "ordering": 2 } }, { "model": "documents.elementpath", - "pk": "7d689028-d217-4e84-b06b-7e6e05e551d0", + "pk": "af2cf103-10c6-4d5d-917e-714fa7d72153", "fields": { - "element": "1d8decb9-a27d-4e8c-82a9-e9fe3379790f", - "path": "[\"9afec968-86bd-4c75-93e9-bf12a463e9df\"]", + "element": "4490a0ec-366a-412b-9725-f28e00290657", + "path": "[\"40e4a6b1-349a-4bd4-a4e1-bcee05c3c1f6\", \"7ea0e102-a84d-4635-96ce-248ec0253867\"]", "ordering": 0 } }, { "model": "documents.elementpath", - "pk": "7f28b72b-b5b9-4dc6-b278-3a46c9a65d19", + "pk": "b4560811-60f0-4da6-a146-29a1dffcf8c9", "fields": { - "element": "eb5447d7-b444-4818-81c6-3df409a2ff6d", - "path": "[\"9afec968-86bd-4c75-93e9-bf12a463e9df\"]", - "ordering": 1 + "element": "e9bed2af-85b9-4cbf-af6e-7b8cc273f148", + "path": "[\"40e4a6b1-349a-4bd4-a4e1-bcee05c3c1f6\", \"fce8eb2a-aa2e-49d6-b875-514e6aae3c84\"]", + "ordering": 0 } }, { "model": "documents.elementpath", - "pk": "8447de6f-af20-41b2-bfa2-c5134ae76b62", + "pk": "bb8ff62b-d3ff-4920-94a6-710662125a56", "fields": { - "element": "ce0c9320-9ea6-4ba7-81d9-42bbf410d40d", - "path": "[\"9afec968-86bd-4c75-93e9-bf12a463e9df\", \"1d8decb9-a27d-4e8c-82a9-e9fe3379790f\"]", + "element": "0006d468-08e2-4855-96f6-9f71185a8d00", + "path": "[\"e59f1432-9d0d-4161-a516-a31c41f02385\"]", "ordering": 1 } }, { "model": "documents.elementpath", - "pk": "8ac2bb0e-0f89-4fc7-bbcf-ff7d9786022a", + "pk": "c42359b5-5ff8-4c0c-8821-ba7c8c019ae1", "fields": { - "element": "be6b085e-9f25-4b45-a2f7-044669deec25", - "path": "[\"9afec968-86bd-4c75-93e9-bf12a463e9df\", \"eb5447d7-b444-4818-81c6-3df409a2ff6d\"]", - "ordering": 0 + "element": "36368687-8b45-4137-8a4d-e81774a6b97f", + "path": "[\"e59f1432-9d0d-4161-a516-a31c41f02385\"]", + "ordering": 2 } }, { "model": "documents.elementpath", - "pk": "9b550d08-8014-4551-96a3-0869c32a44f3", + "pk": "c50bd248-afb9-49c7-86f2-e8d4ead55b58", "fields": { - "element": "ad406e77-3052-4840-a233-2f970db1a16c", - "path": "[\"585a6b5e-b5bc-4632-ad04-e66c0168dab9\"]", - "ordering": 1 + "element": "4908e767-738d-4a53-beb1-b353e1762066", + "path": "[\"40e4a6b1-349a-4bd4-a4e1-bcee05c3c1f6\", \"7ea0e102-a84d-4635-96ce-248ec0253867\"]", + "ordering": 2 } }, { "model": "documents.elementpath", - "pk": "9ff3bc81-82a3-44e7-b585-6c864cfb3495", + "pk": "ccc95560-99a0-44c0-8458-c3d6ad0767ac", "fields": { - "element": "4ee16589-1cb0-4395-a185-79d89a88a106", - "path": "[\"9afec968-86bd-4c75-93e9-bf12a463e9df\", \"195f1fe7-4abd-4582-a2d0-900254d71ecf\"]", - "ordering": 2 + "element": "d6565d2c-5fa1-43e8-ab27-08e8466036c2", + "path": "[\"40e4a6b1-349a-4bd4-a4e1-bcee05c3c1f6\"]", + "ordering": 4 } }, { "model": "documents.elementpath", - "pk": "a6f38594-99d4-4be2-8306-524edd1e3ff2", + "pk": "d073805f-2d73-4c3d-9326-0cdf08267575", "fields": { - "element": "12c1fab0-7082-4308-b474-bda161034a56", - "path": "[\"9afec968-86bd-4c75-93e9-bf12a463e9df\", \"eb5447d7-b444-4818-81c6-3df409a2ff6d\"]", + "element": "c1ad5513-14f5-46de-9496-abf0645a7f20", + "path": "[\"40e4a6b1-349a-4bd4-a4e1-bcee05c3c1f6\"]", "ordering": 1 } }, { "model": "documents.elementpath", - "pk": "bb74d55a-6863-4ad8-a154-a0e89db5fadb", + "pk": "d08fe96d-8a5a-4073-8e57-6bff4f9c0f91", "fields": { - "element": "4168bf0e-e105-4597-82fb-d44f5f601c42", - "path": "[\"9afec968-86bd-4c75-93e9-bf12a463e9df\"]", - "ordering": 4 + "element": "f5144363-f6c7-4dfd-949b-5141fc5c50b6", + "path": "[\"40e4a6b1-349a-4bd4-a4e1-bcee05c3c1f6\"]", + "ordering": 3 } }, { "model": "documents.elementpath", - "pk": "cfd121cb-db35-4ce4-bc94-9cde2576e152", + "pk": "d3fa6ef0-d18b-4cf9-94a2-847ec65f17f7", "fields": { - "element": "57a34149-803b-4104-b873-6880edf25e41", - "path": "[\"9afec968-86bd-4c75-93e9-bf12a463e9df\", \"1d8decb9-a27d-4e8c-82a9-e9fe3379790f\"]", - "ordering": 0 + "element": "820176ad-1230-4663-8e54-3b97556f927b", + "path": "[\"40e4a6b1-349a-4bd4-a4e1-bcee05c3c1f6\"]", + "ordering": 1 } }, { "model": "documents.elementpath", - "pk": "d69de1e0-9347-4cf0-8021-7a8d5b6e3188", + "pk": "dd8f8931-803d-49d8-ac06-68e75e8897ab", "fields": { - "element": "3543b88f-a695-421e-9816-223e014ee5b6", - "path": "[\"9afec968-86bd-4c75-93e9-bf12a463e9df\", \"f8ec061e-1482-4653-aeb1-5db22f286553\"]", + "element": "1edce450-7423-45dc-ae59-8c0e880b437b", + "path": "[\"40e4a6b1-349a-4bd4-a4e1-bcee05c3c1f6\"]", "ordering": 0 } }, { "model": "documents.elementpath", - "pk": "e61e96f4-ef8b-465b-a6ad-d87352356497", + "pk": "e3d9285c-c801-4e0b-ae00-7779168b87aa", "fields": { - "element": "2d970951-a86a-46da-908e-7d5bc030ff21", - "path": "[\"9afec968-86bd-4c75-93e9-bf12a463e9df\", \"195f1fe7-4abd-4582-a2d0-900254d71ecf\"]", - "ordering": 0 + "element": "7ea0e102-a84d-4635-96ce-248ec0253867", + "path": "[\"40e4a6b1-349a-4bd4-a4e1-bcee05c3c1f6\"]", + "ordering": 2 } }, { "model": "documents.elementpath", - "pk": "ff8a475e-4976-4f34-bb90-ab47993e81f2", - "fields": { - "element": "8e0cdc30-08e6-4509-9189-be7743e05fea", - "path": "[\"9afec968-86bd-4c75-93e9-bf12a463e9df\", \"dc25f77d-167d-4762-b1ac-36ead49181f9\"]", - "ordering": 0 - } -}, -{ - "model": "documents.element", - "pk": "000f3a0b-0d42-44db-a120-a626f3a1e73b", + "pk": "e6f5a965-8680-45d5-8117-afa84ddadb51", "fields": { - "created": "2020-02-02T01:23:45.678Z", - "updated": "2020-02-02T01:23:45.678Z", - "corpus": "951ed1f8-9c0d-47ec-b406-79982dcbbce9", - "type": "3b9e0ba4-17ae-4881-a0e0-154bddeda913", - "name": "PARIS", - "zone": "32876cbc-cb85-4c31-9a6e-b743eb3cab8f", - "source": null, - "worker_version": null + "element": "8773bb0a-6820-467c-b616-53f6aa667c2e", + "path": "[\"40e4a6b1-349a-4bd4-a4e1-bcee05c3c1f6\", \"fce8eb2a-aa2e-49d6-b875-514e6aae3c84\"]", + "ordering": 2 } }, { "model": "documents.element", - "pk": "0459e51f-772a-409a-9d30-f2a3d01ab894", + "pk": "0006d468-08e2-4855-96f6-9f71185a8d00", "fields": { "created": "2020-02-02T01:23:45.678Z", "updated": "2020-02-02T01:23:45.678Z", - "corpus": "951ed1f8-9c0d-47ec-b406-79982dcbbce9", - "type": "b53547d0-3a8c-4ad8-b576-f8e539d1d485", - "name": "Surface D", - "zone": "13f4e60d-5e26-4a51-aa70-e193e26dfac8", - "source": null, + "corpus": "61038d3b-9ab5-4bb8-bc1a-7ab296c6d00b", + "type": "a0f4ce71-005e-4b9a-ab2c-dd1b3a82d543", + "name": "Volume 2, page 1v", + "zone": "96427f35-175d-49f1-8443-06b3b840aa93", "worker_version": null } }, { "model": "documents.element", - "pk": "12c1fab0-7082-4308-b474-bda161034a56", + "pk": "194e5073-57a3-4932-a5e2-74164c0fe5cf", "fields": { "created": "2020-02-02T01:23:45.678Z", "updated": "2020-02-02T01:23:45.678Z", - "corpus": "951ed1f8-9c0d-47ec-b406-79982dcbbce9", - "type": "b53547d0-3a8c-4ad8-b576-f8e539d1d485", - "name": "Surface C", - "zone": "b16736b8-48f5-48a5-a301-adbb689581cd", - "source": null, + "corpus": "61038d3b-9ab5-4bb8-bc1a-7ab296c6d00b", + "type": "907d38a6-1d13-4c06-8c3d-7855207296b6", + "name": "DATUM", + "zone": "baf7cc76-08ed-484b-bc46-8e9fddadcf2c", "worker_version": null } }, { "model": "documents.element", - "pk": "144551e9-cce8-42e8-bf58-636e7694ed87", + "pk": "1edce450-7423-45dc-ae59-8c0e880b437b", "fields": { "created": "2020-02-02T01:23:45.678Z", "updated": "2020-02-02T01:23:45.678Z", - "corpus": "951ed1f8-9c0d-47ec-b406-79982dcbbce9", - "type": "361fcb21-5c58-4d4f-b148-233e5087926a", - "name": "Act 3", + "corpus": "61038d3b-9ab5-4bb8-bc1a-7ab296c6d00b", + "type": "770588c7-ff61-480a-8987-498d5f9da9f4", + "name": "Act 1", "zone": null, - "source": null, "worker_version": null } }, { "model": "documents.element", - "pk": "195f1fe7-4abd-4582-a2d0-900254d71ecf", + "pk": "1f45bf2f-5521-409f-af43-5cf3632ee5ec", "fields": { "created": "2020-02-02T01:23:45.678Z", "updated": "2020-02-02T01:23:45.678Z", - "corpus": "951ed1f8-9c0d-47ec-b406-79982dcbbce9", - "type": "0d3157fe-5673-4ecb-83dd-ac7151080d8c", - "name": "Volume 1, page 2r", - "zone": "493954e4-91f7-4f5d-bb1d-f58acf3ad4d1", - "source": null, + "corpus": "61038d3b-9ab5-4bb8-bc1a-7ab296c6d00b", + "type": "93f4311c-34dd-4642-849b-29ae386649c9", + "name": "Surface F", + "zone": "ca73fe53-829a-4c55-8223-fbaaf4dff10c", "worker_version": null } }, { "model": "documents.element", - "pk": "1d8decb9-a27d-4e8c-82a9-e9fe3379790f", + "pk": "20a97319-35d1-47d4-ad6f-c17278218c7f", "fields": { "created": "2020-02-02T01:23:45.678Z", "updated": "2020-02-02T01:23:45.678Z", - "corpus": "951ed1f8-9c0d-47ec-b406-79982dcbbce9", - "type": "0d3157fe-5673-4ecb-83dd-ac7151080d8c", - "name": "Volume 1, page 1r", - "zone": "5dec929a-288d-4d17-9912-5344842d0eac", - "source": null, + "corpus": "61038d3b-9ab5-4bb8-bc1a-7ab296c6d00b", + "type": "907d38a6-1d13-4c06-8c3d-7855207296b6", + "name": "ROY", + "zone": "64212529-490d-452a-ac8c-11da57d2305a", "worker_version": null } }, { "model": "documents.element", - "pk": "2d970951-a86a-46da-908e-7d5bc030ff21", + "pk": "21368a9b-b2a3-4146-b497-537256f7c6d1", "fields": { "created": "2020-02-02T01:23:45.678Z", "updated": "2020-02-02T01:23:45.678Z", - "corpus": "951ed1f8-9c0d-47ec-b406-79982dcbbce9", - "type": "3b9e0ba4-17ae-4881-a0e0-154bddeda913", + "corpus": "61038d3b-9ab5-4bb8-bc1a-7ab296c6d00b", + "type": "907d38a6-1d13-4c06-8c3d-7855207296b6", "name": "PARIS", - "zone": "bddb803a-c25b-43b0-ab03-880af6568397", - "source": null, + "zone": "9be7fa31-5ede-450a-80c1-de9f683ff2bf", "worker_version": null } }, { "model": "documents.element", - "pk": "3543b88f-a695-421e-9816-223e014ee5b6", + "pk": "2269701c-d4b5-4947-8bd0-ec65d4658734", "fields": { "created": "2020-02-02T01:23:45.678Z", "updated": "2020-02-02T01:23:45.678Z", - "corpus": "951ed1f8-9c0d-47ec-b406-79982dcbbce9", - "type": "b53547d0-3a8c-4ad8-b576-f8e539d1d485", - "name": "Surface E", - "zone": "ffc2612e-481b-423f-a902-3538d29542de", - "source": null, + "corpus": "61038d3b-9ab5-4bb8-bc1a-7ab296c6d00b", + "type": "93f4311c-34dd-4642-849b-29ae386649c9", + "name": "Surface D", + "zone": "364c4c7c-b1c9-45ea-a790-8fc6cec96965", "worker_version": null } }, { "model": "documents.element", - "pk": "3b9c805e-d190-4bc7-a8b0-2fdae4b6ab9f", + "pk": "36368687-8b45-4137-8a4d-e81774a6b97f", "fields": { "created": "2020-02-02T01:23:45.678Z", "updated": "2020-02-02T01:23:45.678Z", - "corpus": "951ed1f8-9c0d-47ec-b406-79982dcbbce9", - "type": "361fcb21-5c58-4d4f-b148-233e5087926a", - "name": "Act 1", - "zone": null, - "source": null, + "corpus": "61038d3b-9ab5-4bb8-bc1a-7ab296c6d00b", + "type": "a0f4ce71-005e-4b9a-ab2c-dd1b3a82d543", + "name": "Volume 2, page 2r", + "zone": "a1de4f44-76e3-453e-aeab-5c65ba035b14", "worker_version": null } }, { "model": "documents.element", - "pk": "4168bf0e-e105-4597-82fb-d44f5f601c42", + "pk": "40e4a6b1-349a-4bd4-a4e1-bcee05c3c1f6", "fields": { "created": "2020-02-02T01:23:45.678Z", "updated": "2020-02-02T01:23:45.678Z", - "corpus": "951ed1f8-9c0d-47ec-b406-79982dcbbce9", - "type": "361fcb21-5c58-4d4f-b148-233e5087926a", - "name": "Act 5", + "corpus": "61038d3b-9ab5-4bb8-bc1a-7ab296c6d00b", + "type": "7a9610bd-1bf5-4215-aa06-9b5b281a6a8b", + "name": "Volume 1", "zone": null, - "source": null, "worker_version": null } }, { "model": "documents.element", - "pk": "4ee16589-1cb0-4395-a185-79d89a88a106", + "pk": "4490a0ec-366a-412b-9725-f28e00290657", "fields": { "created": "2020-02-02T01:23:45.678Z", "updated": "2020-02-02T01:23:45.678Z", - "corpus": "951ed1f8-9c0d-47ec-b406-79982dcbbce9", - "type": "3b9e0ba4-17ae-4881-a0e0-154bddeda913", - "name": "DATUM", - "zone": "fcc13de0-6c9e-49ce-8524-8112c7aa454c", - "source": null, + "corpus": "61038d3b-9ab5-4bb8-bc1a-7ab296c6d00b", + "type": "907d38a6-1d13-4c06-8c3d-7855207296b6", + "name": "PARIS", + "zone": "10a1572e-5a70-4e07-a556-d51be314bc8b", "worker_version": null } }, { "model": "documents.element", - "pk": "57a34149-803b-4104-b873-6880edf25e41", + "pk": "4908e767-738d-4a53-beb1-b353e1762066", "fields": { "created": "2020-02-02T01:23:45.678Z", "updated": "2020-02-02T01:23:45.678Z", - "corpus": "951ed1f8-9c0d-47ec-b406-79982dcbbce9", - "type": "a1a545f3-e4d0-47b0-a56e-6703907dca7f", - "name": "Text line", - "zone": "ae266608-3037-4612-9073-f2320c755e70", - "source": null, + "corpus": "61038d3b-9ab5-4bb8-bc1a-7ab296c6d00b", + "type": "907d38a6-1d13-4c06-8c3d-7855207296b6", + "name": "DATUM", + "zone": "bd1fee55-df88-4859-9371-f73765f281bc", "worker_version": null } }, { "model": "documents.element", - "pk": "585a6b5e-b5bc-4632-ad04-e66c0168dab9", + "pk": "55063406-e419-4f9a-9beb-38a9cca22e11", "fields": { "created": "2020-02-02T01:23:45.678Z", "updated": "2020-02-02T01:23:45.678Z", - "corpus": "951ed1f8-9c0d-47ec-b406-79982dcbbce9", - "type": "e05821fd-af1c-4ec6-afec-fc2f6a0fd722", - "name": "Volume 2", - "zone": null, - "source": null, + "corpus": "61038d3b-9ab5-4bb8-bc1a-7ab296c6d00b", + "type": "93f4311c-34dd-4642-849b-29ae386649c9", + "name": "Surface B", + "zone": "f69de5e8-01d7-42c1-b487-53b6821f3fcf", "worker_version": null } }, { "model": "documents.element", - "pk": "7392707d-0b72-4f6b-9fb8-943b95ce197b", + "pk": "6623e7e4-e41f-427b-936f-1308c4a2aac3", "fields": { "created": "2020-02-02T01:23:45.678Z", "updated": "2020-02-02T01:23:45.678Z", - "corpus": "951ed1f8-9c0d-47ec-b406-79982dcbbce9", - "type": "3b9e0ba4-17ae-4881-a0e0-154bddeda913", - "name": "DATUM", - "zone": "7418424e-f978-4554-b4cd-56c96c4196ca", - "source": null, + "corpus": "61038d3b-9ab5-4bb8-bc1a-7ab296c6d00b", + "type": "907d38a6-1d13-4c06-8c3d-7855207296b6", + "name": "ROY", + "zone": "3983c6bf-358a-463f-be1e-d60723dc4e68", "worker_version": null } }, { "model": "documents.element", - "pk": "82e3e1de-3498-462e-b0fa-294704e09871", + "pk": "7ea0e102-a84d-4635-96ce-248ec0253867", "fields": { "created": "2020-02-02T01:23:45.678Z", "updated": "2020-02-02T01:23:45.678Z", - "corpus": "951ed1f8-9c0d-47ec-b406-79982dcbbce9", - "type": "3b9e0ba4-17ae-4881-a0e0-154bddeda913", - "name": "ROY", - "zone": "b9e8a058-a124-443d-825a-05e60e1a29ee", - "source": null, + "corpus": "61038d3b-9ab5-4bb8-bc1a-7ab296c6d00b", + "type": "a0f4ce71-005e-4b9a-ab2c-dd1b3a82d543", + "name": "Volume 1, page 2r", + "zone": "135fafc6-620c-4aee-a714-38b1ae69eacb", "worker_version": null } }, { "model": "documents.element", - "pk": "8e0cdc30-08e6-4509-9189-be7743e05fea", + "pk": "820176ad-1230-4663-8e54-3b97556f927b", "fields": { "created": "2020-02-02T01:23:45.678Z", "updated": "2020-02-02T01:23:45.678Z", - "corpus": "951ed1f8-9c0d-47ec-b406-79982dcbbce9", - "type": "3b9e0ba4-17ae-4881-a0e0-154bddeda913", - "name": "PARIS", - "zone": "ad779382-ad03-4aac-a0fb-e107ede8faf5", - "source": null, + "corpus": "61038d3b-9ab5-4bb8-bc1a-7ab296c6d00b", + "type": "a0f4ce71-005e-4b9a-ab2c-dd1b3a82d543", + "name": "Volume 1, page 1v", + "zone": "87ac302e-3b42-4672-827f-d2b80bbeaec8", "worker_version": null } }, { "model": "documents.element", - "pk": "9afec968-86bd-4c75-93e9-bf12a463e9df", + "pk": "83ad127c-261f-4ca6-8f4e-fdbbeed0d0c8", "fields": { "created": "2020-02-02T01:23:45.678Z", "updated": "2020-02-02T01:23:45.678Z", - "corpus": "951ed1f8-9c0d-47ec-b406-79982dcbbce9", - "type": "e05821fd-af1c-4ec6-afec-fc2f6a0fd722", - "name": "Volume 1", - "zone": null, - "source": null, + "corpus": "61038d3b-9ab5-4bb8-bc1a-7ab296c6d00b", + "type": "93f4311c-34dd-4642-849b-29ae386649c9", + "name": "Surface C", + "zone": "87ac302e-3b42-4672-827f-d2b80bbeaec8", "worker_version": null } }, { "model": "documents.element", - "pk": "9c80ae84-94a3-4ddd-97d8-a80fd73af9b2", + "pk": "8773bb0a-6820-467c-b616-53f6aa667c2e", "fields": { "created": "2020-02-02T01:23:45.678Z", "updated": "2020-02-02T01:23:45.678Z", - "corpus": "951ed1f8-9c0d-47ec-b406-79982dcbbce9", - "type": "b53547d0-3a8c-4ad8-b576-f8e539d1d485", - "name": "Surface A", - "zone": "41b62beb-9aa4-4c99-9820-a7d8e0fc3f62", - "source": null, + "corpus": "61038d3b-9ab5-4bb8-bc1a-7ab296c6d00b", + "type": "907d38a6-1d13-4c06-8c3d-7855207296b6", + "name": "DATUM", + "zone": "120ef74e-0f81-40d4-9d70-25e3d1a598aa", "worker_version": null } }, { "model": "documents.element", - "pk": "ad406e77-3052-4840-a233-2f970db1a16c", + "pk": "89d2dc9d-75c1-4d7d-8fb1-f25b3da597c7", "fields": { "created": "2020-02-02T01:23:45.678Z", "updated": "2020-02-02T01:23:45.678Z", - "corpus": "951ed1f8-9c0d-47ec-b406-79982dcbbce9", - "type": "0d3157fe-5673-4ecb-83dd-ac7151080d8c", - "name": "Volume 2, page 1v", - "zone": "079016a5-09c2-4788-923a-b60954b3da76", - "source": null, + "corpus": "61038d3b-9ab5-4bb8-bc1a-7ab296c6d00b", + "type": "93f4311c-34dd-4642-849b-29ae386649c9", + "name": "Surface E", + "zone": "4cf441c6-3609-4f1c-80bb-23688e4fa62d", "worker_version": null } }, { "model": "documents.element", - "pk": "be6b085e-9f25-4b45-a2f7-044669deec25", + "pk": "a6157f7c-38c9-4d01-b81f-9e8f995140b3", "fields": { "created": "2020-02-02T01:23:45.678Z", "updated": "2020-02-02T01:23:45.678Z", - "corpus": "951ed1f8-9c0d-47ec-b406-79982dcbbce9", - "type": "b53547d0-3a8c-4ad8-b576-f8e539d1d485", - "name": "Surface B", - "zone": "adf62f88-2860-49e8-82df-4a76c8c9f2e3", - "source": null, + "corpus": "61038d3b-9ab5-4bb8-bc1a-7ab296c6d00b", + "type": "907d38a6-1d13-4c06-8c3d-7855207296b6", + "name": "PARIS", + "zone": "85436981-a019-45d7-bef6-0d1cb79f0849", "worker_version": null } }, { "model": "documents.element", - "pk": "ce0c9320-9ea6-4ba7-81d9-42bbf410d40d", + "pk": "b210339b-aa36-40ae-8863-5f56904145ec", "fields": { "created": "2020-02-02T01:23:45.678Z", "updated": "2020-02-02T01:23:45.678Z", - "corpus": "951ed1f8-9c0d-47ec-b406-79982dcbbce9", - "type": "3b9e0ba4-17ae-4881-a0e0-154bddeda913", + "corpus": "61038d3b-9ab5-4bb8-bc1a-7ab296c6d00b", + "type": "907d38a6-1d13-4c06-8c3d-7855207296b6", "name": "ROY", - "zone": "ae266608-3037-4612-9073-f2320c755e70", - "source": null, + "zone": "095dd88c-ca66-4235-bb9e-7519b1908278", "worker_version": null } }, { "model": "documents.element", - "pk": "ce6f78ce-0cfc-4cc3-8d4c-6a7b4fe68530", + "pk": "b4a582ec-6084-4eff-961b-d4c37184fbe1", "fields": { "created": "2020-02-02T01:23:45.678Z", "updated": "2020-02-02T01:23:45.678Z", - "corpus": "951ed1f8-9c0d-47ec-b406-79982dcbbce9", - "type": "0d3157fe-5673-4ecb-83dd-ac7151080d8c", - "name": "Volume 2, page 1r", - "zone": "ff3fc33f-4a14-4e2d-8c78-69c627952aef", - "source": null, + "corpus": "61038d3b-9ab5-4bb8-bc1a-7ab296c6d00b", + "type": "770588c7-ff61-480a-8987-498d5f9da9f4", + "name": "Act 3", + "zone": null, "worker_version": null } }, { "model": "documents.element", - "pk": "d258e27f-ebff-40d2-984b-be7a44ce829e", + "pk": "bc9eee31-f209-474f-b9bf-403735f4b009", "fields": { "created": "2020-02-02T01:23:45.678Z", "updated": "2020-02-02T01:23:45.678Z", - "corpus": "951ed1f8-9c0d-47ec-b406-79982dcbbce9", - "type": "0d3157fe-5673-4ecb-83dd-ac7151080d8c", - "name": "Volume 2, page 2r", - "zone": "ef5fead8-f5dd-44c6-9da7-4bd05cf5c695", - "source": null, + "corpus": "61038d3b-9ab5-4bb8-bc1a-7ab296c6d00b", + "type": "a0f4ce71-005e-4b9a-ab2c-dd1b3a82d543", + "name": "Volume 2, page 1r", + "zone": "1e8433b0-cee0-4207-a310-75c7ea1145dc", "worker_version": null } }, { "model": "documents.element", - "pk": "dc25f77d-167d-4762-b1ac-36ead49181f9", + "pk": "c1ad5513-14f5-46de-9496-abf0645a7f20", "fields": { "created": "2020-02-02T01:23:45.678Z", "updated": "2020-02-02T01:23:45.678Z", - "corpus": "951ed1f8-9c0d-47ec-b406-79982dcbbce9", - "type": "0d3157fe-5673-4ecb-83dd-ac7151080d8c", - "name": "Volume 1, page 1v", - "zone": "b16736b8-48f5-48a5-a301-adbb689581cd", - "source": null, + "corpus": "61038d3b-9ab5-4bb8-bc1a-7ab296c6d00b", + "type": "770588c7-ff61-480a-8987-498d5f9da9f4", + "name": "Act 2", + "zone": null, "worker_version": null } }, { "model": "documents.element", - "pk": "e1020d75-d956-45a0-b933-909f3fc3d91c", + "pk": "d6565d2c-5fa1-43e8-ab27-08e8466036c2", "fields": { "created": "2020-02-02T01:23:45.678Z", "updated": "2020-02-02T01:23:45.678Z", - "corpus": "951ed1f8-9c0d-47ec-b406-79982dcbbce9", - "type": "3b9e0ba4-17ae-4881-a0e0-154bddeda913", - "name": "DATUM", - "zone": "3693c1f6-df38-4845-aead-8b7338ae3227", - "source": null, + "corpus": "61038d3b-9ab5-4bb8-bc1a-7ab296c6d00b", + "type": "770588c7-ff61-480a-8987-498d5f9da9f4", + "name": "Act 5", + "zone": null, "worker_version": null } }, { "model": "documents.element", - "pk": "e8dba6c7-7aa1-4cea-b12f-8aeb58c19da1", + "pk": "e449d58d-e5ec-43e5-bc71-f57aa9c31fdb", "fields": { "created": "2020-02-02T01:23:45.678Z", "updated": "2020-02-02T01:23:45.678Z", - "corpus": "951ed1f8-9c0d-47ec-b406-79982dcbbce9", - "type": "3b9e0ba4-17ae-4881-a0e0-154bddeda913", - "name": "ROY", - "zone": "3dfbc619-ba43-4765-abfe-8bb979fc19e2", - "source": null, + "corpus": "61038d3b-9ab5-4bb8-bc1a-7ab296c6d00b", + "type": "93f4311c-34dd-4642-849b-29ae386649c9", + "name": "Surface A", + "zone": "d9e801ab-b5c1-4c90-b117-8dc01ca5cb70", "worker_version": null } }, { "model": "documents.element", - "pk": "eb5447d7-b444-4818-81c6-3df409a2ff6d", + "pk": "e59f1432-9d0d-4161-a516-a31c41f02385", "fields": { "created": "2020-02-02T01:23:45.678Z", "updated": "2020-02-02T01:23:45.678Z", - "corpus": "951ed1f8-9c0d-47ec-b406-79982dcbbce9", - "type": "361fcb21-5c58-4d4f-b148-233e5087926a", - "name": "Act 2", + "corpus": "61038d3b-9ab5-4bb8-bc1a-7ab296c6d00b", + "type": "7a9610bd-1bf5-4215-aa06-9b5b281a6a8b", + "name": "Volume 2", "zone": null, - "source": null, "worker_version": null } }, { "model": "documents.element", - "pk": "eccfe352-bb4f-4dcd-8af5-cbf20ffb7c93", + "pk": "e9bed2af-85b9-4cbf-af6e-7b8cc273f148", "fields": { "created": "2020-02-02T01:23:45.678Z", "updated": "2020-02-02T01:23:45.678Z", - "corpus": "951ed1f8-9c0d-47ec-b406-79982dcbbce9", - "type": "b53547d0-3a8c-4ad8-b576-f8e539d1d485", - "name": "Surface F", - "zone": "07e721f0-e6f7-49ed-8703-60b7a67f4121", - "source": null, + "corpus": "61038d3b-9ab5-4bb8-bc1a-7ab296c6d00b", + "type": "19a91530-bb60-4055-b959-93bb4e08629b", + "name": "Text line", + "zone": "3983c6bf-358a-463f-be1e-d60723dc4e68", "worker_version": null } }, { "model": "documents.element", - "pk": "f8ec061e-1482-4653-aeb1-5db22f286553", + "pk": "f5144363-f6c7-4dfd-949b-5141fc5c50b6", "fields": { "created": "2020-02-02T01:23:45.678Z", "updated": "2020-02-02T01:23:45.678Z", - "corpus": "951ed1f8-9c0d-47ec-b406-79982dcbbce9", - "type": "361fcb21-5c58-4d4f-b148-233e5087926a", + "corpus": "61038d3b-9ab5-4bb8-bc1a-7ab296c6d00b", + "type": "770588c7-ff61-480a-8987-498d5f9da9f4", "name": "Act 4", "zone": null, - "source": null, "worker_version": null } }, { - "model": "documents.datasource", - "pk": "c06938f1-86a7-40e6-a5a3-7858e93552ac", - "fields": { - "type": "classifier", - "slug": "test", - "name": "Test Classifier", - "revision": "5.1", - "internal": false - } -}, -{ - "model": "documents.datasource", - "pk": "f04c984d-c801-48e5-a939-02b51065dd0a", + "model": "documents.element", + "pk": "fce8eb2a-aa2e-49d6-b875-514e6aae3c84", "fields": { - "type": "recognizer", - "slug": "test", - "name": "Test Recognizer", - "revision": "4.2", - "internal": false + "created": "2020-02-02T01:23:45.678Z", + "updated": "2020-02-02T01:23:45.678Z", + "corpus": "61038d3b-9ab5-4bb8-bc1a-7ab296c6d00b", + "type": "a0f4ce71-005e-4b9a-ab2c-dd1b3a82d543", + "name": "Volume 1, page 1r", + "zone": "81ae18b3-84ea-43c6-84fa-43e05e3b902d", + "worker_version": null } }, { "model": "documents.transcription", - "pk": "26e0ea53-61a1-4d14-a710-a269bfec5f2c", + "pk": "1346f638-ee49-4b02-8d25-fc668c886f8b", "fields": { - "element": "000f3a0b-0d42-44db-a120-a626f3a1e73b", - "source": "f04c984d-c801-48e5-a939-02b51065dd0a", - "worker_version": null, + "element": "21368a9b-b2a3-4146-b497-537256f7c6d1", + "worker_version": "9105c9e3-bf64-4c28-b50c-5307c32aa17b", "text": "PARIS", "score": 1.0 } }, { "model": "documents.transcription", - "pk": "7c29c382-c4de-47c6-8c67-54a5721a16cf", + "pk": "74977dd8-eaa2-43c6-8cd0-d19b7b6c436d", "fields": { - "element": "2d970951-a86a-46da-908e-7d5bc030ff21", - "source": "f04c984d-c801-48e5-a939-02b51065dd0a", - "worker_version": null, - "text": "PARIS", + "element": "8773bb0a-6820-467c-b616-53f6aa667c2e", + "worker_version": "9105c9e3-bf64-4c28-b50c-5307c32aa17b", + "text": "DATUM", "score": 1.0 } }, { "model": "documents.transcription", - "pk": "82f1938c-312e-48c7-85ee-e1d40cd9ecaa", + "pk": "8b5767cd-6305-4610-9538-810f49326afb", "fields": { - "element": "4ee16589-1cb0-4395-a185-79d89a88a106", - "source": "f04c984d-c801-48e5-a939-02b51065dd0a", - "worker_version": null, + "element": "4908e767-738d-4a53-beb1-b353e1762066", + "worker_version": "9105c9e3-bf64-4c28-b50c-5307c32aa17b", "text": "DATUM", "score": 1.0 } }, { "model": "documents.transcription", - "pk": "865610a1-ad8c-43bb-9822-73bccfa7ca75", + "pk": "96b11359-31b2-42ed-ae32-897067fc645f", "fields": { - "element": "7392707d-0b72-4f6b-9fb8-943b95ce197b", - "source": "f04c984d-c801-48e5-a939-02b51065dd0a", - "worker_version": null, + "element": "194e5073-57a3-4932-a5e2-74164c0fe5cf", + "worker_version": "9105c9e3-bf64-4c28-b50c-5307c32aa17b", "text": "DATUM", "score": 1.0 } }, { "model": "documents.transcription", - "pk": "871f08eb-9bc7-4c22-b3a7-e98ea1578f87", + "pk": "af9595b9-42f1-4a5b-9fb3-382c4150a29a", "fields": { - "element": "e8dba6c7-7aa1-4cea-b12f-8aeb58c19da1", - "source": "f04c984d-c801-48e5-a939-02b51065dd0a", - "worker_version": null, - "text": "ROY", + "element": "a6157f7c-38c9-4d01-b81f-9e8f995140b3", + "worker_version": "9105c9e3-bf64-4c28-b50c-5307c32aa17b", + "text": "PARIS", "score": 1.0 } }, { "model": "documents.transcription", - "pk": "a21f186b-f7d1-415a-b67c-5d065d197b40", + "pk": "bd81b2fc-0715-4bca-a1b1-c2ea1a4a56ae", "fields": { - "element": "1d8decb9-a27d-4e8c-82a9-e9fe3379790f", - "source": "f04c984d-c801-48e5-a939-02b51065dd0a", - "worker_version": null, - "text": "Lorem ipsum dolor sit amet", + "element": "6623e7e4-e41f-427b-936f-1308c4a2aac3", + "worker_version": "9105c9e3-bf64-4c28-b50c-5307c32aa17b", + "text": "ROY", "score": 1.0 } }, { "model": "documents.transcription", - "pk": "a35388dc-17a3-4e85-adfc-9ef1880cecb5", + "pk": "cd7c5e49-68cb-4f90-a66d-61afa39159e8", "fields": { - "element": "ce0c9320-9ea6-4ba7-81d9-42bbf410d40d", - "source": "f04c984d-c801-48e5-a939-02b51065dd0a", - "worker_version": null, + "element": "b210339b-aa36-40ae-8863-5f56904145ec", + "worker_version": "9105c9e3-bf64-4c28-b50c-5307c32aa17b", "text": "ROY", "score": 1.0 } }, { "model": "documents.transcription", - "pk": "afd22250-6568-4cb7-8120-6c485174eb05", + "pk": "da87c111-e2bd-4a22-8914-04fd1fb4e800", "fields": { - "element": "8e0cdc30-08e6-4509-9189-be7743e05fea", - "source": "f04c984d-c801-48e5-a939-02b51065dd0a", - "worker_version": null, + "element": "4490a0ec-366a-412b-9725-f28e00290657", + "worker_version": "9105c9e3-bf64-4c28-b50c-5307c32aa17b", "text": "PARIS", "score": 1.0 } }, { "model": "documents.transcription", - "pk": "bc0ad73f-bd3d-49b1-b971-823b18c9e734", + "pk": "f1fd7a88-d388-434b-8dab-89fd23767d85", "fields": { - "element": "82e3e1de-3498-462e-b0fa-294704e09871", - "source": "f04c984d-c801-48e5-a939-02b51065dd0a", - "worker_version": null, + "element": "20a97319-35d1-47d4-ad6f-c17278218c7f", + "worker_version": "9105c9e3-bf64-4c28-b50c-5307c32aa17b", "text": "ROY", "score": 1.0 } }, { "model": "documents.transcription", - "pk": "ebf68090-67bb-4310-a0ba-45cf5fc7e4ab", + "pk": "f8c95981-af03-4130-bad4-e94ffe722dae", "fields": { - "element": "e1020d75-d956-45a0-b933-909f3fc3d91c", - "source": "f04c984d-c801-48e5-a939-02b51065dd0a", - "worker_version": null, - "text": "DATUM", + "element": "fce8eb2a-aa2e-49d6-b875-514e6aae3c84", + "worker_version": "9105c9e3-bf64-4c28-b50c-5307c32aa17b", + "text": "Lorem ipsum dolor sit amet", "score": 1.0 } }, { "model": "documents.allowedmetadata", - "pk": "07b7c3de-0db9-4603-9cd3-e460da01bbce", + "pk": "614bc008-ff93-45dc-991a-593906fa78fa", "fields": { - "corpus": "951ed1f8-9c0d-47ec-b406-79982dcbbce9", - "type": "location", - "name": "location" + "corpus": "61038d3b-9ab5-4bb8-bc1a-7ab296c6d00b", + "type": "date", + "name": "date" } }, { "model": "documents.allowedmetadata", - "pk": "6ad1a69b-abe5-49c7-b654-735ad0c07e45", + "pk": "bc355db6-7afe-431a-9606-4800d6c2d808", "fields": { - "corpus": "951ed1f8-9c0d-47ec-b406-79982dcbbce9", + "corpus": "61038d3b-9ab5-4bb8-bc1a-7ab296c6d00b", "type": "text", "name": "folio" } }, { "model": "documents.allowedmetadata", - "pk": "d17e6e20-9f92-4d74-afdc-19c52122fb8c", + "pk": "c574e542-d459-49bf-bd7d-59db286966b4", "fields": { - "corpus": "951ed1f8-9c0d-47ec-b406-79982dcbbce9", - "type": "date", - "name": "date" + "corpus": "61038d3b-9ab5-4bb8-bc1a-7ab296c6d00b", + "type": "location", + "name": "location" } }, { "model": "documents.metadata", - "pk": "11a8cd94-eb2a-4c0a-b99b-5b8d18ad3e65", + "pk": "0da1fb0a-9d00-4816-91ee-386745077bc3", "fields": { - "element": "3b9c805e-d190-4bc7-a8b0-2fdae4b6ab9f", - "name": "number", + "element": "fce8eb2a-aa2e-49d6-b875-514e6aae3c84", + "name": "folio", "type": "text", - "value": "1", + "value": "1r", "revision": null, "index": 0, "entity": null @@ -988,12 +927,12 @@ }, { "model": "documents.metadata", - "pk": "1ec9b2f1-0a84-4242-ba45-af3f7b66e16a", + "pk": "1200d0f9-d99e-403d-846c-ec54c66b3a93", "fields": { - "element": "144551e9-cce8-42e8-bf58-636e7694ed87", - "name": "number", + "element": "0006d468-08e2-4855-96f6-9f71185a8d00", + "name": "folio", "type": "text", - "value": "3", + "value": "1v", "revision": null, "index": 0, "entity": null @@ -1001,9 +940,9 @@ }, { "model": "documents.metadata", - "pk": "31951d3d-e4b2-4bbc-a035-3c9e562b8e3d", + "pk": "1cffa1cb-78e0-4770-92e0-b98a88accd19", "fields": { - "element": "dc25f77d-167d-4762-b1ac-36ead49181f9", + "element": "820176ad-1230-4663-8e54-3b97556f927b", "name": "folio", "type": "text", "value": "1v", @@ -1014,12 +953,12 @@ }, { "model": "documents.metadata", - "pk": "67f70453-2bda-4b99-a49b-555af9e57cbb", + "pk": "31e89a59-1f9b-442d-9fe9-1130568f7f6b", "fields": { - "element": "ad406e77-3052-4840-a233-2f970db1a16c", - "name": "folio", + "element": "1edce450-7423-45dc-ae59-8c0e880b437b", + "name": "number", "type": "text", - "value": "1v", + "value": "1", "revision": null, "index": 0, "entity": null @@ -1027,9 +966,9 @@ }, { "model": "documents.metadata", - "pk": "80e41d3a-7fff-4b47-a95d-6b3b5e37296c", + "pk": "54769455-8f57-4295-8373-395b8c4b7315", "fields": { - "element": "d258e27f-ebff-40d2-984b-be7a44ce829e", + "element": "7ea0e102-a84d-4635-96ce-248ec0253867", "name": "folio", "type": "text", "value": "2r", @@ -1040,12 +979,12 @@ }, { "model": "documents.metadata", - "pk": "853d974a-408b-44d9-a721-9d04fccec63e", + "pk": "76c75283-d047-4941-9d1d-5550bc1bd05c", "fields": { - "element": "4168bf0e-e105-4597-82fb-d44f5f601c42", + "element": "f5144363-f6c7-4dfd-949b-5141fc5c50b6", "name": "number", "type": "text", - "value": "5", + "value": "4", "revision": null, "index": 0, "entity": null @@ -1053,12 +992,12 @@ }, { "model": "documents.metadata", - "pk": "99a0f6fd-f15b-404b-abf3-69bcdf33ae16", + "pk": "78d9053a-3747-4206-b63b-16fe00cd1d3d", "fields": { - "element": "1d8decb9-a27d-4e8c-82a9-e9fe3379790f", - "name": "folio", + "element": "c1ad5513-14f5-46de-9496-abf0645a7f20", + "name": "number", "type": "text", - "value": "1r", + "value": "2", "revision": null, "index": 0, "entity": null @@ -1066,12 +1005,12 @@ }, { "model": "documents.metadata", - "pk": "ae654dab-d5ad-4b0c-8171-9f134047c0c2", + "pk": "820aa005-eb1d-45b3-a57e-2bd3880939dc", "fields": { - "element": "f8ec061e-1482-4653-aeb1-5db22f286553", - "name": "number", + "element": "36368687-8b45-4137-8a4d-e81774a6b97f", + "name": "folio", "type": "text", - "value": "4", + "value": "2r", "revision": null, "index": 0, "entity": null @@ -1079,12 +1018,12 @@ }, { "model": "documents.metadata", - "pk": "bde6362d-4bab-4111-bd81-4f3bd1ba8299", + "pk": "85963c1e-150d-408d-8769-b39166e502d6", "fields": { - "element": "eb5447d7-b444-4818-81c6-3df409a2ff6d", + "element": "d6565d2c-5fa1-43e8-ab27-08e8466036c2", "name": "number", "type": "text", - "value": "2", + "value": "5", "revision": null, "index": 0, "entity": null @@ -1092,9 +1031,9 @@ }, { "model": "documents.metadata", - "pk": "d8be7701-4691-4f41-8f98-91a73a856240", + "pk": "ac7d595d-727a-492d-87d8-eb6c77e1abfb", "fields": { - "element": "ce6f78ce-0cfc-4cc3-8d4c-6a7b4fe68530", + "element": "bc9eee31-f209-474f-b9bf-403735f4b009", "name": "folio", "type": "text", "value": "1r", @@ -1105,12 +1044,12 @@ }, { "model": "documents.metadata", - "pk": "de5d9e23-4a65-4bd8-b2fe-669d3fcf78bf", + "pk": "dd3d1625-ebc3-4c11-888e-ce85bcff0678", "fields": { - "element": "195f1fe7-4abd-4582-a2d0-900254d71ecf", - "name": "folio", + "element": "b4a582ec-6084-4eff-961b-d4c37184fbe1", + "name": "number", "type": "text", - "value": "2r", + "value": "3", "revision": null, "index": 0, "entity": null @@ -1134,7 +1073,7 @@ }, { "model": "images.image", - "pk": "109c86ef-e898-410a-a141-86556c4b134a", + "pk": "03266c76-b3c5-451b-b490-d8c25aef0282", "fields": { "created": "2020-02-02T01:23:45.678Z", "updated": "2020-02-02T01:23:45.678Z", @@ -1148,12 +1087,12 @@ }, { "model": "images.image", - "pk": "4db60629-45d7-4932-b710-ace0604a99ff", + "pk": "14858e6d-ff16-4747-94d3-0fd2ea5eaea6", "fields": { "created": "2020-02-02T01:23:45.678Z", "updated": "2020-02-02T01:23:45.678Z", "server": 1, - "path": "img4", + "path": "img1", "width": 1000, "height": 1000, "hash": null, @@ -1162,12 +1101,12 @@ }, { "model": "images.image", - "pk": "5aa4052e-09c8-4bf3-8d01-5e00c4f7aa7d", + "pk": "549c7d01-90b3-4d92-a561-d1486bb63881", "fields": { "created": "2020-02-02T01:23:45.678Z", "updated": "2020-02-02T01:23:45.678Z", "server": 1, - "path": "img1", + "path": "img4", "width": 1000, "height": 1000, "hash": null, @@ -1176,7 +1115,7 @@ }, { "model": "images.image", - "pk": "97bbe1d5-82c1-4179-8acd-4647b3e7aeef", + "pk": "91448dc5-08f5-484c-8bae-67123e01edef", "fields": { "created": "2020-02-02T01:23:45.678Z", "updated": "2020-02-02T01:23:45.678Z", @@ -1190,12 +1129,12 @@ }, { "model": "images.image", - "pk": "e66d5fc9-3c52-4c71-bddc-86617b530e0d", + "pk": "aa5f1357-163c-4886-b50e-85bb74c32716", "fields": { "created": "2020-02-02T01:23:45.678Z", "updated": "2020-02-02T01:23:45.678Z", "server": 1, - "path": "img5", + "path": "img6", "width": 1000, "height": 1000, "hash": null, @@ -1204,12 +1143,12 @@ }, { "model": "images.image", - "pk": "fabf9757-1bfc-4e4f-b374-dcbc9b1ac585", + "pk": "faf7d110-63ce-4306-ac09-86f5ff81ac48", "fields": { "created": "2020-02-02T01:23:45.678Z", "updated": "2020-02-02T01:23:45.678Z", "server": 1, - "path": "img6", + "path": "img5", "width": 1000, "height": 1000, "hash": null, @@ -1218,209 +1157,209 @@ }, { "model": "images.zone", - "pk": "079016a5-09c2-4788-923a-b60954b3da76", + "pk": "095dd88c-ca66-4235-bb9e-7519b1908278", "fields": { "created": "2020-02-02T01:23:45.678Z", "updated": "2020-02-02T01:23:45.678Z", - "image": "e66d5fc9-3c52-4c71-bddc-86617b530e0d", - "polygon": "LINEARRING (0 0, 0 1000, 1000 1000, 1000 0, 0 0)" + "image": "03266c76-b3c5-451b-b490-d8c25aef0282", + "polygon": "LINEARRING (400 400, 400 500, 500 500, 500 400, 400 400)" } }, { "model": "images.zone", - "pk": "07e721f0-e6f7-49ed-8703-60b7a67f4121", + "pk": "10a1572e-5a70-4e07-a556-d51be314bc8b", "fields": { "created": "2020-02-02T01:23:45.678Z", "updated": "2020-02-02T01:23:45.678Z", - "image": "97bbe1d5-82c1-4179-8acd-4647b3e7aeef", - "polygon": "LINEARRING (600 600, 600 1000, 1000 1000, 1000 600, 600 600)" + "image": "91448dc5-08f5-484c-8bae-67123e01edef", + "polygon": "LINEARRING (100 100, 100 200, 200 200, 200 100, 100 100)" } }, { "model": "images.zone", - "pk": "13f4e60d-5e26-4a51-aa70-e193e26dfac8", + "pk": "120ef74e-0f81-40d4-9d70-25e3d1a598aa", "fields": { "created": "2020-02-02T01:23:45.678Z", "updated": "2020-02-02T01:23:45.678Z", - "image": "97bbe1d5-82c1-4179-8acd-4647b3e7aeef", - "polygon": "LINEARRING (0 0, 0 300, 300 300, 300 0, 0 0)" + "image": "14858e6d-ff16-4747-94d3-0fd2ea5eaea6", + "polygon": "LINEARRING (700 700, 700 800, 800 800, 800 700, 700 700)" } }, { "model": "images.zone", - "pk": "32876cbc-cb85-4c31-9a6e-b743eb3cab8f", + "pk": "135fafc6-620c-4aee-a714-38b1ae69eacb", "fields": { "created": "2020-02-02T01:23:45.678Z", "updated": "2020-02-02T01:23:45.678Z", - "image": "5aa4052e-09c8-4bf3-8d01-5e00c4f7aa7d", - "polygon": "LINEARRING (100 100, 100 200, 200 200, 200 100, 100 100)" + "image": "91448dc5-08f5-484c-8bae-67123e01edef", + "polygon": "LINEARRING (0 0, 0 1000, 1000 1000, 1000 0, 0 0)" } }, { "model": "images.zone", - "pk": "3693c1f6-df38-4845-aead-8b7338ae3227", + "pk": "1e8433b0-cee0-4207-a310-75c7ea1145dc", "fields": { "created": "2020-02-02T01:23:45.678Z", "updated": "2020-02-02T01:23:45.678Z", - "image": "109c86ef-e898-410a-a141-86556c4b134a", - "polygon": "LINEARRING (700 700, 700 800, 800 800, 800 700, 700 700)" + "image": "549c7d01-90b3-4d92-a561-d1486bb63881", + "polygon": "LINEARRING (0 0, 0 1000, 1000 1000, 1000 0, 0 0)" } }, { "model": "images.zone", - "pk": "3dfbc619-ba43-4765-abfe-8bb979fc19e2", + "pk": "364c4c7c-b1c9-45ea-a790-8fc6cec96965", "fields": { "created": "2020-02-02T01:23:45.678Z", "updated": "2020-02-02T01:23:45.678Z", - "image": "109c86ef-e898-410a-a141-86556c4b134a", - "polygon": "LINEARRING (400 400, 400 500, 500 500, 500 400, 400 400)" + "image": "91448dc5-08f5-484c-8bae-67123e01edef", + "polygon": "LINEARRING (0 0, 0 300, 300 300, 300 0, 0 0)" } }, { "model": "images.zone", - "pk": "41b62beb-9aa4-4c99-9820-a7d8e0fc3f62", + "pk": "3983c6bf-358a-463f-be1e-d60723dc4e68", "fields": { "created": "2020-02-02T01:23:45.678Z", "updated": "2020-02-02T01:23:45.678Z", - "image": "5aa4052e-09c8-4bf3-8d01-5e00c4f7aa7d", - "polygon": "LINEARRING (0 0, 0 600, 600 600, 600 0, 0 0)" + "image": "14858e6d-ff16-4747-94d3-0fd2ea5eaea6", + "polygon": "LINEARRING (400 400, 400 500, 500 500, 500 400, 400 400)" } }, { "model": "images.zone", - "pk": "493954e4-91f7-4f5d-bb1d-f58acf3ad4d1", + "pk": "4cf441c6-3609-4f1c-80bb-23688e4fa62d", "fields": { "created": "2020-02-02T01:23:45.678Z", "updated": "2020-02-02T01:23:45.678Z", - "image": "97bbe1d5-82c1-4179-8acd-4647b3e7aeef", - "polygon": "LINEARRING (0 0, 0 1000, 1000 1000, 1000 0, 0 0)" + "image": "91448dc5-08f5-484c-8bae-67123e01edef", + "polygon": "LINEARRING (300 300, 300 600, 600 600, 600 300, 300 300)" } }, { "model": "images.zone", - "pk": "5dec929a-288d-4d17-9912-5344842d0eac", + "pk": "64212529-490d-452a-ac8c-11da57d2305a", "fields": { "created": "2020-02-02T01:23:45.678Z", "updated": "2020-02-02T01:23:45.678Z", - "image": "5aa4052e-09c8-4bf3-8d01-5e00c4f7aa7d", - "polygon": "LINEARRING (0 0, 0 1000, 1000 1000, 1000 0, 0 0)" + "image": "91448dc5-08f5-484c-8bae-67123e01edef", + "polygon": "LINEARRING (400 400, 400 500, 500 500, 500 400, 400 400)" } }, { "model": "images.zone", - "pk": "7418424e-f978-4554-b4cd-56c96c4196ca", + "pk": "81ae18b3-84ea-43c6-84fa-43e05e3b902d", "fields": { "created": "2020-02-02T01:23:45.678Z", "updated": "2020-02-02T01:23:45.678Z", - "image": "5aa4052e-09c8-4bf3-8d01-5e00c4f7aa7d", - "polygon": "LINEARRING (700 700, 700 800, 800 800, 800 700, 700 700)" + "image": "14858e6d-ff16-4747-94d3-0fd2ea5eaea6", + "polygon": "LINEARRING (0 0, 0 1000, 1000 1000, 1000 0, 0 0)" } }, { "model": "images.zone", - "pk": "ad779382-ad03-4aac-a0fb-e107ede8faf5", + "pk": "85436981-a019-45d7-bef6-0d1cb79f0849", "fields": { "created": "2020-02-02T01:23:45.678Z", "updated": "2020-02-02T01:23:45.678Z", - "image": "109c86ef-e898-410a-a141-86556c4b134a", + "image": "14858e6d-ff16-4747-94d3-0fd2ea5eaea6", "polygon": "LINEARRING (100 100, 100 200, 200 200, 200 100, 100 100)" } }, { "model": "images.zone", - "pk": "adf62f88-2860-49e8-82df-4a76c8c9f2e3", + "pk": "87ac302e-3b42-4672-827f-d2b80bbeaec8", "fields": { "created": "2020-02-02T01:23:45.678Z", "updated": "2020-02-02T01:23:45.678Z", - "image": "5aa4052e-09c8-4bf3-8d01-5e00c4f7aa7d", - "polygon": "LINEARRING (600 600, 600 1000, 1000 1000, 1000 600, 600 600)" + "image": "03266c76-b3c5-451b-b490-d8c25aef0282", + "polygon": "LINEARRING (0 0, 0 1000, 1000 1000, 1000 0, 0 0)" } }, { "model": "images.zone", - "pk": "ae266608-3037-4612-9073-f2320c755e70", + "pk": "96427f35-175d-49f1-8443-06b3b840aa93", "fields": { "created": "2020-02-02T01:23:45.678Z", "updated": "2020-02-02T01:23:45.678Z", - "image": "5aa4052e-09c8-4bf3-8d01-5e00c4f7aa7d", - "polygon": "LINEARRING (400 400, 400 500, 500 500, 500 400, 400 400)" + "image": "faf7d110-63ce-4306-ac09-86f5ff81ac48", + "polygon": "LINEARRING (0 0, 0 1000, 1000 1000, 1000 0, 0 0)" } }, { "model": "images.zone", - "pk": "b16736b8-48f5-48a5-a301-adbb689581cd", + "pk": "9be7fa31-5ede-450a-80c1-de9f683ff2bf", "fields": { "created": "2020-02-02T01:23:45.678Z", "updated": "2020-02-02T01:23:45.678Z", - "image": "109c86ef-e898-410a-a141-86556c4b134a", - "polygon": "LINEARRING (0 0, 0 1000, 1000 1000, 1000 0, 0 0)" + "image": "03266c76-b3c5-451b-b490-d8c25aef0282", + "polygon": "LINEARRING (100 100, 100 200, 200 200, 200 100, 100 100)" } }, { "model": "images.zone", - "pk": "b9e8a058-a124-443d-825a-05e60e1a29ee", + "pk": "a1de4f44-76e3-453e-aeab-5c65ba035b14", "fields": { "created": "2020-02-02T01:23:45.678Z", "updated": "2020-02-02T01:23:45.678Z", - "image": "97bbe1d5-82c1-4179-8acd-4647b3e7aeef", - "polygon": "LINEARRING (400 400, 400 500, 500 500, 500 400, 400 400)" + "image": "aa5f1357-163c-4886-b50e-85bb74c32716", + "polygon": "LINEARRING (0 0, 0 1000, 1000 1000, 1000 0, 0 0)" } }, { "model": "images.zone", - "pk": "bddb803a-c25b-43b0-ab03-880af6568397", + "pk": "baf7cc76-08ed-484b-bc46-8e9fddadcf2c", "fields": { "created": "2020-02-02T01:23:45.678Z", "updated": "2020-02-02T01:23:45.678Z", - "image": "97bbe1d5-82c1-4179-8acd-4647b3e7aeef", - "polygon": "LINEARRING (100 100, 100 200, 200 200, 200 100, 100 100)" + "image": "03266c76-b3c5-451b-b490-d8c25aef0282", + "polygon": "LINEARRING (700 700, 700 800, 800 800, 800 700, 700 700)" } }, { "model": "images.zone", - "pk": "ef5fead8-f5dd-44c6-9da7-4bd05cf5c695", + "pk": "bd1fee55-df88-4859-9371-f73765f281bc", "fields": { "created": "2020-02-02T01:23:45.678Z", "updated": "2020-02-02T01:23:45.678Z", - "image": "fabf9757-1bfc-4e4f-b374-dcbc9b1ac585", - "polygon": "LINEARRING (0 0, 0 1000, 1000 1000, 1000 0, 0 0)" + "image": "91448dc5-08f5-484c-8bae-67123e01edef", + "polygon": "LINEARRING (700 700, 700 800, 800 800, 800 700, 700 700)" } }, { "model": "images.zone", - "pk": "fcc13de0-6c9e-49ce-8524-8112c7aa454c", + "pk": "ca73fe53-829a-4c55-8223-fbaaf4dff10c", "fields": { "created": "2020-02-02T01:23:45.678Z", "updated": "2020-02-02T01:23:45.678Z", - "image": "97bbe1d5-82c1-4179-8acd-4647b3e7aeef", - "polygon": "LINEARRING (700 700, 700 800, 800 800, 800 700, 700 700)" + "image": "91448dc5-08f5-484c-8bae-67123e01edef", + "polygon": "LINEARRING (600 600, 600 1000, 1000 1000, 1000 600, 600 600)" } }, { "model": "images.zone", - "pk": "ff3fc33f-4a14-4e2d-8c78-69c627952aef", + "pk": "d9e801ab-b5c1-4c90-b117-8dc01ca5cb70", "fields": { "created": "2020-02-02T01:23:45.678Z", "updated": "2020-02-02T01:23:45.678Z", - "image": "4db60629-45d7-4932-b710-ace0604a99ff", - "polygon": "LINEARRING (0 0, 0 1000, 1000 1000, 1000 0, 0 0)" + "image": "14858e6d-ff16-4747-94d3-0fd2ea5eaea6", + "polygon": "LINEARRING (0 0, 0 600, 600 600, 600 0, 0 0)" } }, { "model": "images.zone", - "pk": "ffc2612e-481b-423f-a902-3538d29542de", + "pk": "f69de5e8-01d7-42c1-b487-53b6821f3fcf", "fields": { "created": "2020-02-02T01:23:45.678Z", "updated": "2020-02-02T01:23:45.678Z", - "image": "97bbe1d5-82c1-4179-8acd-4647b3e7aeef", - "polygon": "LINEARRING (300 300, 300 600, 600 600, 600 300, 300 300)" + "image": "14858e6d-ff16-4747-94d3-0fd2ea5eaea6", + "polygon": "LINEARRING (600 600, 600 1000, 1000 1000, 1000 600, 600 600)" } }, { "model": "users.user", "pk": 1, "fields": { - "password": "pbkdf2_sha256$216000$WzKWfycXMEFX$AytsezJmnXNWhDh3P7ko1Y/jhRGWISGPhDD1FUxtsPM=", + "password": "pbkdf2_sha256$216000$0hu6ZxnFs7n3$wlIDMfym+3BOs2F4Xs15cwrSsOHz+iDAlTc29RI5U1Y=", "last_login": null, "email": "root@root.fr", "display_name": "Admin", @@ -1435,7 +1374,7 @@ "model": "users.user", "pk": 2, "fields": { - "password": "pbkdf2_sha256$216000$SgEz2rqLb3sp$vVHBOzpAuI9KQZajsa541ACnWd7rnk2oAoDRCZ2NILg=", + "password": "pbkdf2_sha256$216000$DcCSx9mAJWwU$hi15eit74BQg3arHgnNEVQmYf2hPT8ravUVZezBXNcI=", "last_login": null, "email": "internal@internal.fr", "display_name": "Internal user", @@ -1450,7 +1389,7 @@ "model": "users.user", "pk": 3, "fields": { - "password": "pbkdf2_sha256$216000$WMidgXB71uY1$eKhRaj459aaoyBTtT/SmiQou7WS+MhV7jkTUjVIzdBU=", + "password": "pbkdf2_sha256$216000$YxK4xLB0kuxf$eThETFXjFddp2mhIKsDNJFIwsgmjda+TBhDN0GNabrs=", "last_login": null, "email": "user@user.fr", "display_name": "Test user", @@ -1493,7 +1432,7 @@ }, { "model": "users.group", - "pk": "6e2ca3c1-c24d-44b2-a207-f88e8c9ceb26", + "pk": "d06d166a-1c95-4968-a147-350ca1bb153d", "fields": { "name": "User group", "public": false @@ -1501,34 +1440,34 @@ }, { "model": "users.membership", - "pk": "252797d5-9dc8-43a3-a790-2bb643dfd6b3", + "pk": "0fe5261c-370d-409a-8474-53c958916492", "fields": { - "user": 5, - "group": "6e2ca3c1-c24d-44b2-a207-f88e8c9ceb26", - "level": 10 + "user": 3, + "group": "d06d166a-1c95-4968-a147-350ca1bb153d", + "level": 100 } }, { "model": "users.membership", - "pk": "661e312b-f8a5-4557-bba2-84286a208fe5", + "pk": "1b63da0d-5fc7-46b8-80cc-c50582c2547e", "fields": { - "user": 3, - "group": "6e2ca3c1-c24d-44b2-a207-f88e8c9ceb26", - "level": 100 + "user": 5, + "group": "d06d166a-1c95-4968-a147-350ca1bb153d", + "level": 10 } }, { "model": "users.membership", - "pk": "88dc4c4b-0f2a-48b2-a02b-ea7f22ced745", + "pk": "ce477d6a-e07b-436d-8345-f6f802edfd8f", "fields": { "user": 4, - "group": "6e2ca3c1-c24d-44b2-a207-f88e8c9ceb26", + "group": "d06d166a-1c95-4968-a147-350ca1bb153d", "level": 50 } }, { "model": "users.oauthcredentials", - "pk": "c3a461a3-01f8-4c69-905e-0ca37f244d7e", + "pk": "e87ff20b-d28f-42f9-b846-b3de14ac0e27", "fields": { "user": 3, "provider_name": "gitlab", @@ -1545,7 +1484,7 @@ "pk": 1, "fields": { "user": 3, - "corpus": "951ed1f8-9c0d-47ec-b406-79982dcbbce9", + "corpus": "61038d3b-9ab5-4bb8-bc1a-7ab296c6d00b", "can_write": true, "can_admin": true } @@ -2274,977 +2213,941 @@ "model": "auth.permission", "pk": 81, "fields": { - "name": "Can add data source", + "name": "Can add element", "content_type": 21, - "codename": "add_datasource" + "codename": "add_element" } }, { "model": "auth.permission", "pk": 82, "fields": { - "name": "Can change data source", + "name": "Can change element", "content_type": 21, - "codename": "change_datasource" - } -}, -{ - "model": "auth.permission", - "pk": 83, - "fields": { - "name": "Can delete data source", - "content_type": 21, - "codename": "delete_datasource" - } -}, -{ - "model": "auth.permission", - "pk": 84, - "fields": { - "name": "Can view data source", - "content_type": 21, - "codename": "view_datasource" - } -}, -{ - "model": "auth.permission", - "pk": 85, - "fields": { - "name": "Can add element", - "content_type": 22, - "codename": "add_element" - } -}, -{ - "model": "auth.permission", - "pk": 86, - "fields": { - "name": "Can change element", - "content_type": 22, "codename": "change_element" } }, { "model": "auth.permission", - "pk": 87, + "pk": 83, "fields": { "name": "Can delete element", - "content_type": 22, + "content_type": 21, "codename": "delete_element" } }, { "model": "auth.permission", - "pk": 88, + "pk": 84, "fields": { "name": "Can view element", - "content_type": 22, + "content_type": 21, "codename": "view_element" } }, { "model": "auth.permission", - "pk": 89, + "pk": 85, "fields": { "name": "Can add element path", - "content_type": 23, + "content_type": 22, "codename": "add_elementpath" } }, { "model": "auth.permission", - "pk": 90, + "pk": 86, "fields": { "name": "Can change element path", - "content_type": 23, + "content_type": 22, "codename": "change_elementpath" } }, { "model": "auth.permission", - "pk": 91, + "pk": 87, "fields": { "name": "Can delete element path", - "content_type": 23, + "content_type": 22, "codename": "delete_elementpath" } }, { "model": "auth.permission", - "pk": 92, + "pk": 88, "fields": { "name": "Can view element path", - "content_type": 23, + "content_type": 22, "codename": "view_elementpath" } }, { "model": "auth.permission", - "pk": 93, + "pk": 89, "fields": { "name": "Can add element type", - "content_type": 24, + "content_type": 23, "codename": "add_elementtype" } }, { "model": "auth.permission", - "pk": 94, + "pk": 90, "fields": { "name": "Can change element type", - "content_type": 24, + "content_type": 23, "codename": "change_elementtype" } }, { "model": "auth.permission", - "pk": 95, + "pk": 91, "fields": { "name": "Can delete element type", - "content_type": 24, + "content_type": 23, "codename": "delete_elementtype" } }, { "model": "auth.permission", - "pk": 96, + "pk": 92, "fields": { "name": "Can view element type", - "content_type": 24, + "content_type": 23, "codename": "view_elementtype" } }, { "model": "auth.permission", - "pk": 97, + "pk": 93, "fields": { "name": "Can add entity", - "content_type": 25, + "content_type": 24, "codename": "add_entity" } }, { "model": "auth.permission", - "pk": 98, + "pk": 94, "fields": { "name": "Can change entity", - "content_type": 25, + "content_type": 24, "codename": "change_entity" } }, { "model": "auth.permission", - "pk": 99, + "pk": 95, "fields": { "name": "Can delete entity", - "content_type": 25, + "content_type": 24, "codename": "delete_entity" } }, { "model": "auth.permission", - "pk": 100, + "pk": 96, "fields": { "name": "Can view entity", - "content_type": 25, + "content_type": 24, "codename": "view_entity" } }, { "model": "auth.permission", - "pk": 101, + "pk": 97, "fields": { "name": "Can add ml class", - "content_type": 26, + "content_type": 25, "codename": "add_mlclass" } }, { "model": "auth.permission", - "pk": 102, + "pk": 98, "fields": { "name": "Can change ml class", - "content_type": 26, + "content_type": 25, "codename": "change_mlclass" } }, { "model": "auth.permission", - "pk": 103, + "pk": 99, "fields": { "name": "Can delete ml class", - "content_type": 26, + "content_type": 25, "codename": "delete_mlclass" } }, { "model": "auth.permission", - "pk": 104, + "pk": 100, "fields": { "name": "Can view ml class", - "content_type": 26, + "content_type": 25, "codename": "view_mlclass" } }, { "model": "auth.permission", - "pk": 105, + "pk": 101, "fields": { "name": "Can add transcription", - "content_type": 27, + "content_type": 26, "codename": "add_transcription" } }, { "model": "auth.permission", - "pk": 106, + "pk": 102, "fields": { "name": "Can change transcription", - "content_type": 27, + "content_type": 26, "codename": "change_transcription" } }, { "model": "auth.permission", - "pk": 107, + "pk": 103, "fields": { "name": "Can delete transcription", - "content_type": 27, + "content_type": 26, "codename": "delete_transcription" } }, { "model": "auth.permission", - "pk": 108, + "pk": 104, "fields": { "name": "Can view transcription", - "content_type": 27, + "content_type": 26, "codename": "view_transcription" } }, { "model": "auth.permission", - "pk": 109, + "pk": 105, "fields": { "name": "Can add transcription entity", - "content_type": 28, + "content_type": 27, "codename": "add_transcriptionentity" } }, { "model": "auth.permission", - "pk": 110, + "pk": 106, "fields": { "name": "Can change transcription entity", - "content_type": 28, + "content_type": 27, "codename": "change_transcriptionentity" } }, { "model": "auth.permission", - "pk": 111, + "pk": 107, "fields": { "name": "Can delete transcription entity", - "content_type": 28, + "content_type": 27, "codename": "delete_transcriptionentity" } }, { "model": "auth.permission", - "pk": 112, + "pk": 108, "fields": { "name": "Can view transcription entity", - "content_type": 28, + "content_type": 27, "codename": "view_transcriptionentity" } }, { "model": "auth.permission", - "pk": 113, + "pk": 109, "fields": { "name": "Can add meta data", - "content_type": 29, + "content_type": 28, "codename": "add_metadata" } }, { "model": "auth.permission", - "pk": 114, + "pk": 110, "fields": { "name": "Can change meta data", - "content_type": 29, + "content_type": 28, "codename": "change_metadata" } }, { "model": "auth.permission", - "pk": 115, + "pk": 111, "fields": { "name": "Can delete meta data", - "content_type": 29, + "content_type": 28, "codename": "delete_metadata" } }, { "model": "auth.permission", - "pk": 116, + "pk": 112, "fields": { "name": "Can view meta data", - "content_type": 29, + "content_type": 28, "codename": "view_metadata" } }, { "model": "auth.permission", - "pk": 117, + "pk": 113, "fields": { "name": "Can add entity role", - "content_type": 30, + "content_type": 29, "codename": "add_entityrole" } }, { "model": "auth.permission", - "pk": 118, + "pk": 114, "fields": { "name": "Can change entity role", - "content_type": 30, + "content_type": 29, "codename": "change_entityrole" } }, { "model": "auth.permission", - "pk": 119, + "pk": 115, "fields": { "name": "Can delete entity role", - "content_type": 30, + "content_type": 29, "codename": "delete_entityrole" } }, { "model": "auth.permission", - "pk": 120, + "pk": 116, "fields": { "name": "Can view entity role", - "content_type": 30, + "content_type": 29, "codename": "view_entityrole" } }, { "model": "auth.permission", - "pk": 121, + "pk": 117, "fields": { "name": "Can add entity link", - "content_type": 31, + "content_type": 30, "codename": "add_entitylink" } }, { "model": "auth.permission", - "pk": 122, + "pk": 118, "fields": { "name": "Can change entity link", - "content_type": 31, + "content_type": 30, "codename": "change_entitylink" } }, { "model": "auth.permission", - "pk": 123, + "pk": 119, "fields": { "name": "Can delete entity link", - "content_type": 31, + "content_type": 30, "codename": "delete_entitylink" } }, { "model": "auth.permission", - "pk": 124, + "pk": 120, "fields": { "name": "Can view entity link", - "content_type": 31, + "content_type": 30, "codename": "view_entitylink" } }, { "model": "auth.permission", - "pk": 125, + "pk": 121, "fields": { "name": "Can add selection", - "content_type": 32, + "content_type": 31, "codename": "add_selection" } }, { "model": "auth.permission", - "pk": 126, + "pk": 122, "fields": { "name": "Can change selection", - "content_type": 32, + "content_type": 31, "codename": "change_selection" } }, { "model": "auth.permission", - "pk": 127, + "pk": 123, "fields": { "name": "Can delete selection", - "content_type": 32, + "content_type": 31, "codename": "delete_selection" } }, { "model": "auth.permission", - "pk": 128, + "pk": 124, "fields": { "name": "Can view selection", - "content_type": 32, + "content_type": 31, "codename": "view_selection" } }, { "model": "auth.permission", - "pk": 129, + "pk": 125, "fields": { "name": "Can add user", - "content_type": 33, + "content_type": 32, "codename": "add_user" } }, { "model": "auth.permission", - "pk": 130, + "pk": 126, "fields": { "name": "Can change user", - "content_type": 33, + "content_type": 32, "codename": "change_user" } }, { "model": "auth.permission", - "pk": 131, + "pk": 127, "fields": { "name": "Can delete user", - "content_type": 33, + "content_type": 32, "codename": "delete_user" } }, { "model": "auth.permission", - "pk": 132, + "pk": 128, "fields": { "name": "Can view user", - "content_type": 33, + "content_type": 32, "codename": "view_user" } }, { "model": "auth.permission", - "pk": 133, + "pk": 129, "fields": { "name": "Can add o auth credentials", - "content_type": 34, + "content_type": 33, "codename": "add_oauthcredentials" } }, { "model": "auth.permission", - "pk": 134, + "pk": 130, "fields": { "name": "Can change o auth credentials", - "content_type": 34, + "content_type": 33, "codename": "change_oauthcredentials" } }, { "model": "auth.permission", - "pk": 135, + "pk": 131, "fields": { "name": "Can delete o auth credentials", - "content_type": 34, + "content_type": 33, "codename": "delete_oauthcredentials" } }, { "model": "auth.permission", - "pk": 136, + "pk": 132, "fields": { "name": "Can view o auth credentials", - "content_type": 34, + "content_type": 33, "codename": "view_oauthcredentials" } }, { "model": "auth.permission", - "pk": 137, + "pk": 133, "fields": { "name": "Can add corpus right", - "content_type": 35, + "content_type": 34, "codename": "add_corpusright" } }, { "model": "auth.permission", - "pk": 138, + "pk": 134, "fields": { "name": "Can change corpus right", - "content_type": 35, + "content_type": 34, "codename": "change_corpusright" } }, { "model": "auth.permission", - "pk": 139, + "pk": 135, "fields": { "name": "Can delete corpus right", - "content_type": 35, + "content_type": 34, "codename": "delete_corpusright" } }, { "model": "auth.permission", - "pk": 140, + "pk": 136, "fields": { "name": "Can view corpus right", - "content_type": 35, + "content_type": 34, "codename": "view_corpusright" } }, { "model": "auth.permission", - "pk": 141, + "pk": 137, "fields": { "name": "Can add user scope", - "content_type": 36, + "content_type": 35, "codename": "add_userscope" } }, { "model": "auth.permission", - "pk": 142, + "pk": 138, "fields": { "name": "Can change user scope", - "content_type": 36, + "content_type": 35, "codename": "change_userscope" } }, { "model": "auth.permission", - "pk": 143, + "pk": 139, "fields": { "name": "Can delete user scope", - "content_type": 36, + "content_type": 35, "codename": "delete_userscope" } }, { "model": "auth.permission", - "pk": 144, + "pk": 140, "fields": { "name": "Can view user scope", - "content_type": 36, + "content_type": 35, "codename": "view_userscope" } }, { "model": "auth.permission", - "pk": 145, + "pk": 141, "fields": { "name": "Can add group", - "content_type": 37, + "content_type": 36, "codename": "add_group" } }, { "model": "auth.permission", - "pk": 146, + "pk": 142, "fields": { "name": "Can change group", - "content_type": 37, + "content_type": 36, "codename": "change_group" } }, { "model": "auth.permission", - "pk": 147, + "pk": 143, "fields": { "name": "Can delete group", - "content_type": 37, + "content_type": 36, "codename": "delete_group" } }, { "model": "auth.permission", - "pk": 148, + "pk": 144, "fields": { "name": "Can view group", - "content_type": 37, + "content_type": 36, "codename": "view_group" } }, { "model": "auth.permission", - "pk": 149, + "pk": 145, "fields": { "name": "Can add membership", - "content_type": 38, + "content_type": 37, "codename": "add_membership" } }, { "model": "auth.permission", - "pk": 150, + "pk": 146, "fields": { "name": "Can change membership", - "content_type": 38, + "content_type": 37, "codename": "change_membership" } }, { "model": "auth.permission", - "pk": 151, + "pk": 147, "fields": { "name": "Can delete membership", - "content_type": 38, + "content_type": 37, "codename": "delete_membership" } }, { "model": "auth.permission", - "pk": 152, + "pk": 148, "fields": { "name": "Can view membership", - "content_type": 38, + "content_type": 37, "codename": "view_membership" } }, { "model": "auth.permission", - "pk": 153, + "pk": 149, "fields": { "name": "Can add data file", - "content_type": 39, + "content_type": 38, "codename": "add_datafile" } }, { "model": "auth.permission", - "pk": 154, + "pk": 150, "fields": { "name": "Can change data file", - "content_type": 39, + "content_type": 38, "codename": "change_datafile" } }, { "model": "auth.permission", - "pk": 155, + "pk": 151, "fields": { "name": "Can delete data file", - "content_type": 39, + "content_type": 38, "codename": "delete_datafile" } }, { "model": "auth.permission", - "pk": 156, + "pk": 152, "fields": { "name": "Can view data file", - "content_type": 39, + "content_type": 38, "codename": "view_datafile" } }, { "model": "auth.permission", - "pk": 157, + "pk": 153, "fields": { "name": "Can add data import", - "content_type": 40, + "content_type": 39, "codename": "add_dataimport" } }, { "model": "auth.permission", - "pk": 158, + "pk": 154, "fields": { "name": "Can change data import", - "content_type": 40, + "content_type": 39, "codename": "change_dataimport" } }, { "model": "auth.permission", - "pk": 159, + "pk": 155, "fields": { "name": "Can delete data import", - "content_type": 40, + "content_type": 39, "codename": "delete_dataimport" } }, { "model": "auth.permission", - "pk": 160, + "pk": 156, "fields": { "name": "Can view data import", - "content_type": 40, + "content_type": 39, "codename": "view_dataimport" } }, { "model": "auth.permission", - "pk": 161, + "pk": 157, "fields": { "name": "Can add repository", - "content_type": 41, + "content_type": 40, "codename": "add_repository" } }, { "model": "auth.permission", - "pk": 162, + "pk": 158, "fields": { "name": "Can change repository", - "content_type": 41, + "content_type": 40, "codename": "change_repository" } }, { "model": "auth.permission", - "pk": 163, + "pk": 159, "fields": { "name": "Can delete repository", - "content_type": 41, + "content_type": 40, "codename": "delete_repository" } }, { "model": "auth.permission", - "pk": 164, + "pk": 160, "fields": { "name": "Can view repository", - "content_type": 41, + "content_type": 40, "codename": "view_repository" } }, { "model": "auth.permission", - "pk": 165, + "pk": 161, "fields": { "name": "Can add revision", - "content_type": 42, + "content_type": 41, "codename": "add_revision" } }, { "model": "auth.permission", - "pk": 166, + "pk": 162, "fields": { "name": "Can change revision", - "content_type": 42, + "content_type": 41, "codename": "change_revision" } }, { "model": "auth.permission", - "pk": 167, + "pk": 163, "fields": { "name": "Can delete revision", - "content_type": 42, + "content_type": 41, "codename": "delete_revision" } }, { "model": "auth.permission", - "pk": 168, + "pk": 164, "fields": { "name": "Can view revision", - "content_type": 42, + "content_type": 41, "codename": "view_revision" } }, { "model": "auth.permission", - "pk": 169, + "pk": 165, "fields": { "name": "Can add worker", - "content_type": 43, + "content_type": 42, "codename": "add_worker" } }, { "model": "auth.permission", - "pk": 170, + "pk": 166, "fields": { "name": "Can change worker", - "content_type": 43, + "content_type": 42, "codename": "change_worker" } }, { "model": "auth.permission", - "pk": 171, + "pk": 167, "fields": { "name": "Can delete worker", - "content_type": 43, + "content_type": 42, "codename": "delete_worker" } }, { "model": "auth.permission", - "pk": 172, + "pk": 168, "fields": { "name": "Can view worker", - "content_type": 43, + "content_type": 42, "codename": "view_worker" } }, { "model": "auth.permission", - "pk": 173, + "pk": 169, "fields": { "name": "Can add worker version", - "content_type": 44, + "content_type": 43, "codename": "add_workerversion" } }, { "model": "auth.permission", - "pk": 174, + "pk": 170, "fields": { "name": "Can change worker version", - "content_type": 44, + "content_type": 43, "codename": "change_workerversion" } }, { "model": "auth.permission", - "pk": 175, + "pk": 171, "fields": { "name": "Can delete worker version", - "content_type": 44, + "content_type": 43, "codename": "delete_workerversion" } }, { "model": "auth.permission", - "pk": 176, + "pk": 172, "fields": { "name": "Can view worker version", - "content_type": 44, + "content_type": 43, "codename": "view_workerversion" } }, { "model": "auth.permission", - "pk": 177, + "pk": 173, "fields": { "name": "Can add git ref", - "content_type": 45, + "content_type": 44, "codename": "add_gitref" } }, { "model": "auth.permission", - "pk": 178, + "pk": 174, "fields": { "name": "Can change git ref", - "content_type": 45, + "content_type": 44, "codename": "change_gitref" } }, { "model": "auth.permission", - "pk": 179, + "pk": 175, "fields": { "name": "Can delete git ref", - "content_type": 45, + "content_type": 44, "codename": "delete_gitref" } }, { "model": "auth.permission", - "pk": 180, + "pk": 176, "fields": { "name": "Can view git ref", - "content_type": 45, + "content_type": 44, "codename": "view_gitref" } }, { "model": "auth.permission", - "pk": 181, + "pk": 177, "fields": { "name": "Can add worker run", - "content_type": 46, + "content_type": 45, "codename": "add_workerrun" } }, { "model": "auth.permission", - "pk": 182, + "pk": 178, "fields": { "name": "Can change worker run", - "content_type": 46, + "content_type": 45, "codename": "change_workerrun" } }, { "model": "auth.permission", - "pk": 183, + "pk": 179, "fields": { "name": "Can delete worker run", - "content_type": 46, + "content_type": 45, "codename": "delete_workerrun" } }, { "model": "auth.permission", - "pk": 184, + "pk": 180, "fields": { "name": "Can view worker run", - "content_type": 46, + "content_type": 45, "codename": "view_workerrun" } }, { "model": "auth.permission", - "pk": 185, + "pk": 181, "fields": { "name": "Can add data import element", - "content_type": 47, + "content_type": 46, "codename": "add_dataimportelement" } }, { "model": "auth.permission", - "pk": 186, + "pk": 182, "fields": { "name": "Can change data import element", - "content_type": 47, + "content_type": 46, "codename": "change_dataimportelement" } }, { "model": "auth.permission", - "pk": 187, + "pk": 183, "fields": { "name": "Can delete data import element", - "content_type": 47, + "content_type": 46, "codename": "delete_dataimportelement" } }, { "model": "auth.permission", - "pk": 188, + "pk": 184, "fields": { "name": "Can view data import element", - "content_type": 47, + "content_type": 46, "codename": "view_dataimportelement" } }, { "model": "ponos.workflow", - "pk": "d6d653a7-105e-4055-b4e1-ee7a85ffa375", + "pk": "88b380b5-30a0-4123-ba9f-3c671eb70275", "fields": { "recipe": "tasks:\n docker_build:\n image: reco", "created": "2020-02-02T01:23:45.678Z", @@ -3253,7 +3156,7 @@ }, { "model": "ponos.task", - "pk": "43f65c92-f06c-4ad5-969c-8eda3cea20e0", + "pk": "e39fdda0-d27a-4069-ab2e-ce7d12ae4c08", "fields": { "run": 0, "depth": 0, @@ -3267,7 +3170,7 @@ "image_artifact": null, "agent": null, "gpu": null, - "workflow": "d6d653a7-105e-4055-b4e1-ee7a85ffa375", + "workflow": "88b380b5-30a0-4123-ba9f-3c671eb70275", "container": null, "created": "2020-02-02T01:23:45.678Z", "updated": "2020-02-02T01:23:45.678Z", @@ -3276,9 +3179,9 @@ }, { "model": "ponos.artifact", - "pk": "e55e4fb8-65cc-41f3-a464-2e58103d66f4", + "pk": "c291272c-ef11-4c27-a882-099fd0c0e1e8", "fields": { - "task": "43f65c92-f06c-4ad5-969c-8eda3cea20e0", + "task": "e39fdda0-d27a-4069-ab2e-ce7d12ae4c08", "path": "/path/to/docker_build", "size": 42000, "content_type": "application/octet-stream", diff --git a/arkindex/documents/management/commands/build_fixtures.py b/arkindex/documents/management/commands/build_fixtures.py index 6d7edc0ab3..7ed58fe338 100644 --- a/arkindex/documents/management/commands/build_fixtures.py +++ b/arkindex/documents/management/commands/build_fixtures.py @@ -7,11 +7,10 @@ from django.core.management.base import BaseCommand from django.utils import timezone as DjangoTimeZone from arkindex.dataimport.models import RepositoryType, WorkerVersion, WorkerVersionState, Workflow -from arkindex.documents.models import Corpus, DataSource, Element, MetaData +from arkindex.documents.models import Corpus, Element, MetaData from arkindex.images.models import Image, ImageServer, Zone from arkindex.users.models import CorpusRight, Group, User from arkindex_common.enums import MetaType -from arkindex_common.ml_tool import MLToolType from ponos.models import State @@ -84,24 +83,6 @@ class Command(BaseCommand): group.users.create(email='user2@user.fr', display_name='Test user write', through_defaults={'level': 50}) group.users.create(email='user3@user.fr', display_name='Test user read', through_defaults={'level': 10}) - # Create 1 data source for transcriptions - recognizer_source = DataSource.objects.create( - type=MLToolType.Recognizer, - slug='test', - name='Test Recognizer', - revision='4.2', - internal=False, - ) - - # Create 1 data source for classifications - DataSource.objects.create( - type=MLToolType.Classifier, - slug='test', - name='Test Classifier', - revision='5.1', - internal=False, - ) - # Create OAuth credentials for a user creds = user.credentials.create( provider_name='gitlab', @@ -109,6 +90,50 @@ class Command(BaseCommand): token='oauth-token', ) + # Create a worker repository + worker_repo = creds.repos.create( + type=RepositoryType.Worker, + url="http://my_repo.fake/workers/worker", + hook_token='worker-hook-token', + provider_name='GitLabProvider' + ) + + # Create a revision on this repository + revision = worker_repo.revisions.create( + hash="1337", + message="My w0rk3r", + author="Test user" + ) + + # Create a fake docker build with a docker image task + workflow = Workflow.objects.create(recipe='tasks:\n docker_build:\n image: reco') + build_task = workflow.tasks.create(run=0, depth=0, slug='docker_build', state=State.Completed) + docker_image = build_task.artifacts.create(size=42_000, path='/path/to/docker_build') + + # Create two workers for the repository with their available version + recognizer_worker = WorkerVersion.objects.create( + worker=worker_repo.workers.create( + name='Recognizer', + slug='reco', + type='recognizer', + ), + revision=revision, + configuration={'test': 42}, + state=WorkerVersionState.Available, + docker_image=docker_image + ) + WorkerVersion.objects.create( + worker=worker_repo.workers.create( + name='Document layout analyser', + slug='dla', + type='dla', + ), + revision=revision, + configuration={'test': 42}, + state=WorkerVersionState.Available, + docker_image=docker_image + ) + # Create a IIIF repository repo = creds.repos.create( url='http://gitlab/repo', @@ -239,14 +264,14 @@ class Command(BaseCommand): ) element.add_parent(page) element.transcriptions.create( - source=recognizer_source, + worker_version=recognizer_worker, text=word, score=1.0, ) # Create a page transcription on page 1 p1_1.transcriptions.create( - source=recognizer_source, + worker_version=recognizer_worker, text='Lorem ipsum dolor sit amet', score=1.0, ) @@ -322,47 +347,3 @@ class Command(BaseCommand): sd.add_parent(act3) se.add_parent(act4) sf.add_parent(act5) - - # Create a worker repository - worker_repo = creds.repos.create( - type=RepositoryType.Worker, - url="http://my_repo.fake/workers/worker", - hook_token='worker-hook-token', - provider_name='GitLabProvider' - ) - - # Create a revision on this repository - revision = worker_repo.revisions.create( - hash="1337", - message="My w0rk3r", - author="Test user" - ) - - # Create a fake docker build with a docker image task - workflow = Workflow.objects.create(recipe='tasks:\n docker_build:\n image: reco') - build_task = workflow.tasks.create(run=0, depth=0, slug='docker_build', state=State.Completed) - docker_image = build_task.artifacts.create(size=42_000, path='/path/to/docker_build') - - # Create two workers for the repository with their available version - WorkerVersion.objects.create( - worker=worker_repo.workers.create( - name='Recognizer', - slug='reco', - type='recognizer' - ), - revision=revision, - configuration={'test': 42}, - state=WorkerVersionState.Available, - docker_image=docker_image - ) - WorkerVersion.objects.create( - worker=worker_repo.workers.create( - name='Document layout analyser', - slug='dla', - type='dla' - ), - revision=revision, - configuration={'test': 42}, - state=WorkerVersionState.Available, - docker_image=docker_image - ) diff --git a/arkindex/documents/migrations/0024_drop_datasource.py b/arkindex/documents/migrations/0024_drop_datasource.py new file mode 100644 index 0000000000..fe370ef6e4 --- /dev/null +++ b/arkindex/documents/migrations/0024_drop_datasource.py @@ -0,0 +1,64 @@ +# Generated by Django 3.1.3 on 2020-11-30 10:40 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('documents', '0023_remove_transcription_type'), + ] + + operations = [ + migrations.AlterUniqueTogether( + name='datasource', + unique_together=None, + ), + migrations.RemoveConstraint( + model_name='classification', + name='classification_unique_manual', + ), + migrations.RemoveConstraint( + model_name='classification', + name='classification_unique_worker_version', + ), + migrations.RemoveConstraint( + model_name='transcription', + name='transcription_source_not_worker_version', + ), + migrations.RemoveField( + model_name='classification', + name='source', + ), + migrations.RemoveField( + model_name='element', + name='source', + ), + migrations.RemoveField( + model_name='entity', + name='source', + ), + migrations.RemoveField( + model_name='transcription', + name='source', + ), + migrations.AddConstraint( + model_name='classification', + constraint=models.UniqueConstraint( + condition=models.Q(worker_version_id__isnull=True), + fields=('element', 'ml_class'), + name='classification_unique_manual' + ), + ), + migrations.AddConstraint( + model_name='classification', + constraint=models.UniqueConstraint( + condition=models.Q(worker_version_id__isnull=False), + fields=('element', 'ml_class', 'worker_version'), + name='classification_unique_worker_version', + ), + ), + migrations.DeleteModel( + name='DataSource', + ), + ] diff --git a/arkindex/documents/models.py b/arkindex/documents/models.py index 8c85160192..f1ee844e43 100644 --- a/arkindex/documents/models.py +++ b/arkindex/documents/models.py @@ -18,7 +18,6 @@ from arkindex.project.elastic import ESElement, ESEntity, ESTranscription from arkindex.project.fields import ArrayField from arkindex.project.models import IndexableModel from arkindex_common.enums import EntityType, MetaType -from arkindex_common.ml_tool import MLToolType logger = logging.getLogger(__name__) @@ -146,13 +145,6 @@ class Element(IndexableModel): null=True, blank=True, ) - source = models.ForeignKey( - 'documents.DataSource', - on_delete=models.SET_NULL, - related_name='elements', - null=True, - blank=True, - ) worker_version = models.ForeignKey( 'dataimport.WorkerVersion', on_delete=models.SET_NULL, @@ -300,23 +292,6 @@ class Element(IndexableModel): return '{}: {}'.format(self.type.display_name, self.name) -class DataSource(models.Model): - id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) - type = EnumField(MLToolType, max_length=50) - slug = models.CharField(max_length=100) - name = models.CharField(max_length=100) - revision = models.CharField(max_length=100) - internal = models.BooleanField() - - class Meta: - unique_together = ( - ('type', 'slug', 'revision'), - ) - - def __str__(self): - return '{} {}'.format(self.slug, self.revision) - - class Entity(InterpretedDateMixin, models.Model): """ Semantic object in arkindex @@ -336,13 +311,6 @@ class Entity(InterpretedDateMixin, models.Model): null=True, blank=True ) - source = models.ForeignKey( - DataSource, - on_delete=models.CASCADE, - related_name='entities', - null=True, - blank=True, - ) worker_version = models.ForeignKey( 'dataimport.WorkerVersion', on_delete=models.CASCADE, @@ -434,13 +402,6 @@ class Transcription(models.Model): on_delete=models.CASCADE, related_name='transcriptions', ) - source = models.ForeignKey( - DataSource, - on_delete=models.CASCADE, - related_name='transcriptions', - null=True, - blank=True, - ) worker_version = models.ForeignKey( 'dataimport.WorkerVersion', on_delete=models.CASCADE, @@ -459,15 +420,6 @@ class Transcription(models.Model): def __str__(self): return 'Transcription: {}'.format(self.text[:20]) - class Meta: - constraints = [ - # Require either a source, a worker version, or none (manual), but not both at once - models.CheckConstraint( - check=~Q(source_id__isnull=False, worker_version_id__isnull=False), - name='transcription_source_not_worker_version', - ) - ] - class TranscriptionEntity(models.Model): """ @@ -534,13 +486,6 @@ class Classification(models.Model): on_delete=models.CASCADE, related_name='classifications', ) - source = models.ForeignKey( - DataSource, - on_delete=models.CASCADE, - related_name='classifications', - null=True, - blank=True, - ) worker_version = models.ForeignKey( 'dataimport.WorkerVersion', on_delete=models.CASCADE, @@ -571,12 +516,12 @@ class Classification(models.Model): models.UniqueConstraint( fields=['element', 'ml_class'], name='classification_unique_manual', - condition=Q(worker_version_id__isnull=True, source_id__isnull=True), + condition=Q(worker_version_id__isnull=True), ), models.UniqueConstraint( fields=['element', 'ml_class', 'worker_version'], name='classification_unique_worker_version', - condition=Q(worker_version_id__isnull=False, source_id__isnull=True), + condition=Q(worker_version_id__isnull=False), ) ] diff --git a/arkindex/documents/search.py b/arkindex/documents/search.py index b96cb85edc..a905f2dabc 100644 --- a/arkindex/documents/search.py +++ b/arkindex/documents/search.py @@ -15,7 +15,7 @@ def search_transcriptions_post(data): ts = Transcription.objects \ .filter(id__in=transcription_ids) \ .order_by('-score') \ - .prefetch_related('element__zone__image__server', 'source') + .select_related('element__zone__image__server', 'worker_version') element_ids = list(ts.values_list('element_id', flat=True)) all_parent_paths = Element.objects.get_ascendings_paths(*element_ids) for trans in ts: @@ -65,7 +65,7 @@ def search_elements_post(data): transcriptions = { t.id: t - for t in Transcription.objects.filter(id__in=tr_ids).prefetch_related('source') + for t in Transcription.objects.filter(id__in=tr_ids).select_related('worker_version') } elts_tr_ids = { diff --git a/arkindex/documents/serializers/elements.py b/arkindex/documents/serializers/elements.py index c38e865bbc..fe2a1e4cdc 100644 --- a/arkindex/documents/serializers/elements.py +++ b/arkindex/documents/serializers/elements.py @@ -24,7 +24,7 @@ from arkindex.documents.serializers.light import ( ElementTypeLightSerializer, MetaDataLightSerializer, ) -from arkindex.documents.serializers.ml import ClassificationSerializer, DataSourceSerializer +from arkindex.documents.serializers.ml import ClassificationSerializer from arkindex.images.models import Image, Zone from arkindex.images.serializers import ZoneSerializer from arkindex.project.serializer_fields import LinearRingField @@ -248,7 +248,6 @@ class ElementSerializer(ElementSlimSerializer): help_text='Set the polygon linking this element to the image. ' '`image` must be set when this field is set and there was no image or polygon defined before.', ) - source = DataSourceSerializer(read_only=True, required=False) class Meta: model = Element @@ -257,13 +256,11 @@ class ElementSerializer(ElementSlimSerializer): 'classifications', 'image', 'polygon', - 'source', 'worker_version' ) read_only_fields = ElementSlimSerializer.Meta.read_only_fields + ( 'metadata', 'classifications', - 'source', 'worker_version' ) diff --git a/arkindex/documents/serializers/entities.py b/arkindex/documents/serializers/entities.py index 8deca6f31a..932cdf7550 100644 --- a/arkindex/documents/serializers/entities.py +++ b/arkindex/documents/serializers/entities.py @@ -3,7 +3,6 @@ from rest_framework import serializers from arkindex.dataimport.models import WorkerVersion from arkindex.documents.models import Corpus, Entity, EntityLink, EntityRole, TranscriptionEntity from arkindex.documents.serializers.light import CorpusLightSerializer, InterpretedDateSerializer -from arkindex.documents.serializers.ml import DataSourceSerializer from arkindex.project.serializer_fields import EnumField from arkindex.project.triggers import reindex_start from arkindex_common.enums import EntityType @@ -16,7 +15,6 @@ class BaseEntitySerializer(serializers.ModelSerializer): type = EnumField(EntityType) dates = InterpretedDateSerializer(many=True, source='get_dates', read_only=True) metas = serializers.HStoreField(child=serializers.CharField(), required=False, allow_null=True) - source = DataSourceSerializer(read_only=True) worker_version_id = serializers.PrimaryKeyRelatedField(read_only=True) class Meta: @@ -28,13 +26,11 @@ class BaseEntitySerializer(serializers.ModelSerializer): 'metas', 'validated', 'dates', - 'source', 'worker_version_id', ) read_only_fields = ( 'id', 'dates', - 'source', 'worker_version_id', ) diff --git a/arkindex/documents/serializers/ml.py b/arkindex/documents/serializers/ml.py index d3e4326665..dfabfdac86 100644 --- a/arkindex/documents/serializers/ml.py +++ b/arkindex/documents/serializers/ml.py @@ -11,7 +11,6 @@ from arkindex.documents.models import ( Classification, ClassificationState, Corpus, - DataSource, Element, ElementType, MLClass, @@ -19,7 +18,6 @@ from arkindex.documents.models import ( ) from arkindex.documents.serializers.light import ElementZoneSerializer from arkindex.project.serializer_fields import EnumField, LinearRingField -from arkindex_common.ml_tool import MLToolType class ClassificationMode(Enum): @@ -30,41 +28,6 @@ class ClassificationMode(Enum): Create = "create" -class DataSourceSerializer(serializers.ModelSerializer): - """ - Serialize a data source for transcriptions and classifications - """ - - type = EnumField(MLToolType) - - class Meta: - model = DataSource - fields = ( - 'id', - 'type', - 'slug', - 'name', - 'revision', - 'internal', - ) - - -class DataSourceStatsSerializer(DataSourceSerializer): - """ - A data source, but including their result counts - """ - classifications_count = serializers.IntegerField(default=0) - transcriptions_count = serializers.IntegerField(default=0) - entities_count = serializers.IntegerField(default=0) - - class Meta(DataSourceSerializer.Meta): - fields = DataSourceSerializer.Meta.fields + ( - 'classifications_count', - 'transcriptions_count', - 'entities_count', - ) - - class MLClassSerializer(serializers.ModelSerializer): """ Serializer for MLClass instances @@ -98,7 +61,6 @@ class ClassificationSerializer(serializers.ModelSerializer): Serialize a classification on an Element """ - source = DataSourceSerializer() ml_class = MLClassSerializer() state = EnumField(ClassificationState) @@ -107,7 +69,6 @@ class ClassificationSerializer(serializers.ModelSerializer): read_only_fields = ('id', 'confidence', 'high_confidence') fields = ( 'id', - 'source', 'ml_class', 'state', 'confidence', @@ -153,11 +114,11 @@ class ClassificationCreateSerializer(serializers.ModelSerializer): read_only_fields = ('id', 'state') validators = [ UniqueTogetherValidator( - queryset=Classification.objects.using('default').filter(worker_version__isnull=False, source_id__isnull=True), + queryset=Classification.objects.using('default').filter(worker_version__isnull=False), fields=['element', 'worker_version', 'ml_class'] ), UniqueTogetherValidator( - queryset=Classification.objects.using('default').filter(worker_version__isnull=True, source_id__isnull=True), + queryset=Classification.objects.using('default').filter(worker_version__isnull=True), fields=['element', 'ml_class'] ) ] @@ -239,18 +200,16 @@ class ClassificationsSelectionSerializer(serializers.ModelSerializer): class TranscriptionSerializer(serializers.ModelSerializer): """ - Serialises a Transcription + Serializes a Transcription """ - source = DataSourceSerializer(read_only=True) class Meta: model = Transcription - read_only_fields = ('id', 'score', 'source') + read_only_fields = ('id', 'score') fields = ( 'id', 'text', 'score', - 'source', 'worker_version_id', ) diff --git a/arkindex/documents/tasks.py b/arkindex/documents/tasks.py index 599cb6af67..064afbb36a 100644 --- a/arkindex/documents/tasks.py +++ b/arkindex/documents/tasks.py @@ -1,9 +1,7 @@ import logging -from math import ceil from typing import Optional from django.db.models import Q -from django.db.models.deletion import Collector from django_rq import job from rq import get_current_job @@ -26,37 +24,6 @@ from arkindex.documents.models import ( logger = logging.getLogger(__name__) -def _delete_queryset(queryset, batch_size=1000): - """ - Helper to delete large querysets with as little SQL queries and memory footprint as possible. - """ - count = queryset.count() - logger.info('Deleting {} {}'.format(count, queryset.model.__name__)) - - if not count: - return - - if Collector(using=queryset.db).can_fast_delete(queryset.all()): - # If a single DELETE statement can be used, - # bypass both the batched deletion and Django's related objects checks. - logger.debug('Using single-query deletion') - queryset._raw_delete(using=queryset.db) - return - - if count <= batch_size: - # If there is a single batch, just delete. - queryset.delete() - return - - for i in range(ceil(count / batch_size)): - logger.debug('Deleting batch {}'.format(i + 1)) - # Deleting a slice is not allowed; - # we use a sliced subquery instead and still delete in a single query. - # DELETE FROM … WHERE id IN (SELECT id FROM … LIMIT [batch_size]) - ids = queryset[:batch_size].values('id') - queryset.model.objects.filter(id__in=ids).delete() - - @job def reindex_start(corpus_id: Optional[str] = None, element_id: Optional[str] = None, @@ -110,51 +77,6 @@ def reindex_start(corpus_id: Optional[str] = None, indexer.run_index(entities_queryset, bulk_size=400) -@job('high') -def ml_results_delete(corpus_id: Optional[str] = None, - element_id: Optional[str] = None, - batch_size: int = 1000) -> None: - assert corpus_id or element_id, 'Missing element or corpus IDs' - - if element_id: - logger.info('Deleting ML results on element {}'.format(element_id)) - element = Element.objects.get(id=element_id) - if element.type.folder: - # The folder AND its children - elements = Element.objects.filter(id=element_id).values('id').union( - # Disable ordering here because we do not need it and it adds an extra column, - # causing the UNION to fail - Element.objects.get_descending(element_id).order_by().values('id') - ) - else: - elements = [element] - - if not corpus_id: - # The corpus ID is still used in some deletions; deduce it from the element. - corpus_id = Element.objects.get(id=element_id).corpus_id - elif corpus_id: - logger.info('Deleting ML results on corpus {}'.format(corpus_id)) - elements = Element.objects.filter(corpus_id=corpus_id) - - # Simple deletions for classifications and transcriptions. - _delete_queryset(Classification.objects.filter(element__in=elements).exclude(source__slug='manual'), batch_size) - _delete_queryset(Transcription.objects.filter(element__in=elements).exclude(source__slug='manual'), batch_size) - - # Entity deletion is complex: they can be linked to different elements both on transcriptions and metadata. - # Metadata are not considered ML results so we need to keep them: update them to unlink entities. - logger.info('Updating element metadata') - MetaData.objects.filter(element__in=elements).update(entity_id=None) - - # We removed transcriptions earlier, which implies removing the links with entities. - # All is left is to remove 'lonely' entities. - # Note: __isnull's implementation will fetch all element IDs into a list before deleting—use batches! - _delete_queryset(Entity.objects.filter( - corpus_id=corpus_id, - metadatas__isnull=True, - transcriptions__isnull=True, - ), batch_size) - - @job('high') def corpus_delete(corpus_id: str) -> None: # Note that this can be None when the task is run outside of a RQ worker (e.g. unit test) diff --git a/arkindex/documents/tests/commands/test_reindex.py b/arkindex/documents/tests/commands/test_reindex.py index 0531056f53..ff2d90d73e 100644 --- a/arkindex/documents/tests/commands/test_reindex.py +++ b/arkindex/documents/tests/commands/test_reindex.py @@ -3,7 +3,8 @@ from unittest.mock import call, patch from django.core.management import CommandError, call_command from django.test import override_settings -from arkindex.documents.models import DataSource, Element, Entity, EntityType, MLToolType, Transcription +from arkindex.dataimport.models import WorkerVersion +from arkindex.documents.models import Element, Entity, EntityType, Transcription from arkindex.project.elastic import ESElement, ESEntity, ESTranscription from arkindex.project.tests import FixtureTestCase from arkindex_common.enums import MetaType @@ -14,10 +15,10 @@ class TestReindexCommand(FixtureTestCase): @classmethod def setUpTestData(cls): super().setUpTestData() - source = DataSource.objects.create(type=MLToolType.NER, slug='entity', internal=True) cls.indexer_patch = patch('arkindex.documents.management.commands.reindex.Indexer') cls.vol = cls.corpus.elements.get(name="Volume 1") - cls.entity = cls.corpus.entities.create(type=EntityType.Misc, name='Dummy entity', source=source) + worker_version = WorkerVersion.objects.first() + cls.entity = cls.corpus.entities.create(type=EntityType.Misc, name='Dummy entity', worker_version=worker_version) page = cls.corpus.elements.get(name='Volume 1, page 1r') page.metadatas.create(name='Dummy metadata', value='Dummy', type=MetaType.Text, entity=cls.entity) diff --git a/arkindex/documents/tests/tasks/test_corpus_delete.py b/arkindex/documents/tests/tasks/test_corpus_delete.py index 53c2cd99c5..db15d2c640 100644 --- a/arkindex/documents/tests/tasks/test_corpus_delete.py +++ b/arkindex/documents/tests/tasks/test_corpus_delete.py @@ -1,11 +1,10 @@ from django.db.models.signals import pre_delete from arkindex.dataimport.models import Repository, RepositoryType, WorkerVersion -from arkindex.documents.models import Corpus, DataSource, Element, Transcription +from arkindex.documents.models import Corpus, Element, Transcription from arkindex.documents.tasks import corpus_delete from arkindex.project.tests import FixtureTestCase from arkindex_common.enums import DataImportMode, EntityType, MetaType -from arkindex_common.ml_tool import MLToolType class TestDeleteCorpus(FixtureTestCase): @@ -32,12 +31,14 @@ class TestDeleteCorpus(FixtureTestCase): ) file_import.build_workflow() + cls.worker_version = WorkerVersion.objects.get(worker__slug='reco') + element_import = cls.corpus.imports.create( creator=cls.user, mode=DataImportMode.Workers, ) element_import.elements.add(element) - element_import.worker_runs.create(version=WorkerVersion.objects.first(), parents=[]) + element_import.worker_runs.create(version=cls.worker_version, parents=[]) entity1 = cls.corpus.entities.create(name='Magnemite', type=EntityType.Person) entity2 = cls.corpus.entities.create(name='Magneton', type=EntityType.Person) @@ -98,24 +99,14 @@ class TestDeleteCorpus(FixtureTestCase): name='A page', ) cls.page.classifications.create( - source=DataSource.objects.create( - type=MLToolType.Classifier, - slug='classeur', - revision='Early Access', - internal=False, - ), + worker_version=cls.worker_version, ml_class=cls.corpus2.ml_classes.create( name='klass', ), confidence=0.5, ) cls.page.transcriptions.create( - source=DataSource.objects.create( - type=MLToolType.Recognizer, - slug='reco', - revision='-1', - internal=False, - ), + worker_version=cls.worker_version, text='hi', score=0.75, ) @@ -159,11 +150,11 @@ class TestDeleteCorpus(FixtureTestCase): self.assertEqual(md.value, 'data') cl = self.page.classifications.get() - self.assertEqual(cl.source.slug, 'classeur') + self.assertEqual(cl.worker_version, self.worker_version) self.assertEqual(cl.ml_class.name, 'klass') self.assertEqual(cl.confidence, 0.5) ts = self.page.transcriptions.get() - self.assertEqual(ts.source.slug, 'reco') + self.assertEqual(ts.worker_version, self.worker_version) self.assertEqual(ts.text, 'hi') self.assertEqual(ts.score, 0.75) diff --git a/arkindex/documents/tests/tasks/test_ml_results_delete.py b/arkindex/documents/tests/tasks/test_ml_results_delete.py deleted file mode 100644 index 0c7a8b57ce..0000000000 --- a/arkindex/documents/tests/tasks/test_ml_results_delete.py +++ /dev/null @@ -1,169 +0,0 @@ -from arkindex.documents.models import Classification, DataSource, Element, Entity, Transcription -from arkindex.documents.tasks import ml_results_delete -from arkindex.project.tests import FixtureTestCase -from arkindex_common.enums import EntityType, MetaType -from arkindex_common.ml_tool import MLToolType - - -class TestMLResultsDelete(FixtureTestCase): - - @classmethod - def setUpTestData(cls): - super().setUpTestData() - cls.folder1 = cls.corpus.elements.get(name='Volume 1') - cls.folder2 = cls.corpus.elements.get(name='Volume 2') - - source = DataSource.objects.get(slug='test', type=MLToolType.Recognizer) - ml_class = cls.corpus.ml_classes.create(name='Some class') - - cls.page1 = cls.corpus.elements.get(name='Volume 1, page 1r') - cls.page1.classifications.create( - ml_class=ml_class, - confidence=0.42, - source=source, - ) - entity1 = cls.corpus.entities.create( - type=EntityType.Person, - name='Some entity 1', - source=source, - ) - cls.page1.metadatas.create( - type=MetaType.Text, - name='something', - value='Some entity 1', - entity=entity1, - ) - cls.page1.transcriptions.first().transcription_entities.create( - offset=0, - length=1, - entity=entity1, - ) - - cls.page2 = cls.corpus.elements.get(name='Volume 2, page 1r') - cls.page2.classifications.create( - ml_class=ml_class, - confidence=0.42, - source=source, - ) - entity2 = cls.corpus.entities.create( - type=EntityType.Person, - name='Some entity 2', - source=source, - ) - cls.page2.metadatas.create( - type=MetaType.Text, - name='something', - value='Some entity 2', - entity=entity2, - ) - cls.page2.transcriptions.create( - score=0.74, - text='some text', - source=source, - ).transcription_entities.create( - offset=0, - length=1, - entity=entity2, - ) - - def _get_querysets(self, elements): - return [ - Transcription.objects.filter(element__in=elements), - Entity.objects.filter(transcriptions__element__in=elements), - Entity.objects.filter(metadatas__element__in=elements), - Classification.objects.filter(element__in=elements), - ] - - def test_delete_missing_parameters(self): - with self.assertRaises(AssertionError): - ml_results_delete() - - def test_delete_corpus(self): - querysets = self._get_querysets(self.corpus.elements.all()) - - for queryset in querysets: - self.assertTrue(queryset.exists()) - - self.assertEqual(self.page1.metadatas.count(), 2) - self.assertEqual(self.page2.metadatas.count(), 2) - - with self.assertNumQueries(13): - ml_results_delete(corpus_id=self.corpus.id) - - for queryset in querysets: - self.assertFalse(queryset.exists()) - - self.assertEqual(self.page1.metadatas.count(), 2) - self.assertEqual(self.page2.metadatas.count(), 2) - metadata = self.page1.metadatas.get(value='Some entity 1') - self.assertEqual(metadata.type, MetaType.Text) - self.assertIsNone(metadata.entity) - metadata = self.page2.metadatas.get(value='Some entity 2') - self.assertEqual(metadata.type, MetaType.Text) - self.assertIsNone(metadata.entity) - - def test_delete_folder(self): - folder1_querysets = self._get_querysets( - Element.objects.filter(id=self.folder1.id).values('id').union( - Element.objects.get_descending(self.folder1.id).order_by().values('id') - ) - ) - folder2_querysets = self._get_querysets( - Element.objects.filter(id=self.folder2.id).values('id').union( - Element.objects.get_descending(self.folder2.id).order_by().values('id') - ) - ) - - for queryset in folder1_querysets: - self.assertTrue(queryset.exists()) - for queryset in folder2_querysets: - self.assertTrue(queryset.exists()) - - with self.assertNumQueries(16): - ml_results_delete(element_id=self.folder1.id) - - for queryset in folder1_querysets: - self.assertFalse(queryset.exists()) - for queryset in folder2_querysets: # This folder should not change - self.assertTrue(queryset.exists()) - - self.assertEqual(self.page1.metadatas.count(), 2) - self.assertEqual(self.page2.metadatas.count(), 2) - metadata = self.page1.metadatas.get(value='Some entity 1') - self.assertEqual(metadata.type, MetaType.Text) - self.assertIsNone(metadata.entity) - metadata = self.page2.metadatas.get(value='Some entity 2') - self.assertEqual(metadata.type, MetaType.Text) - self.assertIsNotNone(metadata.entity) - - def test_delete_page(self): - page1_querysets = self._get_querysets( - Element.objects.filter(id=self.page1.id).values('id') - ) - folder2_querysets = self._get_querysets( - Element.objects.filter(id=self.folder2.id).values('id').union( - Element.objects.get_descending(self.folder2.id).order_by().values('id') - ) - ) - - for queryset in page1_querysets: - self.assertTrue(queryset.exists()) - for queryset in folder2_querysets: - self.assertTrue(queryset.exists()) - - with self.assertNumQueries(16): - ml_results_delete(element_id=self.page1.id) - - for queryset in page1_querysets: - self.assertFalse(queryset.exists()) - for queryset in folder2_querysets: # This folder should not change - self.assertTrue(queryset.exists()) - - self.assertEqual(self.page1.metadatas.count(), 2) - self.assertEqual(self.page2.metadatas.count(), 2) - metadata = self.page1.metadatas.get(value='Some entity 1') - self.assertEqual(metadata.type, MetaType.Text) - self.assertIsNone(metadata.entity) - metadata = self.page2.metadatas.get(value='Some entity 2') - self.assertEqual(metadata.type, MetaType.Text) - self.assertIsNotNone(metadata.entity) diff --git a/arkindex/documents/tests/tasks/test_reindex.py b/arkindex/documents/tests/tasks/test_reindex.py index 014d09795e..0f5f3fccfe 100644 --- a/arkindex/documents/tests/tasks/test_reindex.py +++ b/arkindex/documents/tests/tasks/test_reindex.py @@ -3,11 +3,11 @@ from unittest.mock import patch from django.contrib.gis.geos import LinearRing from django.db.models import Q -from arkindex.documents.models import Corpus, DataSource, Element, Entity, Transcription +from arkindex.dataimport.models import WorkerVersion +from arkindex.documents.models import Corpus, Element, Entity, Transcription from arkindex.documents.tasks import reindex_start from arkindex.project.tests import FixtureTestCase from arkindex_common.enums import EntityType, MetaType -from arkindex_common.ml_tool import MLToolType @patch('arkindex.documents.tasks.Indexer') @@ -16,8 +16,7 @@ class TestReindex(FixtureTestCase): @classmethod def setUpTestData(cls): super().setUpTestData() - source = DataSource.objects.get(slug='test', type=MLToolType.Recognizer) - + cls.worker_version = WorkerVersion.objects.first() cls.folder = cls.corpus.elements.get(name='Volume 1') cls.folder.metadatas.create( type=MetaType.Text, @@ -26,7 +25,7 @@ class TestReindex(FixtureTestCase): entity=cls.corpus.entities.create( type=EntityType.Person, name='Some entity', - source=source, + worker_version=cls.worker_version, ) ) @@ -41,13 +40,13 @@ class TestReindex(FixtureTestCase): ts = element2.transcriptions.create( score=0.8, text='something', - source=source, + worker_version=cls.worker_version, ) ts.transcription_entities.create( entity=corpus2.entities.create( type=EntityType.Misc, name='Some other entity', - source=source, + worker_version=cls.worker_version, ), offset=0, length=1, diff --git a/arkindex/documents/tests/test_bulk_classification.py b/arkindex/documents/tests/test_bulk_classification.py index 1ab1261240..e834fe55d9 100644 --- a/arkindex/documents/tests/test_bulk_classification.py +++ b/arkindex/documents/tests/test_bulk_classification.py @@ -2,9 +2,8 @@ from django.urls import reverse from rest_framework import status from arkindex.dataimport.models import WorkerVersion -from arkindex.documents.models import Corpus, DataSource, MLClass +from arkindex.documents.models import Corpus, MLClass from arkindex.project.tests import FixtureAPITestCase -from arkindex_common.ml_tool import MLToolType class TestBulkClassification(FixtureAPITestCase): @@ -13,22 +12,9 @@ class TestBulkClassification(FixtureAPITestCase): def setUpTestData(cls): super().setUpTestData() cls.page = cls.corpus.elements.get(name='Volume 1, page 2r') - cls.src = DataSource.objects.get(slug='test', type=MLToolType.Classifier) cls.private_corpus = Corpus.objects.create(name='private', public=False) cls.worker_version = WorkerVersion.objects.get(worker__slug='reco') - @classmethod - def setUpClass(cls): - super().setUpClass() - cls.src.internal = True - cls.src.save() - - @classmethod - def tearDownClass(cls): - super().tearDownClass() - cls.src.internal = False - cls.src.save() - def create_classifications_data(self, classifications, parent=None): return { "parent": parent or str(self.page.id), @@ -90,12 +76,11 @@ class TestBulkClassification(FixtureAPITestCase): 'ml_class__name', 'confidence', 'high_confidence', - 'source', 'worker_version' )), [ - ('dog', 0.99, True, None, self.worker_version.id), - ('cat', 0.42, False, None, self.worker_version.id) + ('dog', 0.99, True, self.worker_version.id), + ('cat', 0.42, False, self.worker_version.id) ], ) diff --git a/arkindex/documents/tests/test_bulk_element_transcriptions.py b/arkindex/documents/tests/test_bulk_element_transcriptions.py index b07e965e4d..71a2abb020 100644 --- a/arkindex/documents/tests/test_bulk_element_transcriptions.py +++ b/arkindex/documents/tests/test_bulk_element_transcriptions.py @@ -75,10 +75,10 @@ class TestBulkElementTranscriptions(FixtureAPITestCase): ] ) self.assertCountEqual( - created_elts.values_list('transcriptions__text', 'transcriptions__source', 'transcriptions__worker_version'), + created_elts.values_list('transcriptions__text', 'transcriptions__worker_version'), [ - ('Hello world !', None, self.worker_version.id), - ('I <3 JavaScript', None, self.worker_version.id) + ('Hello world !', self.worker_version.id), + ('I <3 JavaScript', self.worker_version.id) ] ) self.assertEqual(delay_mock.call_count, 1) @@ -359,10 +359,10 @@ class TestBulkElementTranscriptions(FixtureAPITestCase): ] ) self.assertCountEqual( - created_elts.values_list('transcriptions__text', 'transcriptions__source', 'transcriptions__worker_version'), + created_elts.values_list('transcriptions__text', 'transcriptions__worker_version'), [ - ('Hello world !', None, self.worker_version.id), - ('I <3 JavaScript', None, self.worker_version.id) + ('Hello world !', self.worker_version.id), + ('I <3 JavaScript', self.worker_version.id) ] ) self.assertEqual(delay_mock.call_count, 1) diff --git a/arkindex/documents/tests/test_classes.py b/arkindex/documents/tests/test_classes.py index 9f6bfa282a..8b731dc2a3 100644 --- a/arkindex/documents/tests/test_classes.py +++ b/arkindex/documents/tests/test_classes.py @@ -2,9 +2,9 @@ from django.test import override_settings from django.urls import reverse from rest_framework import status -from arkindex.documents.models import Classification, ClassificationState, Corpus, DataSource, Element, MLClass +from arkindex.dataimport.models import WorkerVersion +from arkindex.documents.models import Classification, ClassificationState, Corpus, Element, MLClass from arkindex.project.tests import FixtureAPITestCase -from arkindex_common.ml_tool import MLToolType class TestClasses(FixtureAPITestCase): @@ -21,14 +21,8 @@ class TestClasses(FixtureAPITestCase): self.parent = self.corpus.elements.create(type=self.folder_type) self.common_children = self.corpus.elements.create(type=self.folder_type) - source1 = DataSource.objects.get(slug='test', type=MLToolType.Recognizer) - source2 = DataSource.objects.create( - type=MLToolType.Classifier, - slug='source2', - name='classifier', - revision='123', - internal=False, - ) + self.version1 = WorkerVersion.objects.get(worker__slug='reco') + self.version2 = WorkerVersion.objects.get(worker__slug='dla') for elt_num in range(1, 13): elt = Element.objects.create( name='elt_{}'.format(elt_num), @@ -38,9 +32,9 @@ class TestClasses(FixtureAPITestCase): elt.add_parent(self.parent) self.common_children.add_parent(elt) for ml_class, score in zip((self.text, self.cover), (.7, .99)): - for source in (source1, source2): + for worker_version in (self.version1, self.version2): elt.classifications.create( - source_id=source.id, + worker_version=worker_version, ml_class_id=ml_class.id, confidence=score, high_confidence=bool(score == .99) @@ -245,8 +239,8 @@ class TestClasses(FixtureAPITestCase): self.assertEqual(data['count'], 12) for elt in data['results']: self.assertCountEqual( - list(map(lambda c: (c['source']['slug'], c['confidence']), elt['best_classes'])), - [('source2', .99), ('test', .99)] + list(map(lambda c: (c['worker_version'], c['confidence']), elt['best_classes'])), + [(str(self.version1.id), .99), (str(self.version2.id), .99)] ) def test_list_elements_best_classes_false(self): @@ -275,8 +269,8 @@ class TestClasses(FixtureAPITestCase): self.assertEqual(data['count'], 12) for elt in data['results']: self.assertCountEqual( - list(map(lambda c: (c['source']['slug'], c['confidence']), elt['best_classes'])), - [('source2', .99), ('test', .99)] + list(map(lambda c: (c['worker_version'], c['confidence']), elt['best_classes'])), + [(str(self.version1.id), .99), (str(self.version2.id), .99)] ) def test_element_children_best_classes(self): @@ -291,8 +285,8 @@ class TestClasses(FixtureAPITestCase): self.assertEqual(data['count'], 12) for elt in data['results']: self.assertCountEqual( - list(map(lambda c: (c['source']['slug'], c['confidence']), elt['best_classes'])), - [('source2', .99), ('test', .99)] + list(map(lambda c: (c['worker_version'], c['confidence']), elt['best_classes'])), + [(str(self.version1.id), .99), (str(self.version2.id), .99)] ) def test_rejected_best_classes(self): @@ -330,13 +324,18 @@ class TestClasses(FixtureAPITestCase): for elt in response.json()['results']: if elt['id'] == str(parent.id): self.assertCountEqual( - list(map(lambda c: (c['source']['slug'], c['confidence']), elt['best_classes'])), - [('source2', .99), ('test', .99), ('source2', .7), ('test', .7)] + list(map(lambda c: (c['worker_version'], c['confidence']), elt['best_classes'])), + [ + (str(self.version1.id), .99), + (str(self.version2.id), .99), + (str(self.version1.id), .7), + (str(self.version2.id), .7), + ] ) continue self.assertCountEqual( - list(map(lambda c: (c['source']['slug'], c['confidence']), elt['best_classes'])), - [('source2', .99), ('test', .99)] + list(map(lambda c: (c['worker_version'], c['confidence']), elt['best_classes'])), + [(str(self.version1.id), .99), (str(self.version2.id), .99)] ) def test_rejected_human_class(self): @@ -344,10 +343,8 @@ class TestClasses(FixtureAPITestCase): A manual classification rejected by a human may not appear in best classes """ self.populate_classified_elements() - data_source, _ = DataSource.objects.get_or_create(type=MLToolType.NER, slug="manual", internal=False) element = Element.objects.filter(type=self.classified.id).first() classif = element.classifications.create( - source_id=data_source.id, ml_class_id=self.text.id, confidence=1, high_confidence=True, @@ -360,13 +357,20 @@ class TestClasses(FixtureAPITestCase): for elt in response.json()['results']: if elt['id'] == str(element.id): self.assertCountEqual( - list(map(lambda c: (c['source']['slug'], c['confidence']), elt['best_classes'])), - [('manual', 1.0), ('test', .99), ('source2', .99)] + list(map(lambda c: (c['worker_version'], c['confidence']), elt['best_classes'])), + [ + (None, 1.0), + (str(self.version1.id), .99), + (str(self.version2.id), .99), + ] ) continue self.assertCountEqual( - list(map(lambda c: (c['source']['slug'], c['confidence']), elt['best_classes'])), - [('source2', .99), ('test', .99)] + list(map(lambda c: (c['worker_version'], c['confidence']), elt['best_classes'])), + [ + (str(self.version1.id), .99), + (str(self.version2.id), .99), + ] ) # Reject the manual classification classif.state = ClassificationState.Rejected @@ -378,16 +382,15 @@ class TestClasses(FixtureAPITestCase): self.assertEqual(response.status_code, status.HTTP_200_OK) for elt in response.json()['results']: self.assertCountEqual( - list(map(lambda c: (c['source']['slug'], c['confidence']), elt['best_classes'])), - [('source2', .99), ('test', .99)] + list(map(lambda c: (c['worker_version'], c['confidence']), elt['best_classes'])), + [(str(self.version1.id), .99), (str(self.version2.id), .99)] ) def test_class_filter_list_elements(self): self.populate_classified_elements() element = Element.objects.filter(type=self.classified.id).first() element.classifications.create( - source_id=DataSource.objects.create(type=MLToolType.NER, slug='ner', internal=False).id, - ml_class_id=self.text.id, + ml_class=self.text, confidence=.1337, high_confidence=True, ) @@ -482,7 +485,7 @@ class TestClasses(FixtureAPITestCase): element = Element.objects.filter(type=self.classified.id).first() element.classifications.all().delete() element.classifications.create( - source_id=DataSource.objects.create(type=MLToolType.NER, slug='ner', internal=False).id, + worker_version=self.version2, ml_class_id=self.text.id, confidence=.1337, high_confidence=True, @@ -507,7 +510,7 @@ class TestClasses(FixtureAPITestCase): element = Element.objects.filter(type=self.classified.id).first() element.classifications.all().delete() element.classifications.create( - source_id=DataSource.objects.create(type=MLToolType.NER, slug='ner', internal=False).id, + worker_version=self.version2, ml_class_id=self.text.id, confidence=.1337, high_confidence=False, diff --git a/arkindex/documents/tests/test_create_elements.py b/arkindex/documents/tests/test_create_elements.py index 607bbfbcb5..5c1423a3b0 100644 --- a/arkindex/documents/tests/test_create_elements.py +++ b/arkindex/documents/tests/test_create_elements.py @@ -65,7 +65,6 @@ class TestCreateElements(FixtureAPITestCase): 'type': volume.type.slug, 'thumbnail_put_url': None, 'thumbnail_url': volume.thumbnail.s3_url, - 'source': None, 'worker_version': None, 'classifications': [], 'metadata': [], @@ -104,7 +103,6 @@ class TestCreateElements(FixtureAPITestCase): 'type': page.type.slug, 'thumbnail_put_url': None, 'thumbnail_url': None, - 'source': None, 'worker_version': None, 'classifications': [], 'metadata': [], @@ -147,7 +145,6 @@ class TestCreateElements(FixtureAPITestCase): act = Element.objects.get(id=response.json()['id']) self.assertEqual(act.name, 'Castle story') self.assertEqual(act.type, self.act_type) - self.assertEqual(act.source, None) self.assertEqual(act.worker_version, self.worker_version) def test_create_element_polygon(self): @@ -176,7 +173,6 @@ class TestCreateElements(FixtureAPITestCase): 'type': page.type.slug, 'thumbnail_put_url': None, 'thumbnail_url': None, - 'source': None, 'worker_version': None, 'classifications': [], 'metadata': [], diff --git a/arkindex/documents/tests/test_create_transcriptions.py b/arkindex/documents/tests/test_create_transcriptions.py index fcc5f14034..d48b2125a1 100644 --- a/arkindex/documents/tests/test_create_transcriptions.py +++ b/arkindex/documents/tests/test_create_transcriptions.py @@ -6,10 +6,9 @@ from django.urls import reverse from rest_framework import status from arkindex.dataimport.models import WorkerVersion -from arkindex.documents.models import Corpus, DataSource, Transcription +from arkindex.documents.models import Corpus, Transcription from arkindex.project.tests import FixtureAPITestCase from arkindex.users.models import User -from arkindex_common.ml_tool import MLToolType class TestTranscriptionCreate(FixtureAPITestCase): @@ -21,7 +20,6 @@ class TestTranscriptionCreate(FixtureAPITestCase): def setUpTestData(cls): super().setUpTestData() cls.page = cls.corpus.elements.get(name='Volume 1, page 1r') - cls.src = DataSource.objects.get(slug='test', type=MLToolType.Recognizer) cls.line = cls.corpus.elements.filter(type__slug='text_line').first() cls.private_corpus = Corpus.objects.create(name='Private') cls.private_page = cls.private_corpus.elements.create(type=cls.page.type) @@ -77,7 +75,6 @@ class TestTranscriptionCreate(FixtureAPITestCase): 'id': str(tr.id), 'score': None, 'text': 'A perfect day in a perfect place', - 'source': None, 'worker_version_id': None, }) @@ -174,7 +171,6 @@ class TestTranscriptionCreate(FixtureAPITestCase): self.assertDictEqual(response.json(), { 'id': str(tr.id), 'score': .42, - 'source': None, 'text': 'NEKUDOTAYIM', 'worker_version_id': str(self.worker_version.id), }) diff --git a/arkindex/documents/tests/test_datasource.py b/arkindex/documents/tests/test_datasource.py deleted file mode 100644 index 2730d7ccaa..0000000000 --- a/arkindex/documents/tests/test_datasource.py +++ /dev/null @@ -1,17 +0,0 @@ -from django.test import TestCase - -from arkindex.documents.models import DataSource -from arkindex_common.ml_tool import MLToolType - - -class TestDataSource(TestCase): - - def test_str(self): - ds = DataSource( - type=MLToolType.Classifier, - slug='something', - name='some classifier', - revision='1.2.3', - internal=False, - ) - self.assertEqual(str(ds), 'something 1.2.3') diff --git a/arkindex/documents/tests/test_edit_transcriptions.py b/arkindex/documents/tests/test_edit_transcriptions.py index 1e5f1bd3f2..15147f17fd 100644 --- a/arkindex/documents/tests/test_edit_transcriptions.py +++ b/arkindex/documents/tests/test_edit_transcriptions.py @@ -3,10 +3,9 @@ from uuid import uuid4 from django.urls import reverse from rest_framework import status -from arkindex.documents.models import Corpus, DataSource, Element, Transcription +from arkindex.documents.models import Corpus, Element, Transcription from arkindex.project.tests import FixtureAPITestCase from arkindex.users.models import User -from arkindex_common.ml_tool import MLToolType class TestEditTranscription(FixtureAPITestCase): @@ -35,15 +34,8 @@ class TestEditTranscription(FixtureAPITestCase): cls.corpus.corpus_right.create(user=cls.write_user, can_write=True) def setUp(self): - self.manual_source = DataSource.objects.create(type=MLToolType.Recognizer, slug='manual', internal=False) - self.manual_transcription = self.line.transcriptions.create( - text='A manual transcription', - source=self.manual_source, - ) - self.private_transcription = self.private_page.transcriptions.create( - text='PEPE', - source=self.manual_source - ) + self.manual_transcription = self.line.transcriptions.create(text='A manual transcription') + self.private_transcription = self.private_page.transcriptions.create(text='PEPE') def test_transcription_retrieve(self): """ @@ -53,18 +45,9 @@ class TestEditTranscription(FixtureAPITestCase): response = self.client.get(reverse('api:transcription-edit', kwargs={'pk': self.manual_transcription.id})) self.assertEqual(response.status_code, status.HTTP_200_OK) tr = Transcription.objects.get(id=response.json()['id']) - self.manual_source.refresh_from_db() self.assertDictEqual(response.json(), { 'id': str(tr.id), 'score': None, - 'source': { - 'id': str(self.manual_source.id), - 'internal': False, - 'name': '', - 'revision': '', - 'slug': 'manual', - 'type': 'recognizer' - }, 'text': 'A manual transcription', 'worker_version_id': None, }) @@ -146,7 +129,6 @@ class TestEditTranscription(FixtureAPITestCase): 'score': .42, 'zone': {'polygon': [[4, 2], [2, 4], [3, 3]], 'center': [42, 1337]}, 'image': {'status': 'checked'}, - 'source': 'castle_source', 'element': 'water' } ) @@ -155,14 +137,6 @@ class TestEditTranscription(FixtureAPITestCase): self.assertDictEqual(response.json(), { 'id': str(manual_tr_id), 'score': None, - 'source': { - 'id': str(self.manual_source.id), - 'internal': False, - 'name': '', - 'revision': '', - 'slug': 'manual', - 'type': 'recognizer' - }, 'text': 'a knight was living lonely', 'worker_version_id': None, }) diff --git a/arkindex/documents/tests/test_entities.py b/arkindex/documents/tests/test_entities.py index ba6efbf003..1f72233cc5 100644 --- a/arkindex/documents/tests/test_entities.py +++ b/arkindex/documents/tests/test_entities.py @@ -1,6 +1,7 @@ from django.core.exceptions import ValidationError -from arkindex.documents.models import Corpus, DataSource, Entity, EntityLink, EntityRole, MetaData, MLToolType +from arkindex.dataimport.models import WorkerVersion +from arkindex.documents.models import Corpus, Entity, EntityLink, EntityRole, MetaData from arkindex.project.tests import FixtureTestCase from arkindex_common.enums import EntityType, MetaType @@ -10,16 +11,21 @@ class TestSaveEntities(FixtureTestCase): @classmethod def setUpTestData(cls): super().setUpTestData() - source = DataSource.objects.create(type=MLToolType.NER, slug='entity', internal=False) + worker_version = WorkerVersion.objects.first() cls.corpus1 = Corpus.objects.create(name='corpus 1') cls.corpus2 = Corpus.objects.create(name='corpus 2') cls.parent = Entity.objects.create( name='parent', type=EntityType.Organization, corpus=cls.corpus1, - source=source + worker_version=worker_version, + ) + cls.child = Entity.objects.create( + type=EntityType.Person, + corpus=cls.corpus1, + name="child", + worker_version=worker_version, ) - cls.child = Entity.objects.create(type=EntityType.Person, corpus=cls.corpus1, name="child", source=source) cls.role = EntityRole.objects.create( parent_name='organization', child_name='person', diff --git a/arkindex/documents/tests/test_entities_api.py b/arkindex/documents/tests/test_entities_api.py index 3910f54e78..c1e945977c 100644 --- a/arkindex/documents/tests/test_entities_api.py +++ b/arkindex/documents/tests/test_entities_api.py @@ -8,17 +8,7 @@ from elasticsearch.exceptions import NotFoundError from rest_framework import status from arkindex.dataimport.models import WorkerVersion -from arkindex.documents.models import ( - Corpus, - DataSource, - Element, - Entity, - EntityLink, - EntityRole, - EntityType, - MLToolType, - TranscriptionEntity, -) +from arkindex.documents.models import Corpus, Element, Entity, EntityLink, EntityRole, EntityType, TranscriptionEntity from arkindex.project.tests import FixtureAPITestCase from arkindex_common.enums import MetaType @@ -28,15 +18,9 @@ class TestEntitiesAPI(FixtureAPITestCase): @classmethod def setUpTestData(cls): super().setUpTestData() - cls.entity_source = DataSource.objects.create( - type=MLToolType.NER, - slug='entity', - name='Test NER', - internal=True, - ) - cls.source = DataSource.objects.get(slug='test', type=MLToolType.Recognizer) cls.private_corpus = Corpus.objects.create(name='private') - cls.worker_version = WorkerVersion.objects.get(worker__slug='reco') + cls.worker_version_1 = WorkerVersion.objects.get(worker__slug='reco') + cls.worker_version_2 = WorkerVersion.objects.get(worker__slug='dla') cls.page = cls.corpus.elements.get(name='Volume 1, page 1r') cls.element_type = cls.corpus.types.get(slug='text_line') @@ -46,13 +30,13 @@ class TestEntitiesAPI(FixtureAPITestCase): type=EntityType.Person, corpus=self.corpus, name='entity 1', - source_id=self.source.id + worker_version=self.worker_version_1, ) self.entity_bis = Entity.objects.create( type=EntityType.Location, corpus=self.corpus, name='entity 2', - source_id=self.source.id + worker_version=self.worker_version_2, ) self.role = EntityRole.objects.create( parent_name="parent", @@ -68,11 +52,11 @@ class TestEntitiesAPI(FixtureAPITestCase): type=self.element_type, name='Transcription', zone=zone, - source=self.source, + worker_version=self.worker_version_1, ) self.transcription = self.element.transcriptions.create( text='Some transcribed text', - source=self.source, + worker_version=self.worker_version_1, ) self.metadata = self.element.metadatas.create( name='test 1', @@ -129,7 +113,7 @@ class TestEntitiesAPI(FixtureAPITestCase): name='001', zone=zone, ) - elt_tr = elt.transcriptions.create(source_id=self.source.id, text='goodbye') + elt_tr = elt.transcriptions.create(worker_version=self.worker_version_1, text='goodbye') TranscriptionEntity.objects.create(transcription=elt_tr, entity=self.entity, offset=42, length=7) with self.assertNumQueries(8): response = self.client.get(reverse('api:entity-elements', kwargs={'pk': str(self.entity.id)})) @@ -239,8 +223,6 @@ class TestEntitiesAPI(FixtureAPITestCase): @patch('arkindex.project.triggers.tasks.reindex_start.delay') def test_create_entity_person(self, delay_mock): - self.entity_source.internal = True - self.entity_source.save() data = { 'name': 'entity', 'type': EntityType.Person.value, @@ -249,7 +231,7 @@ class TestEntitiesAPI(FixtureAPITestCase): 'key': 'value', 'other key': 'other value' }, - 'worker_version': str(self.worker_version.id) + 'worker_version': str(self.worker_version_1.id) } self.client.force_login(self.user) response = self.client.post(reverse('api:entity-create'), data=data, format='json') @@ -257,7 +239,7 @@ class TestEntitiesAPI(FixtureAPITestCase): entity = Entity.objects.get(id=response.json()['id']) self.assertEqual(entity.name, 'entity') self.assertEqual(entity.raw_dates, None) - self.assertEqual(entity.worker_version, self.worker_version) + self.assertEqual(entity.worker_version, self.worker_version_1) self.assertEqual(delay_mock.call_count, 1) self.assertEqual(delay_mock.call_args, call( corpus_id=None, @@ -273,8 +255,6 @@ class TestEntitiesAPI(FixtureAPITestCase): @patch('arkindex.project.triggers.tasks.reindex_start.delay') def test_create_entity_number(self, delay_mock): - self.entity_source.internal = True - self.entity_source.save() data = { 'name': '300g', 'type': EntityType.Number.value, @@ -283,7 +263,7 @@ class TestEntitiesAPI(FixtureAPITestCase): 'key': 'value', 'other key': 'other value' }, - 'worker_version': str(self.worker_version.id) + 'worker_version': str(self.worker_version_1.id) } self.client.force_login(self.user) response = self.client.post(reverse('api:entity-create'), data=data, format='json') @@ -291,7 +271,7 @@ class TestEntitiesAPI(FixtureAPITestCase): entity = Entity.objects.get(id=response.json()['id']) self.assertEqual(entity.name, '300g') self.assertEqual(entity.raw_dates, None) - self.assertEqual(entity.worker_version, self.worker_version) + self.assertEqual(entity.worker_version, self.worker_version_1) self.assertEqual(delay_mock.call_count, 1) self.assertEqual(delay_mock.call_args, call( corpus_id=None, @@ -307,8 +287,6 @@ class TestEntitiesAPI(FixtureAPITestCase): @patch('arkindex.project.triggers.tasks.reindex_start.delay') def test_create_entity_date(self, delay_mock): - self.entity_source.internal = True - self.entity_source.save() data = { 'name': '1789', 'type': EntityType.Date.value, @@ -317,7 +295,7 @@ class TestEntitiesAPI(FixtureAPITestCase): 'key': 'value', 'other key': 'other value' }, - 'worker_version': str(self.worker_version.id) + 'worker_version': str(self.worker_version_1.id) } self.client.force_login(self.user) response = self.client.post(reverse('api:entity-create'), data=data, format='json') @@ -325,7 +303,7 @@ class TestEntitiesAPI(FixtureAPITestCase): entity = Entity.objects.get(id=response.json()['id']) self.assertEqual(entity.name, '1789') self.assertEqual(entity.raw_dates, entity.name) - self.assertEqual(entity.worker_version, self.worker_version) + self.assertEqual(entity.worker_version, self.worker_version_1) self.assertEqual(delay_mock.call_count, 1) self.assertEqual(delay_mock.call_args, call( corpus_id=None, @@ -348,7 +326,7 @@ class TestEntitiesAPI(FixtureAPITestCase): 'key': 'value', 'other key': 'other value' }, - 'ner': self.entity_source.slug + 'worker_version': str(self.worker_version_1.id) } response = self.client.post(reverse('api:entity-create'), data=data, format='json') self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) @@ -363,15 +341,14 @@ class TestEntitiesAPI(FixtureAPITestCase): 'key': 'value', 'other key': 'other value' }, - 'worker_version': str(self.worker_version.id) + 'worker_version': str(self.worker_version_1.id) } self.client.force_login(self.user) response = self.client.post(reverse('api:entity-create'), data=data, format='json') self.assertEqual(response.status_code, status.HTTP_201_CREATED) entity = Entity.objects.get(id=response.json()['id']) self.assertEqual(entity.name, '1789') - self.assertEqual(entity.source, None) - self.assertEqual(entity.worker_version, self.worker_version) + self.assertEqual(entity.worker_version, self.worker_version_1) self.assertEqual(delay_mock.call_count, 1) self.assertEqual(delay_mock.call_args, call( corpus_id=None, @@ -390,7 +367,7 @@ class TestEntitiesAPI(FixtureAPITestCase): type=EntityType.Location, corpus=self.corpus, name="child", - source_id=self.source.id + worker_version=self.worker_version_1, ) data = { 'parent': str(self.entity.id), @@ -410,7 +387,7 @@ class TestEntitiesAPI(FixtureAPITestCase): type=EntityType.Location, corpus=self.corpus, name="child", - source_id=self.source.id + worker_version=self.worker_version_1, ) data = { 'parent': str(self.entity.id), @@ -425,7 +402,7 @@ class TestEntitiesAPI(FixtureAPITestCase): type=EntityType.Person, corpus=self.corpus, name="child", - source_id=self.source.id + worker_version=self.worker_version_1, ) data = { 'parent': str(self.entity.id), @@ -489,7 +466,7 @@ class TestEntitiesAPI(FixtureAPITestCase): type=EntityType.Person, corpus=self.private_corpus, name="a private entity", - source_id=self.source.id + worker_version=self.worker_version_1, ) self.tr_entities_sample.update({'entity': ent.id}) response = self.client.post( @@ -563,15 +540,7 @@ class TestEntitiesAPI(FixtureAPITestCase): 'metas': None, 'validated': self.entity_bis.validated, 'dates': [], - 'source': { - 'id': str(self.source.id), - 'type': self.source.type.value, - 'slug': self.source.slug, - 'name': self.source.name, - 'revision': self.source.revision, - 'internal': self.source.internal, - }, - 'worker_version_id': None, + 'worker_version_id': str(self.worker_version_2.id), }, 'length': self.transcriptionentity.length, 'offset': self.transcriptionentity.offset @@ -579,10 +548,6 @@ class TestEntitiesAPI(FixtureAPITestCase): ) def test_list_transcription_entities_worker_version(self): - self.entity_bis.source = None - self.entity_bis.worker_version = self.worker_version - self.entity_bis.save() - response = self.client.get(reverse('api:transcription-entities', kwargs={'pk': str(self.transcription.id)})) self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertListEqual( @@ -595,8 +560,7 @@ class TestEntitiesAPI(FixtureAPITestCase): 'metas': None, 'validated': False, 'dates': [], - 'source': None, - 'worker_version_id': str(self.worker_version.id), + 'worker_version_id': str(self.worker_version_2.id), }, 'length': 8, 'offset': 2 @@ -627,10 +591,11 @@ class TestEntitiesAPI(FixtureAPITestCase): md = self.element.metadatas.create(name='some_metadata', type=MetaType.Location, value='something') md.entity = self.entity_bis md.save() - with self.assertNumQueries(9): + with self.assertNumQueries(6): response = self.client.get(reverse('api:element-entities', kwargs={'pk': str(self.element.id)})) self.assertEqual(response.status_code, status.HTTP_200_OK) data = response.json() + self.maxDiff = None self.assertDictEqual( data, { @@ -645,15 +610,7 @@ class TestEntitiesAPI(FixtureAPITestCase): 'metas': None, 'validated': t.entity.validated, 'dates': [], - 'source': { - 'id': str(self.source.id), - 'type': self.source.type.value, - 'slug': self.source.slug, - 'name': self.source.name, - 'revision': self.source.revision, - 'internal': self.source.internal, - }, - 'worker_version_id': None, + 'worker_version_id': str(self.worker_version_2.id), }, 'offset': t.offset, 'length': t.length @@ -666,15 +623,7 @@ class TestEntitiesAPI(FixtureAPITestCase): 'metas': None, 'validated': m.entity.validated, 'dates': [], - 'source': { - 'id': str(self.source.id), - 'type': self.source.type.value, - 'slug': self.source.slug, - 'name': self.source.name, - 'revision': self.source.revision, - 'internal': self.source.internal, - }, - 'worker_version_id': None, + 'worker_version_id': str(m.entity.worker_version_id), }, 'id': str(m.id), 'type': m.type.value, @@ -704,14 +653,10 @@ class TestEntitiesAPI(FixtureAPITestCase): self.assertEqual(response.json(), {'worker_version': ['This worker version does not exist.']}) def test_list_element_entities_worker_version(self): - self.entity.source = None - self.entity.worker_version = self.worker_version - self.entity.save() - with self.assertNumQueries(6): response = self.client.get( reverse('api:element-entities', kwargs={'pk': str(self.element.id)}), - data={'worker_version': str(self.worker_version.id)} + data={'worker_version': str(self.worker_version_1.id)} ) self.assertEqual(response.status_code, status.HTTP_200_OK) @@ -737,8 +682,7 @@ class TestEntitiesAPI(FixtureAPITestCase): 'validated': False, 'dates': [], 'metas': None, - 'source': None, - 'worker_version_id': str(self.worker_version.id), + 'worker_version_id': str(self.worker_version_1.id), }, } ], @@ -883,15 +827,7 @@ class TestEntitiesAPI(FixtureAPITestCase): 'metas': None, 'validated': self.entity.validated, 'dates': [], - 'source': { - 'id': str(self.source.id), - 'type': self.source.type.value, - 'slug': self.source.slug, - 'name': self.source.name, - 'revision': self.source.revision, - 'internal': self.source.internal, - }, - 'worker_version_id': None, + 'worker_version_id': str(self.worker_version_1.id), }, 'child': { 'id': str(self.entity_bis.id), @@ -900,15 +836,7 @@ class TestEntitiesAPI(FixtureAPITestCase): 'metas': None, 'validated': self.entity_bis.validated, 'dates': [], - 'source': { - 'id': str(self.source.id), - 'type': self.source.type.value, - 'slug': self.source.slug, - 'name': self.source.name, - 'revision': self.source.revision, - 'internal': self.source.internal, - }, - 'worker_version_id': None, + 'worker_version_id': str(self.worker_version_2.id), }, 'role': { 'id': self.role.id, @@ -934,7 +862,7 @@ class TestEntitiesAPI(FixtureAPITestCase): 'name': 'entity', 'type': EntityType.Person.value, 'corpus': str(self.corpus.id), - 'worker_version': str(self.worker_version.id) + 'worker_version': str(self.worker_version_1.id) } self.client.force_login(self.user) response = self.client.post(reverse('api:entity-create'), data=data, format='json') diff --git a/arkindex/documents/tests/test_indexer.py b/arkindex/documents/tests/test_indexer.py index 52a7d9a9a2..3981f62a9a 100644 --- a/arkindex/documents/tests/test_indexer.py +++ b/arkindex/documents/tests/test_indexer.py @@ -3,8 +3,8 @@ from unittest.mock import MagicMock, call, patch from elasticsearch import Elasticsearch from elasticsearch.exceptions import NotFoundError +from arkindex.dataimport.models import WorkerVersion from arkindex.documents.indexer import Indexer -from arkindex.documents.models import DataSource, MLToolType from arkindex.project.tests import FixtureTestCase from arkindex_common.enums import EntityType @@ -14,9 +14,13 @@ class TestIndexer(FixtureTestCase): @classmethod def setUpTestData(cls): super().setUpTestData() - source = DataSource.objects.create(type=MLToolType.NER, slug='entity', internal=True) + worker_version = WorkerVersion.objects.first() for i in range(10): - cls.corpus.entities.create(name='ES Dummy {}'.format(i), type=EntityType.Misc, source=source) + cls.corpus.entities.create( + name=f'ES Dummy {i}', + type=EntityType.Misc, + worker_version=worker_version, + ) @patch('arkindex.documents.indexer.Elasticsearch') def test_drop_index(self, es_mock): diff --git a/arkindex/documents/tests/test_manifest.py b/arkindex/documents/tests/test_manifest.py index 659e5d3517..4eaafa5699 100644 --- a/arkindex/documents/tests/test_manifest.py +++ b/arkindex/documents/tests/test_manifest.py @@ -4,10 +4,10 @@ from django.urls import reverse from rest_framework import status from tripoli import IIIFValidator -from arkindex.documents.models import DataSource, Element +from arkindex.dataimport.models import WorkerVersion +from arkindex.documents.models import Element from arkindex.project.tests import FixtureAPITestCase from arkindex_common.enums import MetaType -from arkindex_common.ml_tool import MLToolType class TestFolderManifestSerializer(FixtureAPITestCase): @@ -135,11 +135,11 @@ class TestFolderManifestSerializer(FixtureAPITestCase): def test_with_classification(self): self.assertFalse(self.page.classifications.exists()) - source = DataSource.objects.get(slug='test', type=MLToolType.Recognizer) + worker_version = WorkerVersion.objects.first() text_class = self.corpus.ml_classes.create(name='text') cover_class = self.corpus.ml_classes.create(name='cover') - self.page.classifications.create(ml_class=text_class, confidence=0.42, source=source) - self.page.classifications.create(ml_class=cover_class, confidence=0.12, source=source) + self.page.classifications.create(ml_class=text_class, confidence=0.42, worker_version=worker_version) + self.page.classifications.create(ml_class=cover_class, confidence=0.12, worker_version=worker_version) response = self.client.get(reverse('api:folder-manifest', kwargs={'pk': self.vol.id})) self.assertEqual(response.status_code, status.HTTP_200_OK) diff --git a/arkindex/documents/tests/test_metadata.py b/arkindex/documents/tests/test_metadata.py index 989e83bcf0..1802992d19 100644 --- a/arkindex/documents/tests/test_metadata.py +++ b/arkindex/documents/tests/test_metadata.py @@ -4,11 +4,11 @@ from django.test import override_settings from django.urls import reverse from rest_framework import status -from arkindex.documents.models import AllowedMetaData, Corpus, DataSource, MetaData +from arkindex.dataimport.models import WorkerVersion +from arkindex.documents.models import AllowedMetaData, Corpus, MetaData from arkindex.project.tests import FixtureAPITestCase from arkindex.users.models import User from arkindex_common.enums import EntityType, MetaType -from arkindex_common.ml_tool import MLToolType class TestMetaData(FixtureAPITestCase): @@ -31,7 +31,7 @@ class TestMetaData(FixtureAPITestCase): (MetaType.Reference, '_id'), ) ) - cls.source = DataSource.objects.get(slug='test', type=MLToolType.Recognizer) + cls.worker_version = WorkerVersion.objects.first() def setUp(self): super().setUp() @@ -480,7 +480,11 @@ class TestMetaData(FixtureAPITestCase): def test_create_metadata_entity(self): self.client.force_login(self.superuser) - entity = self.corpus.entities.create(name='Texas', type=EntityType.Location, source=self.source) + entity = self.corpus.entities.create( + name='Texas', + type=EntityType.Location, + worker_version=self.worker_version, + ) response = self.client.post( reverse('api:element-metadata', kwargs={'pk': str(self.vol.id)}), data={'type': 'location', 'name': 'location', 'value': 'Texas', 'entity': entity.id} @@ -490,7 +494,11 @@ class TestMetaData(FixtureAPITestCase): def test_patch_metadata_entity(self): self.client.force_login(self.superuser) - entity = self.corpus.entities.create(name='Texas', type=EntityType.Location, source=self.source) + entity = self.corpus.entities.create( + name='Texas', + type=EntityType.Location, + worker_version=self.worker_version, + ) metadata = self.vol.metadatas.create(type=MetaType.Location, name='location', value='Texas') response = self.client.patch( reverse('api:metadata-edit', kwargs={'pk': str(metadata.id)}), @@ -502,7 +510,11 @@ class TestMetaData(FixtureAPITestCase): def test_patch_metadata_entity_none(self): self.client.force_login(self.superuser) - entity = self.corpus.entities.create(name='Texas', type=EntityType.Location, source=self.source) + entity = self.corpus.entities.create( + name='Texas', + type=EntityType.Location, + worker_version=self.worker_version, + ) metadata = self.vol.metadatas.create(type=MetaType.Location, name='location', value='Texas', entity=entity) response = self.client.patch( reverse('api:metadata-edit', kwargs={'pk': str(metadata.id)}), @@ -515,7 +527,11 @@ class TestMetaData(FixtureAPITestCase): def test_create_metadata_entity_corpus_check(self): self.client.force_login(self.superuser) - entity = self.private_corpus.entities.create(name='Texas', type=EntityType.Location, source=self.source) + entity = self.private_corpus.entities.create( + name='Texas', + type=EntityType.Location, + worker_version=self.worker_version, + ) response = self.client.post( reverse('api:element-metadata', kwargs={'pk': str(self.vol.id)}), data={'type': 'location', 'name': 'location', 'value': 'Texas', 'entity': entity.id} @@ -524,7 +540,11 @@ class TestMetaData(FixtureAPITestCase): def test_patch_metadata_entity_corpus_check(self): self.client.force_login(self.superuser) - entity = self.private_corpus.entities.create(name='Texas', type=EntityType.Location, source=self.source) + entity = self.private_corpus.entities.create( + name='Texas', + type=EntityType.Location, + worker_version=self.worker_version, + ) metadata = self.vol.metadatas.create(type=MetaType.Location, name='location', value='Texas') response = self.client.patch( reverse('api:metadata-edit', kwargs={'pk': str(metadata.id)}), diff --git a/arkindex/documents/tests/test_ml_results.py b/arkindex/documents/tests/test_ml_results.py deleted file mode 100644 index 83e932fc02..0000000000 --- a/arkindex/documents/tests/test_ml_results.py +++ /dev/null @@ -1,225 +0,0 @@ -from unittest.mock import call, patch - -from django.test import override_settings -from django.urls import reverse -from rest_framework import status - -from arkindex.documents.models import DataSource, Entity, Transcription -from arkindex.project.tests import FixtureTestCase -from arkindex_common.enums import EntityType, MetaType - - -class TestMLResults(FixtureTestCase): - """ - Tests for ML results-related APIs - """ - - @classmethod - def setUpTestData(cls): - super().setUpTestData() - cls.recognizer = DataSource.objects.get(name='Test Recognizer') - cls.folder = cls.corpus.elements.get(name='Volume 1', type__folder=True) - cls.page = cls.corpus.elements.get(name='Volume 1, page 1r') - - entity = cls.corpus.entities.create( - name='An entity', - type=EntityType.Misc, - source=cls.recognizer, - ) - entity2 = cls.corpus.entities.create( - name='Another entity', - type=EntityType.Misc, - source=cls.recognizer, - ) - - ml_class = cls.corpus.ml_classes.create(name='Some class') - cls.folder.classifications.create(ml_class=ml_class, source=cls.recognizer, confidence=0.42) - cls.page.classifications.create(ml_class=ml_class, source=cls.recognizer, confidence=0.85) - cls.page.metadatas.create(name='key', value='value', type=MetaType.Text, entity=entity) - cls.page.transcriptions.first().transcription_entities.create( - entity=entity2, offset=0, length=1, - ) - - def test_element_stats_requires_login(self): - response = self.client.get(reverse('api:element-ml-stats', kwargs={'pk': str(self.page.id)})) - self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) - - def test_element_stats_requires_admin(self): - self.client.force_login(self.user) - response = self.client.get(reverse('api:element-ml-stats', kwargs={'pk': str(self.page.id)})) - self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) - - def test_element_stats(self): - self.client.force_login(self.superuser) - self.assertEqual(self.page.transcriptions.count(), 1) - self.assertEqual(self.page.classifications.count(), 1) - self.assertEqual(Entity.objects.filter(transcriptions__element=self.page).count(), 1) - self.assertEqual(Entity.objects.filter(metadatas__element=self.page).count(), 1) - - with self.assertNumQueries(7): - response = self.client.get(reverse('api:element-ml-stats', kwargs={'pk': str(self.page.id)})) - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertListEqual(response.json(), [ - { - 'id': str(self.recognizer.id), - 'type': 'recognizer', - 'slug': 'test', - 'name': 'Test Recognizer', - 'revision': '4.2', - 'internal': False, - 'classifications_count': 1, - 'transcriptions_count': 1, - 'entities_count': 2, - } - ]) - - def test_element_stats_folder(self): - self.client.force_login(self.superuser) - with self.assertNumQueries(7): - response = self.client.get(reverse('api:element-ml-stats', kwargs={'pk': str(self.folder.id)})) - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertListEqual(response.json(), [ - { - 'id': str(self.recognizer.id), - 'type': 'recognizer', - 'slug': 'test', - 'name': 'Test Recognizer', - 'revision': '4.2', - 'internal': False, - 'classifications_count': 2, - 'transcriptions_count': 10, - 'entities_count': 2, - } - ]) - - def test_corpus_stats_requires_login(self): - response = self.client.get(reverse('api:corpus-ml-stats', kwargs={'pk': str(self.corpus.id)})) - self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) - - def test_corpus_stats_requires_admin(self): - self.client.force_login(self.user) - response = self.client.get(reverse('api:corpus-ml-stats', kwargs={'pk': str(self.corpus.id)})) - self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) - - def test_corpus_stats(self): - self.client.force_login(self.superuser) - self.assertEqual(self.folder.classifications.count(), 1) - self.assertEqual(self.page.classifications.count(), 1) - self.assertEqual(Entity.objects.filter(transcriptions__element=self.page).count(), 1) - self.assertEqual(Entity.objects.filter(metadatas__element=self.page).count(), 1) - self.assertEqual(Transcription.objects.filter(element__corpus=self.corpus).count(), 10) - - with self.assertNumQueries(7): - response = self.client.get(reverse('api:corpus-ml-stats', kwargs={'pk': str(self.corpus.id)})) - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertListEqual(response.json(), [ - { - 'id': str(self.recognizer.id), - 'type': 'recognizer', - 'slug': 'test', - 'name': 'Test Recognizer', - 'revision': '4.2', - 'internal': False, - 'classifications_count': 2, - 'transcriptions_count': 10, - 'entities_count': 2, - } - ]) - - def test_corpus_destroy_results_requires_login(self): - response = self.client.delete(reverse('api:corpus-ml-stats', kwargs={'pk': str(self.corpus.id)})) - self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) - - def test_corpus_destroy_results_requires_admin(self): - self.client.force_login(self.user) - response = self.client.delete(reverse('api:corpus-ml-stats', kwargs={'pk': str(self.corpus.id)})) - self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) - - @patch('arkindex.project.triggers.tasks.ml_results_delete.delay') - @patch('arkindex.project.triggers.tasks.reindex_start.delay') - def test_corpus_destroy_results(self, reindex_delay_mock, delete_delay_mock): - self.client.force_login(self.superuser) - delete_delay_mock.return_value = 'a' - response = self.client.delete(reverse('api:corpus-ml-stats', kwargs={'pk': str(self.corpus.id)})) - self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT) - self.assertEqual(delete_delay_mock.call_count, 1) - self.assertEqual(delete_delay_mock.call_args, call( - corpus_id=str(self.corpus.id), - element_id=None, - batch_size=1000, - user_id=self.superuser.id, - description=f'ML results deletion on corpus {self.corpus.id}', - )) - self.assertEqual(reindex_delay_mock.call_count, 1) - self.assertEqual(reindex_delay_mock.call_args, call( - corpus_id=str(self.corpus.id), - element_id=None, - depends_on='a', - )) - - @override_settings(ARKINDEX_FEATURES={'search': False}) - @patch('arkindex.project.triggers.tasks.ml_results_delete.delay') - @patch('arkindex.project.triggers.tasks.reindex_start.delay') - def test_corpus_destroy_results_no_search(self, reindex_delay_mock, delete_delay_mock): - self.client.force_login(self.superuser) - delete_delay_mock.return_value = 'a' - response = self.client.delete(reverse('api:corpus-ml-stats', kwargs={'pk': str(self.corpus.id)})) - self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT) - self.assertEqual(delete_delay_mock.call_count, 1) - self.assertEqual(delete_delay_mock.call_args, call( - corpus_id=str(self.corpus.id), - element_id=None, - batch_size=1000, - user_id=self.superuser.id, - description=f'ML results deletion on corpus {self.corpus.id}', - )) - self.assertFalse(reindex_delay_mock.called) - - def test_element_destroy_results_requires_login(self): - response = self.client.delete(reverse('api:element-ml-stats', kwargs={'pk': str(self.page.id)})) - self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) - - def test_element_destroy_results_requires_admin(self): - self.client.force_login(self.user) - response = self.client.delete(reverse('api:element-ml-stats', kwargs={'pk': str(self.page.id)})) - self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) - - @patch('arkindex.project.triggers.tasks.ml_results_delete.delay') - @patch('arkindex.project.triggers.tasks.reindex_start.delay') - def test_element_destroy_results(self, reindex_delay_mock, delete_delay_mock): - self.client.force_login(self.superuser) - delete_delay_mock.return_value = 'a' - response = self.client.delete(reverse('api:element-ml-stats', kwargs={'pk': str(self.page.id)})) - self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT) - self.assertEqual(delete_delay_mock.call_count, 1) - self.assertEqual(delete_delay_mock.call_args, call( - corpus_id=None, - element_id=str(self.page.id), - batch_size=1000, - user_id=self.superuser.id, - description=f'ML results deletion on element {self.page.id}', - )) - self.assertEqual(reindex_delay_mock.call_count, 1) - self.assertEqual(reindex_delay_mock.call_args, call( - corpus_id=None, - element_id=str(self.page.id), - depends_on='a', - )) - - @override_settings(ARKINDEX_FEATURES={'search': False}) - @patch('arkindex.project.triggers.tasks.ml_results_delete.delay') - @patch('arkindex.project.triggers.tasks.reindex_start.delay') - def test_element_destroy_results_no_search(self, reindex_delay_mock, delete_delay_mock): - self.client.force_login(self.superuser) - delete_delay_mock.return_value = 'a' - response = self.client.delete(reverse('api:element-ml-stats', kwargs={'pk': str(self.page.id)})) - self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT) - self.assertEqual(delete_delay_mock.call_count, 1) - self.assertEqual(delete_delay_mock.call_args, call( - corpus_id=None, - element_id=str(self.page.id), - batch_size=1000, - user_id=self.superuser.id, - description=f'ML results deletion on element {self.page.id}', - )) - self.assertFalse(reindex_delay_mock.called) diff --git a/arkindex/documents/tests/test_moderation.py b/arkindex/documents/tests/test_moderation.py index c8819aeaf4..bc0a9b222e 100644 --- a/arkindex/documents/tests/test_moderation.py +++ b/arkindex/documents/tests/test_moderation.py @@ -3,15 +3,7 @@ from django.urls import reverse from rest_framework import status from arkindex.dataimport.models import WorkerVersion -from arkindex.documents.models import ( - Classification, - ClassificationState, - Corpus, - DataSource, - Element, - MLClass, - MLToolType, -) +from arkindex.documents.models import Classification, ClassificationState, Corpus, Element, MLClass from arkindex.project.tests import FixtureAPITestCase from arkindex.users.models import User @@ -26,31 +18,9 @@ class TestClasses(FixtureAPITestCase): cls.act_type = cls.corpus.types.get(slug='act') cls.element = Element.objects.get(name='Volume 1, page 1v') cls.folder = cls.corpus.elements.get(name='Volume 1') - cls.worker_version = WorkerVersion.objects.get(worker__slug='dla') + cls.worker_version_1 = WorkerVersion.objects.get(worker__slug='dla') + cls.worker_version_2 = WorkerVersion.objects.get(worker__slug='reco') cls.internal_user = User.objects.get_by_natural_key('internal@internal.fr') - cls.classifier_source = DataSource.objects.create( - type=MLToolType.Classifier, - slug='some_classifier', - revision='1.3.3.7', - internal=False, - ) - - def _create_classification_from_source(self): - return self.element.classifications.create( - source=self.classifier_source, - ml_class=self.text, - confidence=.5, - ) - - def _serialized_source(self, classification): - return { - 'id': str(classification.source.id), - 'type': classification.source.type.value, - 'slug': classification.source.slug, - 'name': classification.source.name, - 'revision': classification.source.revision, - 'internal': classification.source.internal - } def test_manual_classification_creation(self): """ @@ -192,7 +162,7 @@ class TestClasses(FixtureAPITestCase): response = self.client.post(reverse('api:classification-create'), { 'element': str(self.element.id), 'ml_class': str(self.text.id), - 'worker_version': str(self.worker_version.id) + 'worker_version': str(self.worker_version_1.id) }) self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) @@ -210,12 +180,12 @@ class TestClasses(FixtureAPITestCase): 'ml_class': str(self.text.id), 'confidence': 0.42, 'high_confidence': False, - 'worker_version': str(self.worker_version.id) + 'worker_version': str(self.worker_version_1.id) }) self.assertEqual(response.status_code, status.HTTP_201_CREATED) classification = self.element.classifications.get() - self.assertEqual(classification.worker_version, self.worker_version) + self.assertEqual(classification.worker_version, self.worker_version_1) self.assertEqual(classification.ml_class, self.text) self.assertEqual(classification.state, ClassificationState.Pending) self.assertEqual(classification.confidence, 0.42) @@ -227,15 +197,14 @@ class TestClasses(FixtureAPITestCase): response = self.client.post(reverse('api:classification-create'), { 'element': str(self.element.id), 'ml_class': str(self.text.id), - 'worker_version': str(self.worker_version.id), + 'worker_version': str(self.worker_version_1.id), 'confidence': 0.42, 'high_confidence': False, }) self.assertEqual(response.status_code, status.HTTP_201_CREATED) classification = self.element.classifications.get() - self.assertEqual(classification.source, None) - self.assertEqual(classification.worker_version, self.worker_version) + self.assertEqual(classification.worker_version, self.worker_version_1) self.assertEqual(classification.ml_class, self.text) self.assertEqual(classification.state, ClassificationState.Pending) self.assertEqual(classification.confidence, 0.42) @@ -250,14 +219,14 @@ class TestClasses(FixtureAPITestCase): response = self.client.post(reverse('api:classification-create'), { 'element': str(self.element.id), 'ml_class': str(self.text.id), - 'worker_version': str(self.worker_version.id), + 'worker_version': str(self.worker_version_1.id), 'confidence': 0, 'high_confidence': False, }) self.assertEqual(response.status_code, status.HTTP_201_CREATED) classification = self.element.classifications.get() - self.assertEqual(classification.worker_version, self.worker_version) + self.assertEqual(classification.worker_version, self.worker_version_1) self.assertEqual(classification.ml_class, self.text) self.assertEqual(classification.state, ClassificationState.Pending) self.assertEqual(classification.confidence, 0) @@ -265,7 +234,7 @@ class TestClasses(FixtureAPITestCase): def test_classification_validate(self): classification = self.element.classifications.create( - worker_version=self.worker_version, + worker_version=self.worker_version_1, ml_class=self.text, confidence=.1 ) @@ -277,8 +246,7 @@ class TestClasses(FixtureAPITestCase): self.assertDictEqual(response.json(), { 'id': str(classification.id), - 'source': None, - 'worker_version': str(self.worker_version.id), + 'worker_version': str(self.worker_version_1.id), 'ml_class': { 'id': str(classification.ml_class.id), 'name': classification.ml_class.name @@ -293,52 +261,29 @@ class TestClasses(FixtureAPITestCase): self.assertEqual(classification.moderator, self.user) def test_classification_validate_without_permissions(self): - classification = self._create_classification_from_source() + classification = self.element.classifications.create( + ml_class=self.text, + confidence=.5, + ) with self.assertNumQueries(0): response = self.client.put(reverse('api:classification-validate', kwargs={'pk': classification.id})) self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) - def test_source_classification_reject(self): - self.client.force_login(self.user) - classification = self._create_classification_from_source() - - with self.assertNumQueries(6): - response = self.client.put(reverse('api:classification-reject', kwargs={'pk': classification.id})) - self.assertEqual(response.status_code, status.HTTP_200_OK) - - self.assertDictEqual(response.json(), { - 'id': str(classification.id), - 'source': self._serialized_source(classification), - 'worker_version': None, - 'ml_class': { - 'id': str(classification.ml_class.id), - 'name': classification.ml_class.name - }, - 'state': ClassificationState.Rejected.value, - 'confidence': classification.confidence, - 'high_confidence': False - }) - - # Ensure moderator has been set - classification.refresh_from_db() - self.assertEqual(classification.moderator, self.user) - def test_worker_classification_reject(self): self.client.force_login(self.user) classification = self.element.classifications.create( - worker_version=self.worker_version, + worker_version=self.worker_version_1, ml_class=self.text, confidence=.1, ) - with self.assertNumQueries(6): + with self.assertNumQueries(5): response = self.client.put(reverse('api:classification-reject', kwargs={'pk': classification.id})) self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertDictEqual(response.json(), { 'id': str(classification.id), - 'source': None, - 'worker_version': str(self.worker_version.id), + 'worker_version': str(self.worker_version_1.id), 'ml_class': { 'id': str(classification.ml_class.id), 'name': classification.ml_class.name @@ -352,24 +297,6 @@ class TestClasses(FixtureAPITestCase): classification.refresh_from_db() self.assertEqual(classification.moderator, self.user) - def test_classification_reject_manual_source_delete(self): - """ - A rejected classifications from a manual source should be automatically deleted - """ - self.client.force_login(self.user) - classification = self.element.classifications.create( - source=DataSource.objects.create(slug='manual', type=MLToolType.Classifier, internal=False), - ml_class=self.text, - confidence=.42, - ) - - with self.assertNumQueries(5): - response = self.client.put(reverse('api:classification-reject', kwargs={'pk': classification.id})) - self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT) - - with self.assertRaises(Classification.DoesNotExist): - classification.refresh_from_db() - def test_manual_classification_reject_delete(self): """ A classifications with no worker version should be deleted when rejected @@ -385,26 +312,28 @@ class TestClasses(FixtureAPITestCase): classification.refresh_from_db() def test_classification_reject_without_permissions(self): - classification = self._create_classification_from_source() + classification = self.element.classifications.create(ml_class=self.text, confidence=.42) with self.assertNumQueries(0): response = self.client.put(reverse('api:classification-reject', kwargs={'pk': classification.id})) self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) def test_classification_can_still_be_moderated(self): self.client.force_login(self.user) - classification = self._create_classification_from_source() - classification.moderator = self.user - classification.state = ClassificationState.Validated.value - classification.save() + classification = self.element.classifications.create( + ml_class=self.text, + confidence=.5, + moderator=self.user, + state=ClassificationState.Validated, + worker_version=self.worker_version_2, + ) # First try to reject - with self.assertNumQueries(6): + with self.assertNumQueries(5): response = self.client.put(reverse('api:classification-reject', kwargs={'pk': classification.id})) self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertDictEqual(response.json(), { 'id': str(classification.id), - 'source': self._serialized_source(classification), 'ml_class': { 'id': str(classification.ml_class.id), 'name': classification.ml_class.name @@ -412,17 +341,16 @@ class TestClasses(FixtureAPITestCase): 'state': ClassificationState.Rejected.value, 'confidence': classification.confidence, 'high_confidence': False, - 'worker_version': None + 'worker_version': str(self.worker_version_2.id), }) # Then try to validate - with self.assertNumQueries(6): + with self.assertNumQueries(5): response = self.client.put(reverse('api:classification-validate', kwargs={'pk': classification.id})) self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertDictEqual(response.json(), { 'id': str(classification.id), - 'source': self._serialized_source(classification), 'ml_class': { 'id': str(classification.ml_class.id), 'name': classification.ml_class.name @@ -430,7 +358,7 @@ class TestClasses(FixtureAPITestCase): 'state': ClassificationState.Validated.value, 'confidence': classification.confidence, 'high_confidence': False, - 'worker_version': None + 'worker_version': str(self.worker_version_2.id) }) def test_classification_selection_requires_login(self): @@ -535,7 +463,7 @@ class TestClasses(FixtureAPITestCase): [str(self.element.id), str(self.folder.id), str(act_x.id), str(act_y.id)], ) - with self.assertNumQueries(14): + with self.assertNumQueries(10): response = self.client.post( reverse('api:classification-selection'), data={'corpus_id': self.corpus.id, 'ml_class': self.text.id, 'mode': 'create'} @@ -546,26 +474,17 @@ class TestClasses(FixtureAPITestCase): classification = self.folder.classifications.get() self.assertEqual(classification.ml_class, self.text) self.assertEqual(classification.state, ClassificationState.Validated) - self.assertEqual(classification.source.slug, 'manual') + self.assertIsNone(classification.worker_version) self.assertEqual(classification.confidence, 1) self.assertTrue(self.element.classifications.exists()) classification = self.element.classifications.get() self.assertEqual(classification.ml_class, self.text) self.assertEqual(classification.state, ClassificationState.Validated) - self.assertEqual(classification.source.slug, 'manual') + self.assertIsNone(classification.worker_version) self.assertEqual(classification.confidence, 1) def test_classifications_selection_validate(self): - source_1 = DataSource.objects.get(slug='some_classifier') - source_2 = DataSource.objects.create( - type=MLToolType.NER, - slug='test', - name='Test NER', - revision='4.2', - internal=False, - ) - line = MLClass.objects.create(name='line', corpus=self.private_corpus) act_x = Element.objects.create( type=self.act_type, @@ -574,7 +493,7 @@ class TestClasses(FixtureAPITestCase): ) Classification.objects.create( element=act_x, - source=source_1, + worker_version=self.worker_version_1, state=ClassificationState.Pending, high_confidence=True, ml_class=line @@ -583,14 +502,14 @@ class TestClasses(FixtureAPITestCase): for e in [self.folder, self.element]: Classification.objects.create( element=e, - source=source_1, + worker_version=self.worker_version_1, state=ClassificationState.Pending, high_confidence=True, ml_class=self.text ) Classification.objects.create( element=e, - source=source_2, + worker_version=self.worker_version_2, state=ClassificationState.Pending, high_confidence=False, ml_class=self.text @@ -613,11 +532,11 @@ class TestClasses(FixtureAPITestCase): for e in [self.folder, self.element]: classification = e.classifications.get(state=ClassificationState.Validated) self.assertTrue(classification.high_confidence) - self.assertEqual(classification.source, source_1) + self.assertEqual(classification.worker_version, self.worker_version_1) classification = e.classifications.get(state=ClassificationState.Pending) self.assertFalse(classification.high_confidence) - self.assertEqual(classification.source, source_2) + self.assertEqual(classification.worker_version, self.worker_version_2) classification = act_x.classifications.get() self.assertEqual(classification.state, ClassificationState.Pending) diff --git a/arkindex/documents/tests/test_parents_elements.py b/arkindex/documents/tests/test_parents_elements.py index e0a5a797c6..3d79100b54 100644 --- a/arkindex/documents/tests/test_parents_elements.py +++ b/arkindex/documents/tests/test_parents_elements.py @@ -4,9 +4,8 @@ from django.urls import reverse from rest_framework import status from arkindex.dataimport.models import WorkerVersion -from arkindex.documents.models import Corpus, DataSource, Element +from arkindex.documents.models import Corpus, Element from arkindex.project.tests import FixtureAPITestCase -from arkindex_common.ml_tool import MLToolType class TestParentsElements(FixtureAPITestCase): @@ -17,7 +16,6 @@ class TestParentsElements(FixtureAPITestCase): cls.vol = cls.corpus.elements.get(name='Volume 1') cls.private_corpus = Corpus.objects.create(name='private', public=False) cls.private_elt = cls.private_corpus.elements.create(type=cls.private_corpus.types.create(slug='type')) - cls.manual_source = DataSource.objects.create(type=MLToolType.Recognizer, slug='manual', internal=True) cls.worker_version = WorkerVersion.objects.get(worker__slug='reco') def setUp(self): diff --git a/arkindex/documents/tests/test_patch_elements.py b/arkindex/documents/tests/test_patch_elements.py index 993f0ad5e0..e622da2a3b 100644 --- a/arkindex/documents/tests/test_patch_elements.py +++ b/arkindex/documents/tests/test_patch_elements.py @@ -1,12 +1,11 @@ from django.urls import reverse from rest_framework import status -from arkindex.documents.models import Corpus, DataSource, Element +from arkindex.documents.models import Corpus, Element from arkindex.images.models import ImageServer from arkindex.project.aws import S3FileStatus from arkindex.project.tests import FixtureAPITestCase from arkindex.users.models import User -from arkindex_common.ml_tool import MLToolType class TestPatchElements(FixtureAPITestCase): @@ -28,7 +27,6 @@ class TestPatchElements(FixtureAPITestCase): ) cls.private_corpus = Corpus.objects.create(name='private', public=False) cls.private_elt = cls.private_corpus.elements.create(type=cls.private_corpus.types.create(slug='type')) - cls.manual_source = DataSource.objects.create(type=MLToolType.Recognizer, slug='manual', internal=True) def test_patch_element_unverified(self): """ diff --git a/arkindex/documents/tests/test_retrieve_elements.py b/arkindex/documents/tests/test_retrieve_elements.py index b9e593af2f..cd5566b3c2 100644 --- a/arkindex/documents/tests/test_retrieve_elements.py +++ b/arkindex/documents/tests/test_retrieve_elements.py @@ -1,10 +1,10 @@ from django.urls import reverse from rest_framework import status -from arkindex.documents.models import Classification, Corpus, DataSource, Entity, MLClass +from arkindex.dataimport.models import WorkerVersion +from arkindex.documents.models import Corpus, Entity, MLClass from arkindex.project.tests import FixtureAPITestCase from arkindex_common.enums import EntityType, MetaType -from arkindex_common.ml_tool import MLToolType class TestRetrieveElements(FixtureAPITestCase): @@ -14,6 +14,7 @@ class TestRetrieveElements(FixtureAPITestCase): super().setUpTestData() cls.vol = cls.corpus.elements.get(name='Volume 1') cls.private_corpus = Corpus.objects.create(name='private', public=False) + cls.worker_version = WorkerVersion.objects.get(worker__slug='reco') def setUp(self): self.page = self.corpus.elements.get(name='Volume 1, page 1r') @@ -25,9 +26,8 @@ class TestRetrieveElements(FixtureAPITestCase): ) def test_get_element(self): - data_source = DataSource.objects.get(slug='test', type=MLToolType.Recognizer) ml_class = MLClass.objects.create(name='text', corpus=self.corpus) - classification = Classification.objects.create(element=self.vol, source=data_source, ml_class=ml_class) + classification = self.vol.classifications.create(worker_version=self.worker_version, ml_class=ml_class) response = self.client.get(reverse('api:element-retrieve', kwargs={'pk': str(self.vol.id)})) self.assertEqual(response.status_code, status.HTTP_200_OK) @@ -42,7 +42,6 @@ class TestRetrieveElements(FixtureAPITestCase): }, 'thumbnail_url': self.vol.thumbnail.s3_url, 'thumbnail_put_url': None, - 'source': None, 'worker_version': None, 'zone': None, 'metadata': [], @@ -52,15 +51,7 @@ class TestRetrieveElements(FixtureAPITestCase): 'confidence': None, 'high_confidence': False, 'state': 'pending', - 'worker_version': None, - 'source': { - 'id': str(data_source.id), - 'slug': 'test', - 'type': 'recognizer', - 'name': 'Test Recognizer', - 'revision': '4.2', - 'internal': False, - }, + 'worker_version': str(self.worker_version.id), 'ml_class': { 'id': str(ml_class.id), 'name': 'text', @@ -157,7 +148,7 @@ class TestRetrieveElements(FixtureAPITestCase): type=EntityType.Person, corpus=self.corpus, name='Marc', - source_id=DataSource.objects.get(slug='test', type=MLToolType.Recognizer).id + worker_version=self.worker_version, ) self.metadata.entity = entity self.metadata.save() diff --git a/arkindex/documents/tests/test_search.py b/arkindex/documents/tests/test_search.py index dd001a2ef4..46d0702c9a 100644 --- a/arkindex/documents/tests/test_search.py +++ b/arkindex/documents/tests/test_search.py @@ -6,7 +6,8 @@ from django.urls import reverse from elasticsearch_dsl.connections import connections from rest_framework import status -from arkindex.documents.models import Corpus, DataSource, Element, MLToolType, Transcription +from arkindex.dataimport.models import WorkerVersion +from arkindex.documents.models import Corpus, Element, Transcription from arkindex.project.elastic import ESTranscription from arkindex.project.tests import FixtureAPITestCase from arkindex_common.enums import EntityType, MetaType @@ -237,9 +238,9 @@ class TestSearchAPI(FixtureAPITestCase): self.assertListEqual(conditions, [{'match': {'text': 'paris'}}]) def test_entity_search(self): - source = DataSource.objects.create(type=MLToolType.NER, slug='entity source', internal=True) - entity_1 = self.corpus.entities.create(type=EntityType.Person, name="an entity", source=source) - entity_2 = self.corpus.entities.create(type=EntityType.Location, name="somewhere", source=source) + worker_version = WorkerVersion.objects.first() + entity_1 = self.corpus.entities.create(type=EntityType.Person, name="an entity", worker_version=worker_version) + entity_2 = self.corpus.entities.create(type=EntityType.Location, name="somewhere", worker_version=worker_version) self.es_mock.count.return_value = {'count': 2} self.es_mock.search.return_value = self.build_es_response([ # Test the ES ordering is preserved by returning entities in non-alphabetical order diff --git a/arkindex/documents/tests/test_transcriptions.py b/arkindex/documents/tests/test_transcriptions.py index 7ff9d3a1e0..619d9ae08c 100644 --- a/arkindex/documents/tests/test_transcriptions.py +++ b/arkindex/documents/tests/test_transcriptions.py @@ -2,10 +2,9 @@ from django.urls import reverse from rest_framework import status from arkindex.dataimport.models import WorkerVersion -from arkindex.documents.models import Corpus, DataSource +from arkindex.documents.models import Corpus from arkindex.project.tests import FixtureAPITestCase from arkindex.users.models import User -from arkindex_common.ml_tool import MLToolType class TestTranscriptions(FixtureAPITestCase): @@ -21,12 +20,12 @@ class TestTranscriptions(FixtureAPITestCase): cls.line = cls.corpus.elements.get(name='Text line') cls.private_corpus = Corpus.objects.create(name='Private') cls.private_page = cls.private_corpus.elements.create(type=cls.page.type) - cls.src = DataSource.objects.get(slug='test', type=MLToolType.Recognizer) # Create an user with a read right only on the private corpus cls.private_read_user = User.objects.create_user('a@bc.de', 'a') cls.private_read_user.verified_email = True cls.private_read_user.save() - cls.worker_version = WorkerVersion.objects.get(worker__slug='reco') + cls.worker_version_1 = WorkerVersion.objects.get(worker__slug='reco') + cls.worker_version_2 = WorkerVersion.objects.get(worker__slug='dla') def test_list_transcriptions_read_right(self): # A read right on the element corpus is required to access transcriptions @@ -39,7 +38,7 @@ class TestTranscriptions(FixtureAPITestCase): tr1 = self.page.transcriptions.get() tr2 = self.page.transcriptions.create( text='something', - worker_version=self.worker_version, + worker_version=self.worker_version_2, score=0.369, ) self.client.force_login(self.user) @@ -53,23 +52,14 @@ class TestTranscriptions(FixtureAPITestCase): 'id': str(tr1.id), 'text': 'Lorem ipsum dolor sit amet', 'score': 1.0, - 'source': { - 'id': str(self.src.id), - 'type': 'recognizer', - 'slug': 'test', - 'name': 'Test Recognizer', - 'revision': '4.2', - 'internal': False, - }, - 'worker_version_id': None, + 'worker_version_id': str(self.worker_version_1.id), 'element': None, }, { 'id': str(tr2.id), 'text': 'something', 'score': 0.369, - 'source': None, - 'worker_version_id': str(self.worker_version.id), + 'worker_version_id': str(self.worker_version_2.id), 'element': None, } ]) @@ -99,7 +89,7 @@ class TestTranscriptions(FixtureAPITestCase): def test_list_worker_version_transcriptions(self): worker_transcription = self.page.transcriptions.create( text='something', - worker_version=self.worker_version, + worker_version=self.worker_version_2, score=0.369, ) @@ -108,7 +98,7 @@ class TestTranscriptions(FixtureAPITestCase): with self.assertNumQueries(12): response = self.client.get( reverse('api:element-transcriptions', kwargs={'pk': str(self.page.id)}), - data={'recursive': 'true', 'worker_version': str(self.worker_version.id)} + data={'recursive': 'true', 'worker_version': str(self.worker_version_2.id)} ) self.assertEqual(response.status_code, status.HTTP_200_OK) @@ -117,8 +107,7 @@ class TestTranscriptions(FixtureAPITestCase): 'id': str(worker_transcription.id), 'text': 'something', 'score': 0.369, - 'source': None, - 'worker_version_id': str(self.worker_version.id), + 'worker_version_id': str(self.worker_version_2.id), 'element': { 'id': str(self.page.id), 'name': 'Volume 1, page 1r', diff --git a/arkindex/project/api_v1.py b/arkindex/project/api_v1.py index 361292ddc8..162a1e374f 100644 --- a/arkindex/project/api_v1.py +++ b/arkindex/project/api_v1.py @@ -72,8 +72,6 @@ from arkindex.documents.api.ml import ( ClassificationReject, ClassificationValidate, CorpusMLClassList, - CorpusMLStats, - ElementMLStats, ElementTranscriptionsBulk, ManageClassificationsSelection, MLClassList, @@ -133,7 +131,6 @@ api = [ ElementTranscriptionsBulk.as_view(), name='element-transcriptions-bulk' ), - path('element/<uuid:pk>/ml-stats/', ElementMLStats.as_view(), name='element-ml-stats'), path('element/<uuid:child>/parent/<uuid:parent>/', ElementParent.as_view(), name='element-parent'), # Corpora @@ -143,7 +140,6 @@ api = [ path('corpus/<uuid:corpus>/elements/', CorpusElements.as_view(), name='corpus-elements'), path('corpus/<uuid:pk>/classes/', CorpusMLClassList.as_view(), name='corpus-classes'), path('corpus/<uuid:pk>/roles/', CorpusRoles.as_view(), name='corpus-roles'), - path('corpus/<uuid:pk>/ml-stats/', CorpusMLStats.as_view(), name='corpus-ml-stats'), path('corpus/<uuid:pk>/allowed-metadata/', CorpusAllowedMetaData.as_view(), name='corpus-allowed-metadata'), path('corpus/<uuid:pk>/versions/', CorpusWorkerVersionList.as_view(), name='corpus-versions'), path('corpus/<uuid:pk>/selection/', CorpusDeleteSelection.as_view(), name='corpus-delete-selection'), diff --git a/arkindex/project/openapi/patch.yml b/arkindex/project/openapi/patch.yml index 1aa64baee2..187025b3f9 100644 --- a/arkindex/project/openapi/patch.yml +++ b/arkindex/project/openapi/patch.yml @@ -100,11 +100,6 @@ paths: id: 55cd009d-cd4b-4ec2-a475-b060f98f9138 corpus: - Role already exists in this corpus - /api/v1/corpus/{id}/ml-stats/: - delete: - # Will need https://gitlab.com/arkindex/backend/-/issues/86 to be removed - operationId: DestroyCorpusMLResults - description: Delete machine learning results on all elements of a corpus. /api/v1/element/{id}/: get: description: Retrieve a single element's informations and metadata @@ -115,11 +110,6 @@ paths: description: Rename an element delete: description: Delete a childless element - /api/v1/element/{id}/ml-stats/: - delete: - # Will need https://gitlab.com/arkindex/backend/-/issues/86 to be removed - operationId: DestroyElementMLResults - description: Delete machine learning results on an element and its direct children. /api/v1/elements/{id}/children/: delete: operationId: DestroyElementChildren diff --git a/arkindex/project/tests/test_elastic.py b/arkindex/project/tests/test_elastic.py index 05565df4ec..3a9da527cd 100644 --- a/arkindex/project/tests/test_elastic.py +++ b/arkindex/project/tests/test_elastic.py @@ -1,11 +1,10 @@ from unittest.mock import patch +from arkindex.dataimport.models import WorkerVersion from arkindex.documents.dates import DateType, InterpretedDate -from arkindex.documents.models import DataSource from arkindex.project.elastic import ESElement from arkindex.project.tests import FixtureAPITestCase from arkindex_common.enums import MetaType -from arkindex_common.ml_tool import MLToolType class TestESDocuments(FixtureAPITestCase): @@ -61,7 +60,7 @@ class TestESDocuments(FixtureAPITestCase): surface.add_parent(page) surface.transcriptions.create( text='invisible transcription', - source=DataSource.objects.get(slug='test', type=MLToolType.Recognizer), + worker_version=WorkerVersion.objects.get(worker__slug='reco'), ) texts = [tr['text'] for tr in ESElement.from_model(page).to_dict()['transcriptions']] self.assertNotIn('invisible transcription', texts) diff --git a/arkindex/project/triggers.py b/arkindex/project/triggers.py index 736a7f44b1..a97585f846 100644 --- a/arkindex/project/triggers.py +++ b/arkindex/project/triggers.py @@ -76,46 +76,6 @@ def reindex_start(*, ) -def ml_results_delete(*, - element: Union[Element, UUID, str] = None, - corpus: Union[Corpus, UUID, str] = None, - batch_size: int = 1000, - user_id: Optional[int] = None) -> None: - """ - Delete all ML results from all sources on a corpus - or an element and its *direct* (non-recursive) children. - """ - element_id = None - corpus_id = None - if isinstance(element, Element): - element_id = str(element.id) - elif element: - element_id = str(element) - - if isinstance(corpus, Corpus): - corpus_id = str(corpus.id) - elif corpus: - corpus_id = str(corpus) - - assert element_id or corpus_id, 'Missing element or corpus ID' - - if element_id: - description = f'ML results deletion on element {element_id}' - else: - description = f'ML results deletion on corpus {corpus_id}' - - job = tasks.ml_results_delete.delay( - corpus_id=corpus_id, - element_id=element_id, - batch_size=batch_size, - description=description, - user_id=user_id, - ) - if settings.ARKINDEX_FEATURES['search']: - # Trigger a reindex afterwards to cleanup the deleted results - tasks.reindex_start.delay(corpus_id=corpus_id, element_id=element_id, depends_on=job) - - def corpus_delete(corpus: Union[Corpus, UUID, str], user_id: Optional[int] = None) -> None: """ Delete a whole corpus without killing a server by removing all related diff --git a/arkindex/sql_validation/element_trash_children.sql b/arkindex/sql_validation/element_trash_children.sql index e80795b9f4..e03f175df9 100644 --- a/arkindex/sql_validation/element_trash_children.sql +++ b/arkindex/sql_validation/element_trash_children.sql @@ -112,7 +112,6 @@ SELECT "documents_element"."id", "documents_element"."type_id", "documents_element"."name", "documents_element"."zone_id", - "documents_element"."source_id", "documents_element"."worker_version_id" FROM "documents_element" INNER JOIN "documents_elementpath" ON ("documents_element"."id" = "documents_elementpath"."element_id") @@ -224,7 +223,6 @@ SELECT "documents_element"."id", "documents_element"."type_id", "documents_element"."name", "documents_element"."zone_id", - "documents_element"."source_id", "documents_element"."worker_version_id" FROM "documents_element" INNER JOIN "documents_elementpath" ON ("documents_element"."id" = "documents_elementpath"."element_id") @@ -304,7 +302,6 @@ SELECT "documents_element"."id", "documents_element"."type_id", "documents_element"."name", "documents_element"."zone_id", - "documents_element"."source_id", "documents_element"."worker_version_id" FROM "documents_element" WHERE "documents_element"."id" = '{id}'::uuid; diff --git a/arkindex/sql_validation/element_trash_deep.sql b/arkindex/sql_validation/element_trash_deep.sql index fdc4ce048f..dd5e994626 100644 --- a/arkindex/sql_validation/element_trash_deep.sql +++ b/arkindex/sql_validation/element_trash_deep.sql @@ -111,7 +111,6 @@ SELECT "documents_element"."id", "documents_element"."type_id", "documents_element"."name", "documents_element"."zone_id", - "documents_element"."source_id", "documents_element"."worker_version_id" FROM "documents_element" INNER JOIN "documents_elementpath" ON ("documents_element"."id" = "documents_elementpath"."element_id") @@ -223,7 +222,6 @@ SELECT "documents_element"."id", "documents_element"."type_id", "documents_element"."name", "documents_element"."zone_id", - "documents_element"."source_id", "documents_element"."worker_version_id" FROM "documents_element" INNER JOIN "documents_elementpath" ON ("documents_element"."id" = "documents_elementpath"."element_id") @@ -335,7 +333,6 @@ SELECT "documents_element"."id", "documents_element"."type_id", "documents_element"."name", "documents_element"."zone_id", - "documents_element"."source_id", "documents_element"."worker_version_id" FROM "documents_element" INNER JOIN "documents_elementpath" ON ("documents_element"."id" = "documents_elementpath"."element_id") @@ -447,7 +444,6 @@ SELECT "documents_element"."id", "documents_element"."type_id", "documents_element"."name", "documents_element"."zone_id", - "documents_element"."source_id", "documents_element"."worker_version_id" FROM "documents_element" INNER JOIN "documents_elementpath" ON ("documents_element"."id" = "documents_elementpath"."element_id") @@ -527,7 +523,6 @@ SELECT "documents_element"."id", "documents_element"."type_id", "documents_element"."name", "documents_element"."zone_id", - "documents_element"."source_id", "documents_element"."worker_version_id" FROM "documents_element" WHERE "documents_element"."id" = '{id}'::uuid; diff --git a/arkindex/sql_validation/element_trash_ml_class.sql b/arkindex/sql_validation/element_trash_ml_class.sql index cfbbec2ecf..88be7fe325 100644 --- a/arkindex/sql_validation/element_trash_ml_class.sql +++ b/arkindex/sql_validation/element_trash_ml_class.sql @@ -120,7 +120,6 @@ SELECT "documents_element"."id", "documents_element"."type_id", "documents_element"."name", "documents_element"."zone_id", - "documents_element"."source_id", "documents_element"."worker_version_id" FROM "documents_element" INNER JOIN "documents_elementpath" ON ("documents_element"."id" = "documents_elementpath"."element_id") @@ -209,8 +208,7 @@ SELECT "documents_element"."id", "documents_element"."type_id", "documents_element"."name", "documents_element"."zone_id", - "documents_element"."source_id", "documents_element"."worker_version_id" FROM "documents_element" INNER JOIN "documents_classification" ON ("documents_element"."id" = "documents_classification"."element_id") -WHERE "documents_classification"."id" IS NOT NULL \ No newline at end of file +WHERE "documents_classification"."id" IS NOT NULL diff --git a/arkindex/sql_validation/element_trash_no_children.sql b/arkindex/sql_validation/element_trash_no_children.sql index 41b889a7bb..66f880696c 100644 --- a/arkindex/sql_validation/element_trash_no_children.sql +++ b/arkindex/sql_validation/element_trash_no_children.sql @@ -69,7 +69,6 @@ SELECT "documents_element"."id", "documents_element"."type_id", "documents_element"."name", "documents_element"."zone_id", - "documents_element"."source_id", "documents_element"."worker_version_id" FROM "documents_element" WHERE "documents_element"."id" = '{id}'::uuid; -- GitLab