]> git.ipfire.org Git - thirdparty/paperless-ngx.git/commitdiff
Adds owner and original name to the possible naming schemes
authorTrenton Holmes <797416+stumpylog@users.noreply.github.com>
Sun, 12 Mar 2023 01:43:58 +0000 (17:43 -0800)
committerTrenton H <797416+stumpylog@users.noreply.github.com>
Sun, 12 Mar 2023 22:29:58 +0000 (15:29 -0700)
docs/advanced_usage.md
src/documents/file_handling.py
src/documents/serialisers.py
src/documents/tests/test_api.py
src/documents/tests/test_file_handling.py

index 55ca8ee74a9308f016afad410e55c92de1cf6d45..9b1816befeeedb898c5da7807d1cf7d811a148d1 100644 (file)
@@ -309,6 +309,8 @@ Paperless provides the following placeholders within filenames:
 - `{added_month_name_short}`: Month added abbreviated name, as per
   locale
 - `{added_day}`: Day added only (number 01-31).
+- `{owner_username}`: Username of document owner, if any, or "none"
+- `{original_name}`: Document original filename, minus the extension, if any, or "none"
 
 Paperless will try to conserve the information from your database as
 much as possible. However, some characters that you can use in document
index 5947fe1b0b2e3250fde402156e2f694005b4924d..c046ae15a9c27f21dca13405eb74a865ac9227fa 100644 (file)
@@ -1,12 +1,13 @@
 import logging
 import os
 from collections import defaultdict
+from pathlib import PurePath
 
 import pathvalidate
 from django.conf import settings
 from django.template.defaultfilters import slugify
 from django.utils import timezone
-
+from documents.models import Document
 
 logger = logging.getLogger("paperless.filehandling")
 
@@ -125,7 +126,12 @@ def generate_unique_filename(doc, archive_filename=False):
             return new_filename
 
 
-def generate_filename(doc, counter=0, append_gpg=True, archive_filename=False):
+def generate_filename(
+    doc: Document,
+    counter=0,
+    append_gpg=True,
+    archive_filename=False,
+):
     path = ""
     filename_format = settings.FILENAME_FORMAT
 
@@ -150,13 +156,15 @@ def generate_filename(doc, counter=0, append_gpg=True, archive_filename=False):
                 replacement_text="-",
             )
 
+            no_value_default = "-none-"
+
             if doc.correspondent:
                 correspondent = pathvalidate.sanitize_filename(
                     doc.correspondent.name,
                     replacement_text="-",
                 )
             else:
-                correspondent = "-none-"
+                correspondent = no_value_default
 
             if doc.document_type:
                 document_type = pathvalidate.sanitize_filename(
@@ -164,12 +172,23 @@ def generate_filename(doc, counter=0, append_gpg=True, archive_filename=False):
                     replacement_text="-",
                 )
             else:
-                document_type = "-none-"
+                document_type = no_value_default
 
             if doc.archive_serial_number:
                 asn = str(doc.archive_serial_number)
             else:
-                asn = "-none-"
+                asn = no_value_default
+
+            if doc.owner is not None:
+                owner_username_str = str(doc.owner.username)
+            else:
+                owner_username_str = no_value_default
+
+            if doc.original_filename is not None:
+                # No extension
+                original_name = PurePath(doc.original_filename).with_suffix("").name
+            else:
+                original_name = no_value_default
 
             # Convert UTC database datetime to localized date
             local_added = timezone.localdate(doc.added)
@@ -196,6 +215,8 @@ def generate_filename(doc, counter=0, append_gpg=True, archive_filename=False):
                 asn=asn,
                 tags=tags,
                 tag_list=tag_list,
+                owner_username=owner_username_str,
+                original_name=original_name,
             ).strip()
 
             if settings.FILENAME_FORMAT_REMOVE_NONE:
index d5fc0f07990ff45bf03a15df60ac99443e109461..1f74a43c0de68d5a07d1a5fa194c70eb2d930385 100644 (file)
@@ -818,6 +818,8 @@ class StoragePathSerializer(MatchingModelSerializer, OwnedObjectSerializer):
                 asn="asn",
                 tags="tags",
                 tag_list="tag_list",
