moves to system check, fixes issue with not being a relative path

This commit is contained in:
Trenton H 2024-10-02 10:20:08 -07:00
parent 825f3d6380
commit 7189cec0be
7 changed files with 78 additions and 50 deletions

View File

@ -8,6 +8,7 @@ from django.db.utils import OperationalError
from django.db.utils import ProgrammingError
from documents.signals import document_consumer_declaration
from documents.templatetags import convert_to_django_template_format
@register()
@ -69,3 +70,17 @@ def parser_check(app_configs, **kwargs):
]
else:
return []
@register()
def filename_format_check(app_configs, **kwargs):
if settings.FILENAME_FORMAT:
converted_format = convert_to_django_template_format(settings.FILENAME_FORMAT)
if converted_format != settings.FILENAME_FORMAT:
return [
Error(
f"Filename format {settings.FILENAME_FORMAT} is using the old style, please update to use double curly brackets",
hint=converted_format,
),
]
return []

View File

@ -17,6 +17,7 @@ from documents.models import Document
from documents.models import DocumentType
from documents.models import StoragePath
from documents.models import Tag
from documents.templatetags import convert_to_django_template_format
logger = logging.getLogger("paperless.filehandling")
@ -112,29 +113,6 @@ def generate_unique_filename(doc, archive_filename=False):
return new_filename
def convert_to_django_template_format(old_format: str) -> str:
"""
Converts old Python string format (with {}) to Django template style (with {{ }}),
while ignoring existing {{ ... }} placeholders.
:param old_format: The old style format string (e.g., "{title} by {author}")
:return: Converted string in Django Template style (e.g., "{{ title }} by {{ author }}")
"""
# Step 1: Match placeholders with single curly braces but not those with double braces
pattern = r"(?<!\{)\{(\w*)\}(?!\})" # Matches {var} but not {{var}}
# Step 2: Replace the placeholders with {{ var }} or {{ }}
def replace_with_django(match):
variable = match.group(1) # The variable inside the braces
return f"{{{{ {variable} }}}}" # Convert to {{ variable }}
# Apply the substitution
converted_format = re.sub(pattern, replace_with_django, old_format)
return converted_format
def create_dummy_document():
"""
Create a dummy Document instance with all possible fields filled
@ -258,12 +236,13 @@ def get_tags_context(tags: Iterable[Tag]) -> dict[str, str | list[str]]:
def get_custom_fields_context(
custom_fields: Iterable[CustomFieldInstance],
) -> dict[str, dict[str, str]]:
) -> dict[str, dict[str, dict[str, str]]]:
"""
Given an Iterable of CustomFieldInstance, builds a dictionary mapping the field name
to its type and value
"""
return {
"custom_fields": {
pathvalidate.sanitize_filename(
field_instance.field.name,
replacement_text="-",
@ -278,6 +257,7 @@ def get_custom_fields_context(
),
}
for field_instance in custom_fields
},
}
@ -401,14 +381,6 @@ def generate_filename(
filename_format = convert_to_django_template_format(
settings.FILENAME_FORMAT,
)
# Warn the user they should update
# TODO: Move this to system check
if filename_format != settings.FILENAME_FORMAT:
logger.warning(
f"Filename format {settings.FILENAME_FORMAT} is using the old style, please update to use double curly brackets",
)
logger.info(filename_format)
else:
filename_format = None

View File

@ -10,7 +10,7 @@ def convert_from_format_to_template(apps, schema_editor):
StoragePath = apps.get_model("documents", "StoragePath")
from documents.file_handling import convert_to_django_template_format
from documents.templatetags import convert_to_django_template_format
with transaction.atomic():
for storage_path in StoragePath.objects.all():

View File

@ -29,8 +29,6 @@ from rest_framework import fields
from rest_framework import serializers
from rest_framework.fields import SerializerMethodField
from documents.file_handling import convert_to_django_template_format
if settings.AUDIT_LOG_ENABLED:
from auditlog.context import set_actor
@ -56,6 +54,7 @@ from documents.models import WorkflowTrigger
from documents.parsers import is_mime_type_supported
from documents.permissions import get_groups_with_only_permission
from documents.permissions import set_permissions_for_object
from documents.templatetags import convert_to_django_template_format
from documents.validators import uri_validator
logger = logging.getLogger("paperless.serializers")

View File

@ -1,3 +1,4 @@
import os
import re
from django import template
@ -26,7 +27,10 @@ class FilePathNode(template.Node):
"""
value = value.replace("\n", "").replace("\r", "")
value = re.sub(r"\s*/\s*", "/", value)
return value.strip()
# We remove trailing and leading separators, as these are always relative paths, not absolute, even if the user
# tries
return value.strip().strip(os.sep)
output = self.nodelist.render(context)
return clean_filepath(output)
@ -41,3 +45,26 @@ def construct_filepath(parser, token):
nodelist = parser.parse(("endfilepath",))
parser.delete_first_token()
return FilePathNode(nodelist)
def convert_to_django_template_format(old_format: str) -> str:
"""
Converts old Python string format (with {}) to Django template style (with {{ }}),
while ignoring existing {{ ... }} placeholders.
:param old_format: The old style format string (e.g., "{title} by {author}")
:return: Converted string in Django Template style (e.g., "{{ title }} by {{ author }}")
"""
# Step 1: Match placeholders with single curly braces but not those with double braces
pattern = r"(?<!\{)\{(\w*)\}(?!\})" # Matches {var} but not {{var}}
# Step 2: Replace the placeholders with {{ var }} or {{ }}
def replace_with_django(match):
variable = match.group(1) # The variable inside the braces
return f"{{{{ {variable} }}}}" # Convert to {{ variable }}
# Apply the substitution
converted_format = re.sub(pattern, replace_with_django, old_format)
return converted_format

View File

@ -239,7 +239,7 @@ class TestApiStoragePaths(DirectoriesMixin, APITestCase):
"/{created_year_short}/{created_month}/{created_month_name}"
"/{created_month_name_short}/{created_day}/{added}/{added_year}"
"/{added_year_short}/{added_month}/{added_month_name}"
"/{added_month_name_short}/{added_day}/{asn}/{tags}"
"/{added_month_name_short}/{added_day}/{asn}"
"/{tag_list}/{owner_username}/{original_name}/{doc_pk}/",
},
),

View File

@ -6,6 +6,7 @@ from django.test import TestCase
from django.test import override_settings
from documents.checks import changed_password_check
from documents.checks import filename_format_check
from documents.checks import parser_check
from documents.models import Document
from documents.tests.factories import DocumentFactory
@ -73,3 +74,17 @@ class TestDocumentChecks(TestCase):
),
],
)
def test_filename_format_check(self):
self.assertEqual(filename_format_check(None), [])
with override_settings(FILENAME_FORMAT="{created}/{title}"):
self.assertEqual(
filename_format_check(None),
[
Error(
"Filename format {created}/{title} is using the old style, please update to use double curly brackets",
hint="{{ created }}/{{ title }}",
),
],
)