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