From 857944aabefdf3432f6dd0affed9f91da8fdcee0 Mon Sep 17 00:00:00 2001 From: Trenton H <797416+stumpylog@users.noreply.github.com> Date: Fri, 17 Feb 2023 10:02:19 -0800 Subject: [PATCH] When a StoragePath is changed, check if related documents require a rename --- docs/advanced_usage.md | 7 --- src/documents/signals/handlers.py | 11 ++++ src/documents/tests/test_file_handling.py | 72 +++++++++++++---------- 3 files changed, 53 insertions(+), 37 deletions(-) diff --git a/docs/advanced_usage.md b/docs/advanced_usage.md index 61e7eee20..cd82ab78e 100644 --- a/docs/advanced_usage.md +++ b/docs/advanced_usage.md @@ -414,13 +414,6 @@ structure as in the previous example above. Defining a storage path is optional. If no storage path is defined for a document, the global `PAPERLESS_FILENAME_FORMAT` is applied. -!!! warning - - If you adjust the format of an existing storage path, old documents - don't get relocated automatically. You need to run the - [document renamer](/administration#renamer) to - adjust their paths. - ## Celery Monitoring {#celery-monitoring} The monitoring tool diff --git a/src/documents/signals/handlers.py b/src/documents/signals/handlers.py index cd42c9030..3b4021a2e 100644 --- a/src/documents/signals/handlers.py +++ b/src/documents/signals/handlers.py @@ -27,6 +27,7 @@ from ..file_handling import generate_unique_filename from ..models import Document from ..models import MatchingModel from ..models import PaperlessTask +from ..models import StoragePath from ..models import Tag logger = logging.getLogger("paperless.handlers") @@ -495,6 +496,16 @@ def update_filename_and_move_files(sender, instance, **kwargs): ) +@receiver(models.signals.post_save, sender=StoragePath) +def update_document_storage_path(sender, instance, **kwargs): + """ + Triggers when a storage path is changed, running against any documents using + the path, and checks to see if they need to be renamed + """ + for document in instance.documents.all(): + update_filename_and_move_files(None, document) + + def set_log_entry(sender, document=None, logging_group=None, **kwargs): ct = ContentType.objects.get(model="document") diff --git a/src/documents/tests/test_file_handling.py b/src/documents/tests/test_file_handling.py index 8d726b339..6b55d9b88 100644 --- a/src/documents/tests/test_file_handling.py +++ b/src/documents/tests/test_file_handling.py @@ -1,9 +1,6 @@ import datetime -import hashlib import os -import random import tempfile -import uuid from pathlib import Path from unittest import mock @@ -16,7 +13,6 @@ from django.utils import timezone from ..file_handling import create_source_path_directory from ..file_handling import delete_empty_directories from ..file_handling import generate_filename -from ..file_handling import generate_unique_filename from ..models import Correspondent from ..models import Document from ..models import DocumentType @@ -871,7 +867,7 @@ class TestFileHandlingWithArchive(DirectoriesMixin, TestCase): self.assertTrue(os.path.isfile(doc.archive_path)) -class TestFilenameGeneration(TestCase): +class TestFilenameGeneration(DirectoriesMixin, TestCase): @override_settings(FILENAME_FORMAT="{title}") def test_invalid_characters(self): @@ -1036,6 +1032,47 @@ class TestFilenameGeneration(TestCase): self.assertEqual(generate_filename(doc_a), "0000002.pdf") self.assertEqual(generate_filename(doc_b), "SomeImportantNone/2020-07-25.pdf") + def test_document_storage_path_changed(self): + """ + GIVEN: + - Document with a defined storage path + WHEN: + - The storage path format is updated + THEN: + - Document is renamed to the new format + """ + sp = StoragePath.objects.create(path="TestFolder/{created}") + document = Document.objects.create( + mime_type="application/pdf", + created=timezone.make_aware(datetime.datetime(2020, 6, 25, 7, 36, 51, 153)), + storage_path=sp, + ) + + # Ensure that filename is properly generated + document.filename = generate_filename(document) + self.assertEqual( + document.source_path, + os.path.join(settings.ORIGINALS_DIR, "TestFolder/2020-06-25.pdf"), + ) + + # Actually create the file + create_source_path_directory(document.source_path) + Path(document.source_path).touch() + self.assertTrue(os.path.isfile(document.source_path)) + document.save() + + # Change format + sp.path = "NewFolder/{created}" + sp.save() + + document.refresh_from_db() + + self.assertEqual( + document.source_path, + os.path.join(settings.ORIGINALS_DIR, "NewFolder/2020-06-25.pdf"), + ) + self.assertTrue(os.path.isfile(document.source_path)) + @override_settings( FILENAME_FORMAT="{created_year_short}/{created_month_name_short}/{created_month_name}/{title}", ) @@ -1063,28 +1100,3 @@ class TestFilenameGeneration(TestCase): checksum="2", ) self.assertEqual(generate_filename(doc), "84/August/Aug/The Title.pdf") - - -def run(): - doc = Document.objects.create( - checksum=str(uuid.uuid4()), - title=str(uuid.uuid4()), - content="wow", - ) - doc.filename = generate_unique_filename(doc) - Path(doc.thumbnail_path).touch() - with open(doc.source_path, "w") as f: - f.write(str(uuid.uuid4())) - with open(doc.source_path, "rb") as f: - doc.checksum = hashlib.md5(f.read()).hexdigest() - - with open(doc.archive_path, "w") as f: - f.write(str(uuid.uuid4())) - with open(doc.archive_path, "rb") as f: - doc.archive_checksum = hashlib.md5(f.read()).hexdigest() - - doc.save() - - for i in range(30): - doc.title = str(random.randrange(1, 5)) - doc.save()