mirror of
				https://github.com/paperless-ngx/paperless-ngx.git
				synced 2025-10-30 03:56:23 -05:00 
			
		
		
		
	| @@ -63,8 +63,20 @@ class Command(BaseCommand): | ||||
|             action="store_true", | ||||
|             help="If set, the progress bar will not be shown" | ||||
|         ) | ||||
|         parser.add_argument( | ||||
|             "--suggest", | ||||
|             default=False, | ||||
|             action="store_true", | ||||
|             help="Return the suggestion, don't change anything." | ||||
|         ) | ||||
|         parser.add_argument( | ||||
|             "--base-url", | ||||
|             help="The base URL to use to build the link to the documents." | ||||
|         ) | ||||
|  | ||||
|     def handle(self, *args, **options): | ||||
|         # Detect if we support color | ||||
|         color = self.style.ERROR("test") != "test" | ||||
|  | ||||
|         if options["inbox_only"]: | ||||
|             queryset = Document.objects.filter(tags__is_inbox_tag=True) | ||||
| @@ -85,18 +97,27 @@ class Command(BaseCommand): | ||||
|                     document=document, | ||||
|                     classifier=classifier, | ||||
|                     replace=options['overwrite'], | ||||
|                     use_first=options['use_first']) | ||||
|                     use_first=options['use_first'], | ||||
|                     suggest=options['suggest'], | ||||
|                     base_url=options['base_url'], | ||||
|                     color=color) | ||||
|  | ||||
|             if options['document_type']: | ||||
|                 set_document_type(sender=None, | ||||
|                                   document=document, | ||||
|                                   classifier=classifier, | ||||
|                                   replace=options['overwrite'], | ||||
|                                   use_first=options['use_first']) | ||||
|                                   use_first=options['use_first'], | ||||
|                                   suggest=options['suggest'], | ||||
|                                   base_url=options['base_url'], | ||||
|                                   color=color) | ||||
|  | ||||
|             if options['tags']: | ||||
|                 set_tags( | ||||
|                     sender=None, | ||||
|                     document=document, | ||||
|                     classifier=classifier, | ||||
|                     replace=options['overwrite']) | ||||
|                     replace=options['overwrite'], | ||||
|                     suggest=options['suggest'], | ||||
|                     base_url=options['base_url'], | ||||
|                     color=color) | ||||
|   | ||||
| @@ -1,6 +1,7 @@ | ||||
| import logging | ||||
| import os | ||||
|  | ||||
| from django.utils import termcolors | ||||
| from django.conf import settings | ||||
| from django.contrib.admin.models import ADDITION, LogEntry | ||||
| from django.contrib.auth.models import User | ||||
| @@ -8,14 +9,14 @@ from django.contrib.contenttypes.models import ContentType | ||||
| from django.db import models, DatabaseError | ||||
| from django.db.models import Q | ||||
| from django.dispatch import receiver | ||||
| from django.utils import timezone | ||||
| from django.utils import termcolors, timezone | ||||
| from filelock import FileLock | ||||
|  | ||||
| from .. import matching | ||||
| from ..file_handling import delete_empty_directories, \ | ||||
|     create_source_path_directory, \ | ||||
|     generate_unique_filename | ||||
| from ..models import Document, Tag | ||||
| from ..models import Document, Tag, MatchingModel | ||||
|  | ||||
|  | ||||
| logger = logging.getLogger("paperless.handlers") | ||||
| @@ -32,6 +33,9 @@ def set_correspondent(sender, | ||||
|                       classifier=None, | ||||
|                       replace=False, | ||||
|                       use_first=True, | ||||
|                       suggest=False, | ||||
|                       base_url=None, | ||||
|                       color=False, | ||||
|                       **kwargs): | ||||
|     if document.correspondent and not replace: | ||||
|         return | ||||
| @@ -60,6 +64,24 @@ def set_correspondent(sender, | ||||
|             return | ||||
|  | ||||
|     if selected or replace: | ||||
|         if suggest: | ||||
|             if base_url: | ||||
|                 print( | ||||
|                     termcolors.colorize(str(document), fg='green') | ||||
|                     if color | ||||
|                     else str(document) | ||||
|                 ) | ||||
|                 print(f"{base_url}/documents/{document.pk}") | ||||
|             else: | ||||
|                 print( | ||||
|                     ( | ||||
|                         termcolors.colorize(str(document), fg='green') | ||||
|                         if color | ||||
|                         else str(document) | ||||
|                     ) + f" [{document.pk}]" | ||||
|                 ) | ||||
|             print(f"Suggest correspondent {selected}") | ||||
|         else: | ||||
|             logger.info( | ||||
|                 f"Assigning correspondent {selected} to {document}", | ||||
|                 extra={'group': logging_group} | ||||
| @@ -75,6 +97,9 @@ def set_document_type(sender, | ||||
|                       classifier=None, | ||||
|                       replace=False, | ||||
|                       use_first=True, | ||||
|                       suggest=False, | ||||
|                       base_url=None, | ||||
|                       color=False, | ||||
|                       **kwargs): | ||||
|     if document.document_type and not replace: | ||||
|         return | ||||
| @@ -104,6 +129,24 @@ def set_document_type(sender, | ||||
|             return | ||||
|  | ||||
|     if selected or replace: | ||||
|         if suggest: | ||||
|             if base_url: | ||||
|                 print( | ||||
|                     termcolors.colorize(str(document), fg='green') | ||||
|                     if color | ||||
|                     else str(document) | ||||
|                 ) | ||||
|                 print(f"{base_url}/documents/{document.pk}") | ||||
|             else: | ||||
|                 print( | ||||
|                     ( | ||||
|                         termcolors.colorize(str(document), fg='green') | ||||
|                         if color | ||||
|                         else str(document) | ||||
|                     ) + f" [{document.pk}]" | ||||
|                 ) | ||||
|             print(f"Sugest document type {selected}") | ||||
|         else: | ||||
|             logger.info( | ||||
|                 f"Assigning document type {selected} to {document}", | ||||
|                 extra={'group': logging_group} | ||||
| @@ -118,6 +161,9 @@ def set_tags(sender, | ||||
|              logging_group=None, | ||||
|              classifier=None, | ||||
|              replace=False, | ||||
|              suggest=False, | ||||
|              base_url=None, | ||||
|              color=False, | ||||
|              **kwargs): | ||||
|  | ||||
|     if replace: | ||||
| @@ -132,12 +178,44 @@ def set_tags(sender, | ||||
|  | ||||
|     relevant_tags = set(matched_tags) - current_tags | ||||
|  | ||||
|     if suggest: | ||||
|         extra_tags = current_tags - set(matched_tags) | ||||
|         extra_tags = [ | ||||
|             t for t in extra_tags | ||||
|             if t.matching_algorithm == MatchingModel.MATCH_AUTO | ||||
|         ] | ||||
|         if not relevant_tags and not extra_tags: | ||||
|             return | ||||
|         if base_url: | ||||
|             print( | ||||
|                 termcolors.colorize(str(document), fg='green') | ||||
|                 if color | ||||
|                 else str(document) | ||||
|             ) | ||||
|             print(f"{base_url}/documents/{document.pk}") | ||||
|         else: | ||||
|             print( | ||||
|                 ( | ||||
|                     termcolors.colorize(str(document), fg='green') | ||||
|                     if color | ||||
|                     else str(document) | ||||
|                 ) + f" [{document.pk}]" | ||||
|             ) | ||||
|         if relevant_tags: | ||||
|             print( | ||||
|                 "Suggest tags: " + ", ".join([t.name for t in relevant_tags]) | ||||
|             ) | ||||
|         if extra_tags: | ||||
|             print("Extra tags: " + ", ".join([t.name for t in extra_tags])) | ||||
|     else: | ||||
|         if not relevant_tags: | ||||
|             return | ||||
|  | ||||
|         message = 'Tagging "{}" with "{}"' | ||||
|         logger.info( | ||||
|         message.format(document, ", ".join([t.name for t in relevant_tags])), | ||||
|             message.format( | ||||
|                 document, ", ".join([t.name for t in relevant_tags]) | ||||
|             ), | ||||
|             extra={'group': logging_group} | ||||
|         ) | ||||
|  | ||||
|   | ||||
| @@ -11,14 +11,17 @@ class TestRetagger(DirectoriesMixin, TestCase): | ||||
|         self.d1 = Document.objects.create(checksum="A", title="A", content="first document") | ||||
|         self.d2 = Document.objects.create(checksum="B", title="B", content="second document") | ||||
|         self.d3 = Document.objects.create(checksum="C", title="C", content="unrelated document") | ||||
|         self.d4 = Document.objects.create(checksum="D", title="D", content="auto document") | ||||
|  | ||||
|         self.tag_first = Tag.objects.create(name="tag1", match="first", matching_algorithm=Tag.MATCH_ANY) | ||||
|         self.tag_second = Tag.objects.create(name="tag2", match="second", matching_algorithm=Tag.MATCH_ANY) | ||||
|         self.tag_inbox = Tag.objects.create(name="test", is_inbox_tag=True) | ||||
|         self.tag_no_match = Tag.objects.create(name="test2") | ||||
|         self.tag_auto = Tag.objects.create(name="tagauto", matching_algorithm=Tag.MATCH_AUTO) | ||||
|  | ||||
|         self.d3.tags.add(self.tag_inbox) | ||||
|         self.d3.tags.add(self.tag_no_match) | ||||
|         self.d4.tags.add(self.tag_auto) | ||||
|  | ||||
|  | ||||
|         self.correspondent_first = Correspondent.objects.create( | ||||
| @@ -32,7 +35,8 @@ class TestRetagger(DirectoriesMixin, TestCase): | ||||
|             name="dt2", match="second", matching_algorithm=DocumentType.MATCH_ANY) | ||||
|  | ||||
|     def get_updated_docs(self): | ||||
|         return Document.objects.get(title="A"), Document.objects.get(title="B"), Document.objects.get(title="C") | ||||
|         return Document.objects.get(title="A"), Document.objects.get(title="B"), \ | ||||
|             Document.objects.get(title="C"), Document.objects.get(title="D") | ||||
|  | ||||
|     def setUp(self) -> None: | ||||
|         super(TestRetagger, self).setUp() | ||||
| @@ -40,25 +44,26 @@ class TestRetagger(DirectoriesMixin, TestCase): | ||||
|  | ||||
|     def test_add_tags(self): | ||||
|         call_command('document_retagger', '--tags') | ||||
|         d_first, d_second, d_unrelated = self.get_updated_docs() | ||||
|         d_first, d_second, d_unrelated, d_auto = self.get_updated_docs() | ||||
|  | ||||
|         self.assertEqual(d_first.tags.count(), 1) | ||||
|         self.assertEqual(d_second.tags.count(), 1) | ||||
|         self.assertEqual(d_unrelated.tags.count(), 2) | ||||
|         self.assertEqual(d_auto.tags.count(), 1) | ||||
|  | ||||
|         self.assertEqual(d_first.tags.first(), self.tag_first) | ||||
|         self.assertEqual(d_second.tags.first(), self.tag_second) | ||||
|  | ||||
|     def test_add_type(self): | ||||
|         call_command('document_retagger', '--document_type') | ||||
|         d_first, d_second, d_unrelated = self.get_updated_docs() | ||||
|         d_first, d_second, d_unrelated, d_auto = self.get_updated_docs() | ||||
|  | ||||
|         self.assertEqual(d_first.document_type, self.doctype_first) | ||||
|         self.assertEqual(d_second.document_type, self.doctype_second) | ||||
|  | ||||
|     def test_add_correspondent(self): | ||||
|         call_command('document_retagger', '--correspondent') | ||||
|         d_first, d_second, d_unrelated = self.get_updated_docs() | ||||
|         d_first, d_second, d_unrelated, d_auto = self.get_updated_docs() | ||||
|  | ||||
|         self.assertEqual(d_first.correspondent, self.correspondent_first) | ||||
|         self.assertEqual(d_second.correspondent, self.correspondent_second) | ||||
| @@ -68,11 +73,55 @@ class TestRetagger(DirectoriesMixin, TestCase): | ||||
|  | ||||
|         call_command('document_retagger', '--tags', '--overwrite') | ||||
|  | ||||
|         d_first, d_second, d_unrelated = self.get_updated_docs() | ||||
|         d_first, d_second, d_unrelated, d_auto = self.get_updated_docs() | ||||
|  | ||||
|         self.assertIsNotNone(Tag.objects.get(id=self.tag_second.id)) | ||||
|  | ||||
|         self.assertCountEqual([tag.id for tag in d_first.tags.all()], [self.tag_first.id]) | ||||
|         self.assertCountEqual([tag.id for tag in d_second.tags.all()], [self.tag_second.id]) | ||||
|         self.assertCountEqual([tag.id for tag in d_unrelated.tags.all()], [self.tag_inbox.id, self.tag_no_match.id]) | ||||
|         self.assertEqual(d_auto.tags.count(), 0) | ||||
|  | ||||
|     def test_add_tags_suggest(self): | ||||
|         call_command('document_retagger', '--tags', '--suggest') | ||||
|         d_first, d_second, d_unrelated, d_auto = self.get_updated_docs() | ||||
|  | ||||
|         self.assertEqual(d_first.tags.count(), 0) | ||||
|         self.assertEqual(d_second.tags.count(), 0) | ||||
|         self.assertEqual(d_auto.tags.count(), 1) | ||||
|  | ||||
|     def test_add_type_suggest(self): | ||||
|         call_command('document_retagger', '--document_type', '--suggest') | ||||
|         d_first, d_second, d_unrelated, d_auto = self.get_updated_docs() | ||||
|  | ||||
|         self.assertEqual(d_first.document_type, None) | ||||
|         self.assertEqual(d_second.document_type, None) | ||||
|  | ||||
|     def test_add_correspondent_suggest(self): | ||||
|         call_command('document_retagger', '--correspondent', '--suggest') | ||||
|         d_first, d_second, d_unrelated, d_auto = self.get_updated_docs() | ||||
|  | ||||
|         self.assertEqual(d_first.correspondent, None) | ||||
|         self.assertEqual(d_second.correspondent, None) | ||||
|  | ||||
|     def test_add_tags_suggest_url(self): | ||||
|         call_command('document_retagger', '--tags', '--suggest', '--base-url=http://localhost') | ||||
|         d_first, d_second, d_unrelated, d_auto = self.get_updated_docs() | ||||
|  | ||||
|         self.assertEqual(d_first.tags.count(), 0) | ||||
|         self.assertEqual(d_second.tags.count(), 0) | ||||
|         self.assertEqual(d_auto.tags.count(), 1) | ||||
|  | ||||
|     def test_add_type_suggest_url(self): | ||||
|         call_command('document_retagger', '--document_type', '--suggest', '--base-url=http://localhost') | ||||
|         d_first, d_second, d_unrelated, d_auto = self.get_updated_docs() | ||||
|  | ||||
|         self.assertEqual(d_first.document_type, None) | ||||
|         self.assertEqual(d_second.document_type, None) | ||||
|  | ||||
|     def test_add_correspondent_suggest_url(self): | ||||
|         call_command('document_retagger', '--correspondent', '--suggest', '--base-url=http://localhost') | ||||
|         d_first, d_second, d_unrelated, d_auto = self.get_updated_docs() | ||||
|  | ||||
|         self.assertEqual(d_first.correspondent, None) | ||||
|         self.assertEqual(d_second.correspondent, None) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Jonas Winkler
					Jonas Winkler