(backend) allow too migrate a specific document

The management command migrating document to yhub didn't allow to target
a specific document. This can be usefull for debugging purpose but also
to replay the migration of a specific document.
This commit is contained in:
Manuel Raynaud
2026-09-02 09:41:53 +02:00
parent 754360daaa
commit 0f2787973a
2 changed files with 74 additions and 1 deletions
@@ -10,13 +10,17 @@ It is meant to be run again: every document it finishes is recorded, and the
collaboration server answers "already" for anything it has already migrated, so
a run that is interrupted, rate limited or killed simply picks up where it
stopped. Nothing is destroyed, on either side.
One document can also be handed over on its own with `--document-id`, which is
how a document a run left behind is dealt with once its cause is understood.
"""
import logging
import time
import uuid
from concurrent.futures import ThreadPoolExecutor
from django.core.management.base import BaseCommand
from django.core.management.base import BaseCommand, CommandError
from django.utils import timezone
from core import models
@@ -46,6 +50,16 @@ class Command(BaseCommand):
def add_arguments(self, parser):
"""Define the arguments of the command."""
parser.add_argument(
"--document-id",
type=uuid.UUID,
default=None,
help=(
"Migrate this document alone, whatever a previous run recorded "
"for it. The filters selecting a corpus (--created-before, "
"--limit, --retry-failed) do not apply to it."
),
)
parser.add_argument(
"--concurrency",
type=int,
@@ -136,7 +150,20 @@ class Command(BaseCommand):
A document is opened before it is missed: the ones edited recently are
the ones users are about to read, and until a document is migrated the
collaboration server only seeds its latest state, without its history.
Naming one document is an instruction rather than a filter: it is handed
over even when a previous run recorded it as done, which costs a call
the collaboration server answers with "already". Nothing else would be
useful — a command asked for one document and reporting that it had
nothing to do says neither what happened nor why.
"""
if options["document_id"]:
queryset = models.Document.objects.filter(pk=options["document_id"])
if not queryset.exists():
raise CommandError(f"No document with id {options['document_id']}")
return queryset
queryset = models.Document.objects.all()
if options["created_before"]:
@@ -1,9 +1,11 @@
"""Unit tests for the `migrate_documents` command."""
import uuid
from io import StringIO
from unittest import mock
from django.core.management import call_command
from django.core.management.base import CommandError
import pytest
@@ -167,3 +169,47 @@ def test_commands_migrate_documents_dry_run(collaboration_server):
assert "2 documents to migrate" in output
collaboration_server.assert_not_called()
assert not models.DocumentMigration.objects.exists()
def test_commands_migrate_documents_document_id(collaboration_server):
"""Naming a document should hand over that one and leave the corpus alone."""
factories.DocumentFactory.create_batch(3)
document = factories.DocumentFactory()
output = run_command(document_id=document.pk)
assert collaboration_server.call_count == 1
assert collaboration_server.call_args[0][0].pk == document.pk
assert models.DocumentMigration.objects.get().document_id == document.pk
assert "ok=1" in output
def test_commands_migrate_documents_document_id_already_migrated(
collaboration_server,
):
"""
A document already recorded as done should be handed over again when named.
Asking for a document by its id is an instruction, not a filter over what is
left to do: the collaboration server answers "already" when it has nothing
to replay, which is the answer the run records.
"""
document = factories.DocumentFactory()
run_command()
collaboration_server.return_value = migrated(status="already")
run_command(document_id=document.pk)
assert collaboration_server.call_count == 2
assert (
models.DocumentMigration.objects.get(document=document).status
== models.DocumentMigrationStatus.ALREADY
)
def test_commands_migrate_documents_document_id_unknown(collaboration_server):
"""An id that is no document should stop the command, not migrate nothing."""
with pytest.raises(CommandError, match="No document with id"):
run_command(document_id=uuid.uuid4())
collaboration_server.assert_not_called()