mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2025-12-09 00:05:21 +01:00
Feature: Dynamic document storage pathes (#916)
* Added devcontainer * Add feature storage pathes * Exclude tests and add versioning * Check escaping * Check escaping * Check quoting * Echo * Escape * Escape : * Double escape \ * Escaping * Remove if * Escape colon * Missing \ * Esacpe : * Escape all * test * Remove sed * Fix exclude * Remove SED command * Add LD_LIBRARY_PATH * Adjusted to v1.7 * Updated test-cases * Remove devcontainer * Removed internal build-file * Run pre-commit * Corrected flak8 error * Adjusted to v1.7 * Updated test-cases * Corrected flak8 error * Adjusted to new plural translations * Small adjustments due to code-review backend * Adjusted line-break * Removed PAPERLESS prefix from settings variables * Corrected style change due to search+replace * First documentation draft * Revert changes to Pipfile * Add sphinx-autobuild with keep-outdated * Revert merge error that results in wrong storage path is evaluated * Adjust styles of generated files ... * Adds additional testing to cover dynamic storage path functionality * Remove unnecessary condition * Add hint to edit storage path dialog * Correct spelling of pathes to paths * Minor documentation tweaks * Minor typo * improving wrapping of filter editor buttons with new storage path button * Update .gitignore * Fix select border radius in non input-groups * Better storage path edit hint * Add note to edit storage path dialog re document_renamer * Add note to bulk edit storage path re document_renamer * Rename FILTER_STORAGE_DIRECTORY to PATH * Fix broken filter rule parsing * Show default storage if unspecified * Remove note re storage path on bulk edit * Add basic validation of filename variables Co-authored-by: Markus Kling <markus@markus-kling.net> Co-authored-by: Trenton Holmes <holmes.trenton@gmail.com> Co-authored-by: Michael Shamoon <4887959+shamoon@users.noreply.github.com> Co-authored-by: Quinn Casey <quinn@quinncasey.com>
This commit is contained in:
parent
c3997c9f26
commit
69ef26dab0
67 changed files with 1427 additions and 203 deletions
|
|
@ -26,8 +26,10 @@ from documents.models import Document
|
|||
from documents.models import DocumentType
|
||||
from documents.models import MatchingModel
|
||||
from documents.models import SavedView
|
||||
from documents.models import StoragePath
|
||||
from documents.models import Tag
|
||||
from documents.models import UiSettings
|
||||
from documents.models import StoragePath
|
||||
from documents.tests.utils import DirectoriesMixin
|
||||
from paperless import version
|
||||
from rest_framework.test import APITestCase
|
||||
|
|
@ -99,6 +101,7 @@ class TestDocumentApi(DirectoriesMixin, APITestCase):
|
|||
c = Correspondent.objects.create(name="c", pk=41)
|
||||
dt = DocumentType.objects.create(name="dt", pk=63)
|
||||
tag = Tag.objects.create(name="t", pk=85)
|
||||
storage_path = StoragePath.objects.create(name="sp", pk=77, path="p")
|
||||
doc = Document.objects.create(
|
||||
title="WOW",
|
||||
content="the content",
|
||||
|
|
@ -106,6 +109,7 @@ class TestDocumentApi(DirectoriesMixin, APITestCase):
|
|||
document_type=dt,
|
||||
checksum="123",
|
||||
mime_type="application/pdf",
|
||||
storage_path=storage_path,
|
||||
)
|
||||
|
||||
response = self.client.get("/api/documents/", format="json")
|
||||
|
|
@ -192,7 +196,7 @@ class TestDocumentApi(DirectoriesMixin, APITestCase):
|
|||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(response.content, content_thumbnail)
|
||||
|
||||
@override_settings(PAPERLESS_FILENAME_FORMAT="")
|
||||
@override_settings(FILENAME_FORMAT="")
|
||||
def test_download_with_archive(self):
|
||||
|
||||
content = b"This is a test"
|
||||
|
|
@ -580,10 +584,12 @@ class TestDocumentApi(DirectoriesMixin, APITestCase):
|
|||
t2 = Tag.objects.create(name="tag2")
|
||||
c = Correspondent.objects.create(name="correspondent")
|
||||
dt = DocumentType.objects.create(name="type")
|
||||
sp = StoragePath.objects.create(name="path")
|
||||
|
||||
d1 = Document.objects.create(checksum="1", correspondent=c, content="test")
|
||||
d2 = Document.objects.create(checksum="2", document_type=dt, content="test")
|
||||
d3 = Document.objects.create(checksum="3", content="test")
|
||||
|
||||
d3.tags.add(t)
|
||||
d3.tags.add(t2)
|
||||
d4 = Document.objects.create(
|
||||
|
|
@ -598,6 +604,7 @@ class TestDocumentApi(DirectoriesMixin, APITestCase):
|
|||
content="test",
|
||||
)
|
||||
d6 = Document.objects.create(checksum="6", content="test2")
|
||||
d7 = Document.objects.create(checksum="7", storage_path=sp, content="test")
|
||||
|
||||
with AsyncWriter(index.open_index()) as writer:
|
||||
for doc in Document.objects.all():
|
||||
|
|
@ -608,18 +615,30 @@ class TestDocumentApi(DirectoriesMixin, APITestCase):
|
|||
self.assertEqual(r.status_code, 200)
|
||||
return [hit["id"] for hit in r.data["results"]]
|
||||
|
||||
self.assertCountEqual(search_query(""), [d1.id, d2.id, d3.id, d4.id, d5.id])
|
||||
self.assertCountEqual(
|
||||
search_query(""),
|
||||
[d1.id, d2.id, d3.id, d4.id, d5.id, d7.id],
|
||||
)
|
||||
self.assertCountEqual(search_query("&is_tagged=true"), [d3.id, d4.id])
|
||||
self.assertCountEqual(search_query("&is_tagged=false"), [d1.id, d2.id, d5.id])
|
||||
self.assertCountEqual(
|
||||
search_query("&is_tagged=false"),
|
||||
[d1.id, d2.id, d5.id, d7.id],
|
||||
)
|
||||
self.assertCountEqual(search_query("&correspondent__id=" + str(c.id)), [d1.id])
|
||||
self.assertCountEqual(search_query("&document_type__id=" + str(dt.id)), [d2.id])
|
||||
self.assertCountEqual(search_query("&storage_path__id=" + str(sp.id)), [d7.id])
|
||||
|
||||
self.assertCountEqual(
|
||||
search_query("&storage_path__isnull"),
|
||||
[d1.id, d2.id, d3.id, d4.id, d5.id],
|
||||
)
|
||||
self.assertCountEqual(
|
||||
search_query("&correspondent__isnull"),
|
||||
[d2.id, d3.id, d4.id, d5.id],
|
||||
[d2.id, d3.id, d4.id, d5.id, d7.id],
|
||||
)
|
||||
self.assertCountEqual(
|
||||
search_query("&document_type__isnull"),
|
||||
[d1.id, d3.id, d4.id, d5.id],
|
||||
[d1.id, d3.id, d4.id, d5.id, d7.id],
|
||||
)
|
||||
self.assertCountEqual(
|
||||
search_query("&tags__id__all=" + str(t.id) + "," + str(t2.id)),
|
||||
|
|
@ -1080,35 +1099,49 @@ class TestDocumentApi(DirectoriesMixin, APITestCase):
|
|||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(
|
||||
response.data,
|
||||
{"correspondents": [], "tags": [], "document_types": []},
|
||||
{
|
||||
"correspondents": [],
|
||||
"tags": [],
|
||||
"document_types": [],
|
||||
"storage_paths": [],
|
||||
},
|
||||
)
|
||||
|
||||
def test_get_suggestions_invalid_doc(self):
|
||||
response = self.client.get(f"/api/documents/34676/suggestions/")
|
||||
self.assertEqual(response.status_code, 404)
|
||||
|
||||
@mock.patch("documents.views.match_correspondents")
|
||||
@mock.patch("documents.views.match_tags")
|
||||
@mock.patch("documents.views.match_storage_paths")
|
||||
@mock.patch("documents.views.match_document_types")
|
||||
@mock.patch("documents.views.match_tags")
|
||||
@mock.patch("documents.views.match_correspondents")
|
||||
def test_get_suggestions(
|
||||
self,
|
||||
match_document_types,
|
||||
match_tags,
|
||||
match_correspondents,
|
||||
match_tags,
|
||||
match_document_types,
|
||||
match_storage_paths,
|
||||
):
|
||||
doc = Document.objects.create(
|
||||
title="test",
|
||||
mime_type="application/pdf",
|
||||
content="this is an invoice!",
|
||||
)
|
||||
|
||||
match_correspondents.return_value = [Correspondent(id=88), Correspondent(id=2)]
|
||||
match_tags.return_value = [Tag(id=56), Tag(id=123)]
|
||||
match_document_types.return_value = [DocumentType(id=23)]
|
||||
match_correspondents.return_value = [Correspondent(id=88), Correspondent(id=2)]
|
||||
match_storage_paths.return_value = [StoragePath(id=99), StoragePath(id=77)]
|
||||
|
||||
response = self.client.get(f"/api/documents/{doc.pk}/suggestions/")
|
||||
self.assertEqual(
|
||||
response.data,
|
||||
{"correspondents": [88, 2], "tags": [56, 123], "document_types": [23]},
|
||||
{
|
||||
"correspondents": [88, 2],
|
||||
"tags": [56, 123],
|
||||
"document_types": [23],
|
||||
"storage_paths": [99, 77],
|
||||
},
|
||||
)
|
||||
|
||||
def test_saved_views(self):
|
||||
|
|
@ -1469,6 +1502,7 @@ class TestBulkEdit(DirectoriesMixin, APITestCase):
|
|||
self.doc2.tags.add(self.t1)
|
||||
self.doc3.tags.add(self.t2)
|
||||
self.doc4.tags.add(self.t1, self.t2)
|
||||
self.sp1 = StoragePath.objects.create(name="sp1", path="Something/{checksum}")
|
||||
|
||||
def test_set_correspondent(self):
|
||||
self.assertEqual(Document.objects.filter(correspondent=self.c2).count(), 1)
|
||||
|
|
@ -1508,6 +1542,60 @@ class TestBulkEdit(DirectoriesMixin, APITestCase):
|
|||
args, kwargs = self.async_task.call_args
|
||||
self.assertCountEqual(kwargs["document_ids"], [self.doc2.id, self.doc3.id])
|
||||
|
||||
def test_set_document_storage_path(self):
|
||||
"""
|
||||
GIVEN:
|
||||
- 5 documents without defined storage path
|
||||
WHEN:
|
||||
- Bulk edit called to add storage path to 1 document
|
||||
THEN:
|
||||
- Single document storage path update
|
||||
"""
|
||||
self.assertEqual(Document.objects.filter(storage_path=None).count(), 5)
|
||||
|
||||
bulk_edit.set_storage_path(
|
||||
[self.doc1.id],
|
||||
self.sp1.id,
|
||||
)
|
||||
|
||||
self.assertEqual(Document.objects.filter(storage_path=None).count(), 4)
|
||||
|
||||
self.async_task.assert_called_once()
|
||||
args, kwargs = self.async_task.call_args
|
||||
|
||||
self.assertCountEqual(kwargs["document_ids"], [self.doc1.id])
|
||||
|
||||
def test_unset_document_storage_path(self):
|
||||
"""
|
||||
GIVEN:
|
||||
- 4 documents without defined storage path
|
||||
- 1 document with a defined storage
|
||||
WHEN:
|
||||
- Bulk edit called to remove storage path from 1 document
|
||||
THEN:
|
||||
- Single document storage path removed
|
||||
"""
|
||||
self.assertEqual(Document.objects.filter(storage_path=None).count(), 5)
|
||||
|
||||
bulk_edit.set_storage_path(
|
||||
[self.doc1.id],
|
||||
self.sp1.id,
|
||||
)
|
||||
|
||||
self.assertEqual(Document.objects.filter(storage_path=None).count(), 4)
|
||||
|
||||
bulk_edit.set_storage_path(
|
||||
[self.doc1.id],
|
||||
None,
|
||||
)
|
||||
|
||||
self.assertEqual(Document.objects.filter(storage_path=None).count(), 5)
|
||||
|
||||
self.async_task.assert_called()
|
||||
args, kwargs = self.async_task.call_args
|
||||
|
||||
self.assertCountEqual(kwargs["document_ids"], [self.doc1.id])
|
||||
|
||||
def test_add_tag(self):
|
||||
self.assertEqual(Document.objects.filter(tags__id=self.t1.id).count(), 2)
|
||||
bulk_edit.add_tag(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue