When a StoragePath is changed, check if related documents require a rename

This commit is contained in:
Trenton H 2023-02-17 10:02:19 -08:00
parent 9893ae9880
commit 857944aabe
3 changed files with 53 additions and 37 deletions

View File

@ -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 Defining a storage path is optional. If no storage path is defined for a
document, the global `PAPERLESS_FILENAME_FORMAT` is applied. 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} ## Celery Monitoring {#celery-monitoring}
The monitoring tool The monitoring tool

View File

@ -27,6 +27,7 @@ from ..file_handling import generate_unique_filename
from ..models import Document from ..models import Document
from ..models import MatchingModel from ..models import MatchingModel
from ..models import PaperlessTask from ..models import PaperlessTask
from ..models import StoragePath
from ..models import Tag from ..models import Tag
logger = logging.getLogger("paperless.handlers") 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): def set_log_entry(sender, document=None, logging_group=None, **kwargs):
ct = ContentType.objects.get(model="document") ct = ContentType.objects.get(model="document")

View File

@ -1,9 +1,6 @@
import datetime import datetime
import hashlib
import os import os
import random
import tempfile import tempfile
import uuid
from pathlib import Path from pathlib import Path
from unittest import mock 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 create_source_path_directory
from ..file_handling import delete_empty_directories from ..file_handling import delete_empty_directories
from ..file_handling import generate_filename from ..file_handling import generate_filename
from ..file_handling import generate_unique_filename
from ..models import Correspondent from ..models import Correspondent
from ..models import Document from ..models import Document
from ..models import DocumentType from ..models import DocumentType
@ -871,7 +867,7 @@ class TestFileHandlingWithArchive(DirectoriesMixin, TestCase):
self.assertTrue(os.path.isfile(doc.archive_path)) self.assertTrue(os.path.isfile(doc.archive_path))
class TestFilenameGeneration(TestCase): class TestFilenameGeneration(DirectoriesMixin, TestCase):
@override_settings(FILENAME_FORMAT="{title}") @override_settings(FILENAME_FORMAT="{title}")
def test_invalid_characters(self): 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_a), "0000002.pdf")
self.assertEqual(generate_filename(doc_b), "SomeImportantNone/2020-07-25.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( @override_settings(
FILENAME_FORMAT="{created_year_short}/{created_month_name_short}/{created_month_name}/{title}", FILENAME_FORMAT="{created_year_short}/{created_month_name_short}/{created_month_name}/{title}",
) )
@ -1063,28 +1100,3 @@ class TestFilenameGeneration(TestCase):
checksum="2", checksum="2",
) )
self.assertEqual(generate_filename(doc), "84/August/Aug/The Title.pdf") 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()