mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2025-04-09 09:58:20 -05:00
Adds owner and original name to the possible naming schemes
This commit is contained in:
parent
ef6c4e6789
commit
97ff2e126c
@ -309,6 +309,8 @@ Paperless provides the following placeholders within filenames:
|
|||||||
- `{added_month_name_short}`: Month added abbreviated name, as per
|
- `{added_month_name_short}`: Month added abbreviated name, as per
|
||||||
locale
|
locale
|
||||||
- `{added_day}`: Day added only (number 01-31).
|
- `{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
|
Paperless will try to conserve the information from your database as
|
||||||
much as possible. However, some characters that you can use in document
|
much as possible. However, some characters that you can use in document
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
|
from pathlib import PurePath
|
||||||
|
|
||||||
import pathvalidate
|
import pathvalidate
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.template.defaultfilters import slugify
|
from django.template.defaultfilters import slugify
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
from documents.models import Document
|
||||||
|
|
||||||
logger = logging.getLogger("paperless.filehandling")
|
logger = logging.getLogger("paperless.filehandling")
|
||||||
|
|
||||||
@ -125,7 +126,12 @@ def generate_unique_filename(doc, archive_filename=False):
|
|||||||
return new_filename
|
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 = ""
|
path = ""
|
||||||
filename_format = settings.FILENAME_FORMAT
|
filename_format = settings.FILENAME_FORMAT
|
||||||
|
|
||||||
@ -150,13 +156,15 @@ def generate_filename(doc, counter=0, append_gpg=True, archive_filename=False):
|
|||||||
replacement_text="-",
|
replacement_text="-",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
no_value_default = "-none-"
|
||||||
|
|
||||||
if doc.correspondent:
|
if doc.correspondent:
|
||||||
correspondent = pathvalidate.sanitize_filename(
|
correspondent = pathvalidate.sanitize_filename(
|
||||||
doc.correspondent.name,
|
doc.correspondent.name,
|
||||||
replacement_text="-",
|
replacement_text="-",
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
correspondent = "-none-"
|
correspondent = no_value_default
|
||||||
|
|
||||||
if doc.document_type:
|
if doc.document_type:
|
||||||
document_type = pathvalidate.sanitize_filename(
|
document_type = pathvalidate.sanitize_filename(
|
||||||
@ -164,12 +172,23 @@ def generate_filename(doc, counter=0, append_gpg=True, archive_filename=False):
|
|||||||
replacement_text="-",
|
replacement_text="-",
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
document_type = "-none-"
|
document_type = no_value_default
|
||||||
|
|
||||||
if doc.archive_serial_number:
|
if doc.archive_serial_number:
|
||||||
asn = str(doc.archive_serial_number)
|
asn = str(doc.archive_serial_number)
|
||||||
else:
|
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
|
# Convert UTC database datetime to localized date
|
||||||
local_added = timezone.localdate(doc.added)
|
local_added = timezone.localdate(doc.added)
|
||||||
@ -196,6 +215,8 @@ def generate_filename(doc, counter=0, append_gpg=True, archive_filename=False):
|
|||||||
asn=asn,
|
asn=asn,
|
||||||
tags=tags,
|
tags=tags,
|
||||||
tag_list=tag_list,
|
tag_list=tag_list,
|
||||||
|
owner_username=owner_username_str,
|
||||||
|
original_name=original_name,
|
||||||
).strip()
|
).strip()
|
||||||
|
|
||||||
if settings.FILENAME_FORMAT_REMOVE_NONE:
|
if settings.FILENAME_FORMAT_REMOVE_NONE:
|
||||||
|
@ -818,6 +818,8 @@ class StoragePathSerializer(MatchingModelSerializer, OwnedObjectSerializer):
|
|||||||
asn="asn",
|
asn="asn",
|
||||||
tags="tags",
|
tags="tags",
|
||||||
tag_list="tag_list",
|
tag_list="tag_list",
|
||||||
|
owner_username="someone",
|
||||||
|
original_name="testfile",
|
||||||
)
|
)
|
||||||
|
|
||||||
except (KeyError):
|
except (KeyError):
|
||||||
|
@ -20,8 +20,6 @@ except ImportError:
|
|||||||
import backports.zoneinfo as zoneinfo
|
import backports.zoneinfo as zoneinfo
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from django.db import transaction
|
|
||||||
from django.db.utils import IntegrityError
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.auth.models import Group
|
from django.contrib.auth.models import Group
|
||||||
from django.contrib.auth.models import Permission
|
from django.contrib.auth.models import Permission
|
||||||
|
@ -5,6 +5,7 @@ from pathlib import Path
|
|||||||
from unittest import mock
|
from unittest import mock
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from django.contrib.auth.models import User
|
||||||
from django.db import DatabaseError
|
from django.db import DatabaseError
|
||||||
from django.test import override_settings
|
from django.test import override_settings
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
@ -1059,3 +1060,93 @@ class TestFilenameGeneration(DirectoriesMixin, 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")
|
||||||
|
|
||||||
|
@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")
|
||||||
|
Loading…
x
Reference in New Issue
Block a user