+                owner_username="someone",
+                original_name="testfile",
             )
 
         except (KeyError):
index 96f82dea317565f6fc4062b3109577b3ee487c06..f56d0344ccc924ee59f24ebdc50723b4b594e60d 100644 (file)
@@ -20,8 +20,6 @@ except ImportError:
     import backports.zoneinfo as zoneinfo
 
 import pytest
-from django.db import transaction
-from django.db.utils import IntegrityError
 from django.conf import settings
 from django.contrib.auth.models import Group
 from django.contrib.auth.models import Permission
index 0d548264c777687e1293a0c380c770d73e5924a8..04ef0a79fe97c21c825e825794cbcae085c3c87b 100644 (file)
@@ -5,6 +5,7 @@ from pathlib import Path
 from unittest import mock
 
 from django.conf import settings
+from django.contrib.auth.models import User
 from django.db import DatabaseError
 from django.test import override_settings
 from django.test import TestCase
@@ -1059,3 +1060,93 @@ class TestFilenameGeneration(DirectoriesMixin, TestCase):
             checksum="2",
         )
         self.assertEqual(generate_filename(doc), "84/August/Aug/The Title.pdf")
+
+    @override_settings(
+        FILENAME_FORMAT="{owner_username}/{title}",
+    )
+    def test_document_owner_string(self):
+        """
+        GIVEN:
+            - Document with an other
+            - Document without an owner
+            - Filename format string includes owner
+        WHEN:
+            - Filename is generated for each document
+        THEN:
+            - Owned document includes username
+            - Document without owner returns "none"
+        """
+
+        u1 = User.objects.create_user("user1")
+
+        owned_doc = Document.objects.create(
+            title="The Title",
+            mime_type="application/pdf",
+            checksum="2",
+            owner=u1,
+        )
+
+        no_owner_doc = Document.objects.create(
+            title="does matter",
+            mime_type="application/pdf",
+            checksum="3",
+        )
+
+        self.assertEqual(generate_filename(owned_doc), "user1/The Title.pdf")
+        self.assertEqual(generate_filename(no_owner_doc), "none/does matter.pdf")
+
+    @override_settings(
+        FILENAME_FORMAT="{original_name}",
+    )
+    def test_document_original_filename(self):
+        """
+        GIVEN:
+            - Document with an original filename
+            - Document without an original filename
+            - Document which was plain text document
+            - Filename format string includes original filename
+        WHEN:
+            - Filename is generated for each document
+        THEN:
+            - Document with original name uses it, dropping suffix
+            - Document without original name returns "none"
+            - Text document returns extension of .txt
+            - Text document archive returns extension of .pdf
+            - No extensions are doubled
+        """
+        doc_with_original = Document.objects.create(
+            title="does matter",
+            mime_type="application/pdf",
+            checksum="3",
+            original_filename="someepdf.pdf",
+        )
+        tricky_with_original = Document.objects.create(
+            title="does matter",
+            mime_type="application/pdf",
+            checksum="1",
+            original_filename="some pdf with spaces and stuff.pdf",
+        )
+        no_original = Document.objects.create(
+            title="does matter",
+            mime_type="application/pdf",
+            checksum="2",
+        )
+
+        text_doc = Document.objects.create(
+            title="does matter",
+            mime_type="text/plain",
+            checksum="4",
+            original_filename="logs.txt",
+        )
+
+        self.assertEqual(generate_filename(doc_with_original), "someepdf.pdf")
+
+        self.assertEqual(
+            generate_filename(tricky_with_original),
+            "some pdf with spaces and stuff.pdf",
+        )
+
+        self.assertEqual(generate_filename(no_original), "none.pdf")
+
+        self.assertEqual(generate_filename(text_doc), "logs.txt")
+        self.assertEqual(generate_filename(text_doc, archive_filename=True), "logs.pdf")