mirror of
				https://github.com/paperless-ngx/paperless-ngx.git
				synced 2025-11-04 03:27:12 -05:00 
			
		
		
		
	Merge branch 'dev' into feature/2396-better-mail-actions
This commit is contained in:
		
						commit
						b7a2601724
					
				@ -180,7 +180,7 @@ RUN set -eux \
 | 
				
			|||||||
RUN set -eux \
 | 
					RUN set -eux \
 | 
				
			||||||
  && echo "Getting binaries" \
 | 
					  && echo "Getting binaries" \
 | 
				
			||||||
    && mkdir paperless-ngx \
 | 
					    && mkdir paperless-ngx \
 | 
				
			||||||
    && curl --fail --silent --show-error --output paperless-ngx.tar.gz --location https://github.com/paperless-ngx/paperless-ngx/archive/41d6e7e407af09a0882736d50c89b6e015997bff.tar.gz \
 | 
					    && curl --fail --silent --show-error --output paperless-ngx.tar.gz --location https://github.com/paperless-ngx/paperless-ngx/archive/2a18975fe33e7b1c06254a72a2433c914cd49ed1.tar.gz \
 | 
				
			||||||
    && tar -xf paperless-ngx.tar.gz --directory paperless-ngx --strip-components=1 \
 | 
					    && tar -xf paperless-ngx.tar.gz --directory paperless-ngx --strip-components=1 \
 | 
				
			||||||
    && cd paperless-ngx \
 | 
					    && cd paperless-ngx \
 | 
				
			||||||
    # Setting a specific revision ensures we know what this installed
 | 
					    # Setting a specific revision ensures we know what this installed
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										968
									
								
								Pipfile.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										968
									
								
								Pipfile.lock
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										11
									
								
								docs/api.md
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								docs/api.md
									
									
									
									
									
								
							@ -257,11 +257,14 @@ The endpoint supports the following optional form fields:
 | 
				
			|||||||
- `tags`: Similar to correspondent. Specify this multiple times to
 | 
					- `tags`: Similar to correspondent. Specify this multiple times to
 | 
				
			||||||
  have multiple tags added to the document.
 | 
					  have multiple tags added to the document.
 | 
				
			||||||
- `owner`: An optional user ID to set as the owner.
 | 
					- `owner`: An optional user ID to set as the owner.
 | 
				
			||||||
 | 
					- `archive_serial_number`: An optional archive serial number to set.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
The endpoint will immediately return "OK" if the document consumption
 | 
					The endpoint will immediately return HTTP 200 if the document consumption
 | 
				
			||||||
process was started successfully. No additional status information about
 | 
					process was started successfully, with the UUID of the consumption task
 | 
				
			||||||
the consumption process itself is available, since that happens in a
 | 
					as the data. No additional status information about
 | 
				
			||||||
different process.
 | 
					the consumption process itself is available immediately, since that happens in a
 | 
				
			||||||
 | 
					different process. Querying the tasks endpoint with the returned UUID will
 | 
				
			||||||
 | 
					provide information on the state of the consumption.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## API Versioning
 | 
					## API Versioning
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -358,6 +358,7 @@ export class FilterableDropdownComponent {
 | 
				
			|||||||
      }, 0)
 | 
					      }, 0)
 | 
				
			||||||
      if (this.editing) {
 | 
					      if (this.editing) {
 | 
				
			||||||
        this.selectionModel.reset()
 | 
					        this.selectionModel.reset()
 | 
				
			||||||
 | 
					        this.modelIsDirty = false
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      this.opened.next(this)
 | 
					      this.opened.next(this)
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
 | 
				
			|||||||
@ -71,7 +71,7 @@
 | 
				
			|||||||
  <div class="col-auto ms-auto mb-2 mb-xl-0 d-flex">
 | 
					  <div class="col-auto ms-auto mb-2 mb-xl-0 d-flex">
 | 
				
			||||||
    <div class="btn-toolbar me-2">
 | 
					    <div class="btn-toolbar me-2">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      <button type="button" class="btn btn-sm btn-outline-primary me-2" (click)="setPermissions()" [disabled]="!userOwnsAll">
 | 
					      <button type="button" class="btn btn-sm btn-outline-primary me-2" (click)="setPermissions()" [disabled]="!userOwnsAll || !userCanEditAll">
 | 
				
			||||||
        <svg width="1em" height="1em" viewBox="0 0 16 16" fill="currentColor">
 | 
					        <svg width="1em" height="1em" viewBox="0 0 16 16" fill="currentColor">
 | 
				
			||||||
          <use xlink:href="assets/bootstrap-icons.svg#person-fill-lock" />
 | 
					          <use xlink:href="assets/bootstrap-icons.svg#person-fill-lock" />
 | 
				
			||||||
        </svg> <ng-container i18n>Permissions</ng-container>
 | 
					        </svg> <ng-container i18n>Permissions</ng-container>
 | 
				
			||||||
 | 
				
			|||||||
@ -27,7 +27,11 @@ import { PaperlessStoragePath } from 'src/app/data/paperless-storage-path'
 | 
				
			|||||||
import { SETTINGS_KEYS } from 'src/app/data/paperless-uisettings'
 | 
					import { SETTINGS_KEYS } from 'src/app/data/paperless-uisettings'
 | 
				
			||||||
import { ComponentWithPermissions } from '../../with-permissions/with-permissions.component'
 | 
					import { ComponentWithPermissions } from '../../with-permissions/with-permissions.component'
 | 
				
			||||||
import { PermissionsDialogComponent } from '../../common/permissions-dialog/permissions-dialog.component'
 | 
					import { PermissionsDialogComponent } from '../../common/permissions-dialog/permissions-dialog.component'
 | 
				
			||||||
import { PermissionsService } from 'src/app/services/permissions.service'
 | 
					import {
 | 
				
			||||||
 | 
					  PermissionAction,
 | 
				
			||||||
 | 
					  PermissionsService,
 | 
				
			||||||
 | 
					  PermissionType,
 | 
				
			||||||
 | 
					} from 'src/app/services/permissions.service'
 | 
				
			||||||
import { FormControl, FormGroup } from '@angular/forms'
 | 
					import { FormControl, FormGroup } from '@angular/forms'
 | 
				
			||||||
import { first, Subject, takeUntil } from 'rxjs'
 | 
					import { first, Subject, takeUntil } from 'rxjs'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -83,7 +87,12 @@ export class BulkEditorComponent
 | 
				
			|||||||
  )
 | 
					  )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  get userCanEditAll(): boolean {
 | 
					  get userCanEditAll(): boolean {
 | 
				
			||||||
    let canEdit: boolean = true
 | 
					    let canEdit: boolean = this.permissionService.currentUserCan(
 | 
				
			||||||
 | 
					      PermissionAction.Change,
 | 
				
			||||||
 | 
					      PermissionType.Document
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    if (!canEdit) return false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const docs = this.list.documents.filter((d) => this.list.selected.has(d.id))
 | 
					    const docs = this.list.documents.filter((d) => this.list.selected.has(d.id))
 | 
				
			||||||
    canEdit = docs.every((d) =>
 | 
					    canEdit = docs.every((d) =>
 | 
				
			||||||
      this.permissionService.currentUserHasObjectPermissions(
 | 
					      this.permissionService.currentUserHasObjectPermissions(
 | 
				
			||||||
 | 
				
			|||||||
@ -691,6 +691,14 @@ class PostDocumentSerializer(serializers.Serializer):
 | 
				
			|||||||
        required=False,
 | 
					        required=False,
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    archive_serial_number = serializers.IntegerField(
 | 
				
			||||||
 | 
					        label="ASN",
 | 
				
			||||||
 | 
					        write_only=True,
 | 
				
			||||||
 | 
					        required=False,
 | 
				
			||||||
 | 
					        min_value=Document.ARCHIVE_SERIAL_NUMBER_MIN,
 | 
				
			||||||
 | 
					        max_value=Document.ARCHIVE_SERIAL_NUMBER_MAX,
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def validate_document(self, document):
 | 
					    def validate_document(self, document):
 | 
				
			||||||
        document_data = document.file.read()
 | 
					        document_data = document.file.read()
 | 
				
			||||||
        mime_type = magic.from_buffer(document_data, mime=True)
 | 
					        mime_type = magic.from_buffer(document_data, mime=True)
 | 
				
			||||||
 | 
				
			|||||||
@ -4,6 +4,7 @@ import os
 | 
				
			|||||||
import shutil
 | 
					import shutil
 | 
				
			||||||
import uuid
 | 
					import uuid
 | 
				
			||||||
from pathlib import Path
 | 
					from pathlib import Path
 | 
				
			||||||
 | 
					from typing import Optional
 | 
				
			||||||
from typing import Type
 | 
					from typing import Type
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import dateutil.parser
 | 
					import dateutil.parser
 | 
				
			||||||
@ -97,6 +98,7 @@ def consume_file(
 | 
				
			|||||||
    task_id=None,
 | 
					    task_id=None,
 | 
				
			||||||
    override_created=None,
 | 
					    override_created=None,
 | 
				
			||||||
    override_owner_id=None,
 | 
					    override_owner_id=None,
 | 
				
			||||||
 | 
					    override_archive_serial_num: Optional[int] = None,
 | 
				
			||||||
):
 | 
					):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    path = Path(path).resolve()
 | 
					    path = Path(path).resolve()
 | 
				
			||||||
@ -207,7 +209,7 @@ def consume_file(
 | 
				
			|||||||
        override_tag_ids=override_tag_ids,
 | 
					        override_tag_ids=override_tag_ids,
 | 
				
			||||||
        task_id=task_id,
 | 
					        task_id=task_id,
 | 
				
			||||||
        override_created=override_created,
 | 
					        override_created=override_created,
 | 
				
			||||||
        override_asn=asn,
 | 
					        override_asn=override_archive_serial_num or asn,
 | 
				
			||||||
        override_owner_id=override_owner_id,
 | 
					        override_owner_id=override_owner_id,
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@ -9,10 +9,11 @@ from documents import barcodes
 | 
				
			|||||||
from documents import tasks
 | 
					from documents import tasks
 | 
				
			||||||
from documents.consumer import ConsumerError
 | 
					from documents.consumer import ConsumerError
 | 
				
			||||||
from documents.tests.utils import DirectoriesMixin
 | 
					from documents.tests.utils import DirectoriesMixin
 | 
				
			||||||
 | 
					from documents.tests.utils import FileSystemAssertsMixin
 | 
				
			||||||
from PIL import Image
 | 
					from PIL import Image
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class TestBarcode(DirectoriesMixin, TestCase):
 | 
					class TestBarcode(DirectoriesMixin, FileSystemAssertsMixin, TestCase):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    SAMPLE_DIR = os.path.join(
 | 
					    SAMPLE_DIR = os.path.join(
 | 
				
			||||||
        os.path.dirname(__file__),
 | 
					        os.path.dirname(__file__),
 | 
				
			||||||
@ -253,7 +254,7 @@ class TestBarcode(DirectoriesMixin, TestCase):
 | 
				
			|||||||
        shutil.copy(test_file, dst)
 | 
					        shutil.copy(test_file, dst)
 | 
				
			||||||
        target_file = barcodes.convert_from_tiff_to_pdf(dst)
 | 
					        target_file = barcodes.convert_from_tiff_to_pdf(dst)
 | 
				
			||||||
        file_extension = os.path.splitext(os.path.basename(target_file))[1]
 | 
					        file_extension = os.path.splitext(os.path.basename(target_file))[1]
 | 
				
			||||||
        self.assertTrue(os.path.isfile(target_file))
 | 
					        self.assertIsFile(target_file)
 | 
				
			||||||
        self.assertEqual(file_extension, ".pdf")
 | 
					        self.assertEqual(file_extension, ".pdf")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_convert_error_from_pdf_to_pdf(self):
 | 
					    def test_convert_error_from_pdf_to_pdf(self):
 | 
				
			||||||
@ -634,7 +635,7 @@ class TestBarcode(DirectoriesMixin, TestCase):
 | 
				
			|||||||
        )
 | 
					        )
 | 
				
			||||||
        barcodes.save_to_dir(test_file, target_dir=settings.SCRATCH_DIR)
 | 
					        barcodes.save_to_dir(test_file, target_dir=settings.SCRATCH_DIR)
 | 
				
			||||||
        target_file = os.path.join(settings.SCRATCH_DIR, "patch-code-t.pdf")
 | 
					        target_file = os.path.join(settings.SCRATCH_DIR, "patch-code-t.pdf")
 | 
				
			||||||
        self.assertTrue(os.path.isfile(target_file))
 | 
					        self.assertIsFile(target_file)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_save_to_dir_not_existing(self):
 | 
					    def test_save_to_dir_not_existing(self):
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
@ -651,8 +652,7 @@ class TestBarcode(DirectoriesMixin, TestCase):
 | 
				
			|||||||
            "patch-code-t.pdf",
 | 
					            "patch-code-t.pdf",
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        nonexistingdir = "/nowhere"
 | 
					        nonexistingdir = "/nowhere"
 | 
				
			||||||
        if os.path.isdir(nonexistingdir):
 | 
					        self.assertIsNotDir(nonexistingdir)
 | 
				
			||||||
            self.fail("non-existing dir exists")
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        with self.assertLogs("paperless.barcodes", level="WARNING") as cm:
 | 
					        with self.assertLogs("paperless.barcodes", level="WARNING") as cm:
 | 
				
			||||||
            barcodes.save_to_dir(test_file, target_dir=nonexistingdir)
 | 
					            barcodes.save_to_dir(test_file, target_dir=nonexistingdir)
 | 
				
			||||||
@ -683,7 +683,7 @@ class TestBarcode(DirectoriesMixin, TestCase):
 | 
				
			|||||||
            target_dir=settings.SCRATCH_DIR,
 | 
					            target_dir=settings.SCRATCH_DIR,
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        target_file = os.path.join(settings.SCRATCH_DIR, "newname.pdf")
 | 
					        target_file = os.path.join(settings.SCRATCH_DIR, "newname.pdf")
 | 
				
			||||||
        self.assertTrue(os.path.isfile(target_file))
 | 
					        self.assertIsFile(target_file)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_barcode_splitter(self):
 | 
					    def test_barcode_splitter(self):
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
@ -724,8 +724,8 @@ class TestBarcode(DirectoriesMixin, TestCase):
 | 
				
			|||||||
            "patch-code-t-middle_document_1.pdf",
 | 
					            "patch-code-t-middle_document_1.pdf",
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.assertTrue(os.path.isfile(target_file1))
 | 
					        self.assertIsFile(target_file1)
 | 
				
			||||||
        self.assertTrue(os.path.isfile(target_file2))
 | 
					        self.assertIsFile(target_file2)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @override_settings(CONSUMER_ENABLE_BARCODES=True)
 | 
					    @override_settings(CONSUMER_ENABLE_BARCODES=True)
 | 
				
			||||||
    def test_consume_barcode_file(self):
 | 
					    def test_consume_barcode_file(self):
 | 
				
			||||||
 | 
				
			|||||||
@ -30,6 +30,7 @@ from ..parsers import DocumentParser
 | 
				
			|||||||
from ..parsers import ParseError
 | 
					from ..parsers import ParseError
 | 
				
			||||||
from ..tasks import sanity_check
 | 
					from ..tasks import sanity_check
 | 
				
			||||||
from .utils import DirectoriesMixin
 | 
					from .utils import DirectoriesMixin
 | 
				
			||||||
 | 
					from documents.tests.utils import FileSystemAssertsMixin
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class TestAttributes(TestCase):
 | 
					class TestAttributes(TestCase):
 | 
				
			||||||
@ -241,7 +242,7 @@ def fake_magic_from_file(file, mime=False):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@mock.patch("documents.consumer.magic.from_file", fake_magic_from_file)
 | 
					@mock.patch("documents.consumer.magic.from_file", fake_magic_from_file)
 | 
				
			||||||
class TestConsumer(DirectoriesMixin, TestCase):
 | 
					class TestConsumer(DirectoriesMixin, FileSystemAssertsMixin, TestCase):
 | 
				
			||||||
    def _assert_first_last_send_progress(
 | 
					    def _assert_first_last_send_progress(
 | 
				
			||||||
        self,
 | 
					        self,
 | 
				
			||||||
        first_status="STARTING",
 | 
					        first_status="STARTING",
 | 
				
			||||||
@ -346,16 +347,16 @@ class TestConsumer(DirectoriesMixin, TestCase):
 | 
				
			|||||||
        self.assertEqual(document.filename, "0000001.pdf")
 | 
					        self.assertEqual(document.filename, "0000001.pdf")
 | 
				
			||||||
        self.assertEqual(document.archive_filename, "0000001.pdf")
 | 
					        self.assertEqual(document.archive_filename, "0000001.pdf")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.assertTrue(os.path.isfile(document.source_path))
 | 
					        self.assertIsFile(document.source_path)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.assertTrue(os.path.isfile(document.thumbnail_path))
 | 
					        self.assertIsFile(document.thumbnail_path)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.assertTrue(os.path.isfile(document.archive_path))
 | 
					        self.assertIsFile(document.archive_path)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.assertEqual(document.checksum, "42995833e01aea9b3edee44bbfdd7ce1")
 | 
					        self.assertEqual(document.checksum, "42995833e01aea9b3edee44bbfdd7ce1")
 | 
				
			||||||
        self.assertEqual(document.archive_checksum, "62acb0bcbfbcaa62ca6ad3668e4e404b")
 | 
					        self.assertEqual(document.archive_checksum, "62acb0bcbfbcaa62ca6ad3668e4e404b")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.assertFalse(os.path.isfile(filename))
 | 
					        self.assertIsNotFile(filename)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self._assert_first_last_send_progress()
 | 
					        self._assert_first_last_send_progress()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -383,14 +384,14 @@ class TestConsumer(DirectoriesMixin, TestCase):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        shutil.copy(filename, shadow_file)
 | 
					        shutil.copy(filename, shadow_file)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.assertTrue(os.path.isfile(shadow_file))
 | 
					        self.assertIsFile(shadow_file)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        document = self.consumer.try_consume_file(filename)
 | 
					        document = self.consumer.try_consume_file(filename)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.assertTrue(os.path.isfile(document.source_path))
 | 
					        self.assertIsFile(document.source_path)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.assertFalse(os.path.isfile(shadow_file))
 | 
					        self.assertIsNotFile(shadow_file)
 | 
				
			||||||
        self.assertFalse(os.path.isfile(filename))
 | 
					        self.assertIsNotFile(filename)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def testOverrideFilename(self):
 | 
					    def testOverrideFilename(self):
 | 
				
			||||||
        filename = self.get_test_file()
 | 
					        filename = self.get_test_file()
 | 
				
			||||||
@ -536,7 +537,7 @@ class TestConsumer(DirectoriesMixin, TestCase):
 | 
				
			|||||||
        self._assert_first_last_send_progress(last_status="FAILED")
 | 
					        self._assert_first_last_send_progress(last_status="FAILED")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # file not deleted
 | 
					        # file not deleted
 | 
				
			||||||
        self.assertTrue(os.path.isfile(filename))
 | 
					        self.assertIsFile(filename)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # Database empty
 | 
					        # Database empty
 | 
				
			||||||
        self.assertEqual(len(Document.objects.all()), 0)
 | 
					        self.assertEqual(len(Document.objects.all()), 0)
 | 
				
			||||||
@ -573,9 +574,9 @@ class TestConsumer(DirectoriesMixin, TestCase):
 | 
				
			|||||||
        document = self.consumer.try_consume_file(filename, override_title="new docs")
 | 
					        document = self.consumer.try_consume_file(filename, override_title="new docs")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.assertEqual(document.title, "new docs")
 | 
					        self.assertEqual(document.title, "new docs")
 | 
				
			||||||
        self.assertIsNotNone(os.path.isfile(document.title))
 | 
					        self.assertIsNotNone(document.title)
 | 
				
			||||||
        self.assertTrue(os.path.isfile(document.source_path))
 | 
					        self.assertIsFile(document.source_path)
 | 
				
			||||||
        self.assertTrue(os.path.isfile(document.archive_path))
 | 
					        self.assertIsFile(document.archive_path)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self._assert_first_last_send_progress()
 | 
					        self._assert_first_last_send_progress()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -603,35 +604,35 @@ class TestConsumer(DirectoriesMixin, TestCase):
 | 
				
			|||||||
    @override_settings(CONSUMER_DELETE_DUPLICATES=True)
 | 
					    @override_settings(CONSUMER_DELETE_DUPLICATES=True)
 | 
				
			||||||
    def test_delete_duplicate(self):
 | 
					    def test_delete_duplicate(self):
 | 
				
			||||||
        dst = self.get_test_file()
 | 
					        dst = self.get_test_file()
 | 
				
			||||||
        self.assertTrue(os.path.isfile(dst))
 | 
					        self.assertIsFile(dst)
 | 
				
			||||||
        doc = self.consumer.try_consume_file(dst)
 | 
					        doc = self.consumer.try_consume_file(dst)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self._assert_first_last_send_progress()
 | 
					        self._assert_first_last_send_progress()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.assertFalse(os.path.isfile(dst))
 | 
					        self.assertIsNotFile(dst)
 | 
				
			||||||
        self.assertIsNotNone(doc)
 | 
					        self.assertIsNotNone(doc)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self._send_progress.reset_mock()
 | 
					        self._send_progress.reset_mock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        dst = self.get_test_file()
 | 
					        dst = self.get_test_file()
 | 
				
			||||||
        self.assertTrue(os.path.isfile(dst))
 | 
					        self.assertIsFile(dst)
 | 
				
			||||||
        self.assertRaises(ConsumerError, self.consumer.try_consume_file, dst)
 | 
					        self.assertRaises(ConsumerError, self.consumer.try_consume_file, dst)
 | 
				
			||||||
        self.assertFalse(os.path.isfile(dst))
 | 
					        self.assertIsNotFile(dst)
 | 
				
			||||||
        self._assert_first_last_send_progress(last_status="FAILED")
 | 
					        self._assert_first_last_send_progress(last_status="FAILED")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @override_settings(CONSUMER_DELETE_DUPLICATES=False)
 | 
					    @override_settings(CONSUMER_DELETE_DUPLICATES=False)
 | 
				
			||||||
    def test_no_delete_duplicate(self):
 | 
					    def test_no_delete_duplicate(self):
 | 
				
			||||||
        dst = self.get_test_file()
 | 
					        dst = self.get_test_file()
 | 
				
			||||||
        self.assertTrue(os.path.isfile(dst))
 | 
					        self.assertIsFile(dst)
 | 
				
			||||||
        doc = self.consumer.try_consume_file(dst)
 | 
					        doc = self.consumer.try_consume_file(dst)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.assertFalse(os.path.isfile(dst))
 | 
					        self.assertIsNotFile(dst)
 | 
				
			||||||
        self.assertIsNotNone(doc)
 | 
					        self.assertIsNotNone(doc)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        dst = self.get_test_file()
 | 
					        dst = self.get_test_file()
 | 
				
			||||||
        self.assertTrue(os.path.isfile(dst))
 | 
					        self.assertIsFile(dst)
 | 
				
			||||||
        self.assertRaises(ConsumerError, self.consumer.try_consume_file, dst)
 | 
					        self.assertRaises(ConsumerError, self.consumer.try_consume_file, dst)
 | 
				
			||||||
        self.assertTrue(os.path.isfile(dst))
 | 
					        self.assertIsFile(dst)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self._assert_first_last_send_progress(last_status="FAILED")
 | 
					        self._assert_first_last_send_progress(last_status="FAILED")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -9,8 +9,8 @@ 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
 | 
				
			||||||
from django.utils import timezone
 | 
					from django.utils import timezone
 | 
				
			||||||
 | 
					from documents.tests.utils import FileSystemAssertsMixin
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from ..bulk_edit import bulk_update_documents
 | 
					 | 
				
			||||||
from ..file_handling import create_source_path_directory
 | 
					from ..file_handling import create_source_path_directory
 | 
				
			||||||
from ..file_handling import delete_empty_directories
 | 
					from ..file_handling import delete_empty_directories
 | 
				
			||||||
from ..file_handling import generate_filename
 | 
					from ..file_handling import generate_filename
 | 
				
			||||||
@ -21,7 +21,7 @@ from ..models import StoragePath
 | 
				
			|||||||
from .utils import DirectoriesMixin
 | 
					from .utils import DirectoriesMixin
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class TestFileHandling(DirectoriesMixin, TestCase):
 | 
					class TestFileHandling(DirectoriesMixin, FileSystemAssertsMixin, TestCase):
 | 
				
			||||||
    @override_settings(FILENAME_FORMAT="")
 | 
					    @override_settings(FILENAME_FORMAT="")
 | 
				
			||||||
    def test_generate_source_filename(self):
 | 
					    def test_generate_source_filename(self):
 | 
				
			||||||
        document = Document()
 | 
					        document = Document()
 | 
				
			||||||
@ -47,7 +47,7 @@ class TestFileHandling(DirectoriesMixin, TestCase):
 | 
				
			|||||||
        # Test default source_path
 | 
					        # Test default source_path
 | 
				
			||||||
        self.assertEqual(
 | 
					        self.assertEqual(
 | 
				
			||||||
            document.source_path,
 | 
					            document.source_path,
 | 
				
			||||||
            settings.ORIGINALS_DIR + f"/{document.pk:07d}.pdf",
 | 
					            os.path.join(settings.ORIGINALS_DIR, f"{document.pk:07d}.pdf"),
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        document.filename = generate_filename(document)
 | 
					        document.filename = generate_filename(document)
 | 
				
			||||||
@ -65,18 +65,17 @@ class TestFileHandling(DirectoriesMixin, TestCase):
 | 
				
			|||||||
        # test that creating dirs for the source_path creates the correct directory
 | 
					        # test that creating dirs for the source_path creates the correct directory
 | 
				
			||||||
        create_source_path_directory(document.source_path)
 | 
					        create_source_path_directory(document.source_path)
 | 
				
			||||||
        Path(document.source_path).touch()
 | 
					        Path(document.source_path).touch()
 | 
				
			||||||
        self.assertEqual(os.path.isdir(settings.ORIGINALS_DIR + "/none"), True)
 | 
					        self.assertIsDir(os.path.join(settings.ORIGINALS_DIR, "none"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # Set a correspondent and save the document
 | 
					        # Set a correspondent and save the document
 | 
				
			||||||
        document.correspondent = Correspondent.objects.get_or_create(name="test")[0]
 | 
					        document.correspondent = Correspondent.objects.get_or_create(name="test")[0]
 | 
				
			||||||
        document.save()
 | 
					        document.save()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # Check proper handling of files
 | 
					        # Check proper handling of files
 | 
				
			||||||
        self.assertEqual(os.path.isdir(settings.ORIGINALS_DIR + "/test"), True)
 | 
					        self.assertIsDir(os.path.join(settings.ORIGINALS_DIR, "test"))
 | 
				
			||||||
        self.assertEqual(os.path.isdir(settings.ORIGINALS_DIR + "/none"), False)
 | 
					        self.assertIsNotDir(os.path.join(settings.ORIGINALS_DIR, "none"))
 | 
				
			||||||
        self.assertEqual(
 | 
					        self.assertIsFile(
 | 
				
			||||||
            os.path.isfile(settings.ORIGINALS_DIR + "/test/test.pdf.gpg"),
 | 
					            os.path.join(settings.ORIGINALS_DIR, "test/test.pdf.gpg"),
 | 
				
			||||||
            True,
 | 
					 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @override_settings(FILENAME_FORMAT="{correspondent}/{correspondent}")
 | 
					    @override_settings(FILENAME_FORMAT="{correspondent}/{correspondent}")
 | 
				
			||||||
@ -95,24 +94,23 @@ class TestFileHandling(DirectoriesMixin, TestCase):
 | 
				
			|||||||
        # Test source_path
 | 
					        # Test source_path
 | 
				
			||||||
        self.assertEqual(
 | 
					        self.assertEqual(
 | 
				
			||||||
            document.source_path,
 | 
					            document.source_path,
 | 
				
			||||||
            settings.ORIGINALS_DIR + "/none/none.pdf",
 | 
					            os.path.join(settings.ORIGINALS_DIR, "none/none.pdf"),
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # Make the folder read- and execute-only (no writing and no renaming)
 | 
					        # Make the folder read- and execute-only (no writing and no renaming)
 | 
				
			||||||
        os.chmod(settings.ORIGINALS_DIR + "/none", 0o555)
 | 
					        os.chmod(os.path.join(settings.ORIGINALS_DIR, "none"), 0o555)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # Set a correspondent and save the document
 | 
					        # Set a correspondent and save the document
 | 
				
			||||||
        document.correspondent = Correspondent.objects.get_or_create(name="test")[0]
 | 
					        document.correspondent = Correspondent.objects.get_or_create(name="test")[0]
 | 
				
			||||||
        document.save()
 | 
					        document.save()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # Check proper handling of files
 | 
					        # Check proper handling of files
 | 
				
			||||||
        self.assertEqual(
 | 
					        self.assertIsFile(
 | 
				
			||||||
            os.path.isfile(settings.ORIGINALS_DIR + "/none/none.pdf"),
 | 
					            os.path.join(settings.ORIGINALS_DIR, "none/none.pdf"),
 | 
				
			||||||
            True,
 | 
					 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        self.assertEqual(document.filename, "none/none.pdf")
 | 
					        self.assertEqual(document.filename, "none/none.pdf")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        os.chmod(settings.ORIGINALS_DIR + "/none", 0o777)
 | 
					        os.chmod(os.path.join(settings.ORIGINALS_DIR, "none"), 0o777)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @override_settings(FILENAME_FORMAT="{correspondent}/{correspondent}")
 | 
					    @override_settings(FILENAME_FORMAT="{correspondent}/{correspondent}")
 | 
				
			||||||
    def test_file_renaming_database_error(self):
 | 
					    def test_file_renaming_database_error(self):
 | 
				
			||||||
@ -136,7 +134,7 @@ class TestFileHandling(DirectoriesMixin, TestCase):
 | 
				
			|||||||
        Path(document.source_path).touch()
 | 
					        Path(document.source_path).touch()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # Test source_path
 | 
					        # Test source_path
 | 
				
			||||||
        self.assertTrue(os.path.isfile(document.source_path))
 | 
					        self.assertIsFile(document.source_path)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # Set a correspondent and save the document
 | 
					        # Set a correspondent and save the document
 | 
				
			||||||
        document.correspondent = Correspondent.objects.get_or_create(name="test")[0]
 | 
					        document.correspondent = Correspondent.objects.get_or_create(name="test")[0]
 | 
				
			||||||
@ -146,10 +144,9 @@ class TestFileHandling(DirectoriesMixin, TestCase):
 | 
				
			|||||||
            document.save()
 | 
					            document.save()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            # Check proper handling of files
 | 
					            # Check proper handling of files
 | 
				
			||||||
            self.assertTrue(os.path.isfile(document.source_path))
 | 
					            self.assertIsFile(document.source_path)
 | 
				
			||||||
            self.assertEqual(
 | 
					            self.assertIsFile(
 | 
				
			||||||
                os.path.isfile(settings.ORIGINALS_DIR + "/none/none.pdf"),
 | 
					                os.path.join(settings.ORIGINALS_DIR, "none/none.pdf"),
 | 
				
			||||||
                True,
 | 
					 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
            self.assertEqual(document.filename, "none/none.pdf")
 | 
					            self.assertEqual(document.filename, "none/none.pdf")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -170,11 +167,10 @@ class TestFileHandling(DirectoriesMixin, TestCase):
 | 
				
			|||||||
        # Ensure file deletion after delete
 | 
					        # Ensure file deletion after delete
 | 
				
			||||||
        pk = document.pk
 | 
					        pk = document.pk
 | 
				
			||||||
        document.delete()
 | 
					        document.delete()
 | 
				
			||||||
        self.assertEqual(
 | 
					        self.assertIsNotFile(
 | 
				
			||||||
            os.path.isfile(settings.ORIGINALS_DIR + "/none/none.pdf"),
 | 
					            os.path.join(settings.ORIGINALS_DIR, "none", "none.pdf"),
 | 
				
			||||||
            False,
 | 
					 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        self.assertEqual(os.path.isdir(settings.ORIGINALS_DIR + "/none"), False)
 | 
					        self.assertIsNotDir(os.path.join(settings.ORIGINALS_DIR, "none"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @override_settings(
 | 
					    @override_settings(
 | 
				
			||||||
        FILENAME_FORMAT="{correspondent}/{correspondent}",
 | 
					        FILENAME_FORMAT="{correspondent}/{correspondent}",
 | 
				
			||||||
@ -194,15 +190,14 @@ class TestFileHandling(DirectoriesMixin, TestCase):
 | 
				
			|||||||
        Path(document.source_path).touch()
 | 
					        Path(document.source_path).touch()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # Ensure file was moved to trash after delete
 | 
					        # Ensure file was moved to trash after delete
 | 
				
			||||||
        self.assertEqual(os.path.isfile(settings.TRASH_DIR + "/none/none.pdf"), False)
 | 
					        self.assertIsNotFile(os.path.join(settings.TRASH_DIR, "none", "none.pdf"))
 | 
				
			||||||
        document.delete()
 | 
					        document.delete()
 | 
				
			||||||
        self.assertEqual(
 | 
					        self.assertIsNotFile(
 | 
				
			||||||
            os.path.isfile(settings.ORIGINALS_DIR + "/none/none.pdf"),
 | 
					            os.path.join(settings.ORIGINALS_DIR, "none", "none.pdf"),
 | 
				
			||||||
            False,
 | 
					 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        self.assertEqual(os.path.isdir(settings.ORIGINALS_DIR + "/none"), False)
 | 
					        self.assertIsNotDir(os.path.join(settings.ORIGINALS_DIR, "none"))
 | 
				
			||||||
        self.assertEqual(os.path.isfile(settings.TRASH_DIR + "/none.pdf"), True)
 | 
					        self.assertIsFile(os.path.join(settings.TRASH_DIR, "none.pdf"))
 | 
				
			||||||
        self.assertEqual(os.path.isfile(settings.TRASH_DIR + "/none_01.pdf"), False)
 | 
					        self.assertIsNotFile(os.path.join(settings.TRASH_DIR, "none_01.pdf"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # Create an identical document and ensure it is trashed under a new name
 | 
					        # Create an identical document and ensure it is trashed under a new name
 | 
				
			||||||
        document = Document()
 | 
					        document = Document()
 | 
				
			||||||
@ -213,7 +208,7 @@ class TestFileHandling(DirectoriesMixin, TestCase):
 | 
				
			|||||||
        create_source_path_directory(document.source_path)
 | 
					        create_source_path_directory(document.source_path)
 | 
				
			||||||
        Path(document.source_path).touch()
 | 
					        Path(document.source_path).touch()
 | 
				
			||||||
        document.delete()
 | 
					        document.delete()
 | 
				
			||||||
        self.assertEqual(os.path.isfile(settings.TRASH_DIR + "/none_01.pdf"), True)
 | 
					        self.assertIsFile(os.path.join(settings.TRASH_DIR, "none_01.pdf"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @override_settings(FILENAME_FORMAT="{correspondent}/{correspondent}")
 | 
					    @override_settings(FILENAME_FORMAT="{correspondent}/{correspondent}")
 | 
				
			||||||
    def test_document_delete_nofile(self):
 | 
					    def test_document_delete_nofile(self):
 | 
				
			||||||
@ -246,9 +241,9 @@ class TestFileHandling(DirectoriesMixin, TestCase):
 | 
				
			|||||||
        document.save()
 | 
					        document.save()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # Check proper handling of files
 | 
					        # Check proper handling of files
 | 
				
			||||||
        self.assertEqual(os.path.isdir(settings.ORIGINALS_DIR + "/test"), True)
 | 
					        self.assertIsDir(os.path.join(settings.ORIGINALS_DIR, "test"))
 | 
				
			||||||
        self.assertEqual(os.path.isdir(settings.ORIGINALS_DIR + "/none"), True)
 | 
					        self.assertIsDir(os.path.join(settings.ORIGINALS_DIR, "none"))
 | 
				
			||||||
        self.assertTrue(os.path.isfile(important_file))
 | 
					        self.assertIsFile(important_file)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @override_settings(FILENAME_FORMAT="{document_type} - {title}")
 | 
					    @override_settings(FILENAME_FORMAT="{document_type} - {title}")
 | 
				
			||||||
    def test_document_type(self):
 | 
					    def test_document_type(self):
 | 
				
			||||||
@ -437,18 +432,17 @@ class TestFileHandling(DirectoriesMixin, TestCase):
 | 
				
			|||||||
        Path(document.source_path).touch()
 | 
					        Path(document.source_path).touch()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # Check proper handling of files
 | 
					        # Check proper handling of files
 | 
				
			||||||
        self.assertEqual(os.path.isdir(settings.ORIGINALS_DIR + "/none/none"), True)
 | 
					        self.assertIsDir(os.path.join(settings.ORIGINALS_DIR, "none/none"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        pk = document.pk
 | 
					        pk = document.pk
 | 
				
			||||||
        document.delete()
 | 
					        document.delete()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.assertEqual(
 | 
					        self.assertIsNotFile(
 | 
				
			||||||
            os.path.isfile(settings.ORIGINALS_DIR + "/none/none/none.pdf"),
 | 
					            os.path.join(settings.ORIGINALS_DIR, "none/none/none.pdf"),
 | 
				
			||||||
            False,
 | 
					 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        self.assertEqual(os.path.isdir(settings.ORIGINALS_DIR + "/none/none"), False)
 | 
					        self.assertIsNotDir(os.path.join(settings.ORIGINALS_DIR, "none/none"))
 | 
				
			||||||
        self.assertEqual(os.path.isdir(settings.ORIGINALS_DIR + "/none"), False)
 | 
					        self.assertIsNotDir(os.path.join(settings.ORIGINALS_DIR, "none"))
 | 
				
			||||||
        self.assertEqual(os.path.isdir(settings.ORIGINALS_DIR), True)
 | 
					        self.assertIsDir(settings.ORIGINALS_DIR)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @override_settings(FILENAME_FORMAT=None)
 | 
					    @override_settings(FILENAME_FORMAT=None)
 | 
				
			||||||
    def test_format_none(self):
 | 
					    def test_format_none(self):
 | 
				
			||||||
@ -472,9 +466,9 @@ class TestFileHandling(DirectoriesMixin, TestCase):
 | 
				
			|||||||
            os.path.join(tmp, "notempty", "empty"),
 | 
					            os.path.join(tmp, "notempty", "empty"),
 | 
				
			||||||
            root=settings.ORIGINALS_DIR,
 | 
					            root=settings.ORIGINALS_DIR,
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        self.assertEqual(os.path.isdir(os.path.join(tmp, "notempty")), True)
 | 
					        self.assertIsDir(os.path.join(tmp, "notempty"))
 | 
				
			||||||
        self.assertEqual(os.path.isfile(os.path.join(tmp, "notempty", "file")), True)
 | 
					        self.assertIsFile(os.path.join(tmp, "notempty", "file"))
 | 
				
			||||||
        self.assertEqual(os.path.isdir(os.path.join(tmp, "notempty", "empty")), False)
 | 
					        self.assertIsNotDir(os.path.join(tmp, "notempty", "empty"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @override_settings(FILENAME_FORMAT="{created/[title]")
 | 
					    @override_settings(FILENAME_FORMAT="{created/[title]")
 | 
				
			||||||
    def test_invalid_format(self):
 | 
					    def test_invalid_format(self):
 | 
				
			||||||
@ -513,36 +507,36 @@ class TestFileHandling(DirectoriesMixin, TestCase):
 | 
				
			|||||||
        document.filename = "0000001.pdf"
 | 
					        document.filename = "0000001.pdf"
 | 
				
			||||||
        document.save()
 | 
					        document.save()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.assertTrue(os.path.isfile(document.source_path))
 | 
					        self.assertIsFile(document.source_path)
 | 
				
			||||||
        self.assertEqual(document.filename, "qwe.pdf")
 | 
					        self.assertEqual(document.filename, "qwe.pdf")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        document2.filename = "0000002.pdf"
 | 
					        document2.filename = "0000002.pdf"
 | 
				
			||||||
        document2.save()
 | 
					        document2.save()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.assertTrue(os.path.isfile(document.source_path))
 | 
					        self.assertIsFile(document.source_path)
 | 
				
			||||||
        self.assertEqual(document2.filename, "qwe_01.pdf")
 | 
					        self.assertEqual(document2.filename, "qwe_01.pdf")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # saving should not change the file names.
 | 
					        # saving should not change the file names.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        document.save()
 | 
					        document.save()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.assertTrue(os.path.isfile(document.source_path))
 | 
					        self.assertIsFile(document.source_path)
 | 
				
			||||||
        self.assertEqual(document.filename, "qwe.pdf")
 | 
					        self.assertEqual(document.filename, "qwe.pdf")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        document2.save()
 | 
					        document2.save()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.assertTrue(os.path.isfile(document.source_path))
 | 
					        self.assertIsFile(document.source_path)
 | 
				
			||||||
        self.assertEqual(document2.filename, "qwe_01.pdf")
 | 
					        self.assertEqual(document2.filename, "qwe_01.pdf")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        document.delete()
 | 
					        document.delete()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.assertFalse(os.path.isfile(document.source_path))
 | 
					        self.assertIsNotFile(document.source_path)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # filename free, should remove _01 suffix
 | 
					        # filename free, should remove _01 suffix
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        document2.save()
 | 
					        document2.save()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.assertTrue(os.path.isfile(document.source_path))
 | 
					        self.assertIsFile(document.source_path)
 | 
				
			||||||
        self.assertEqual(document2.filename, "qwe.pdf")
 | 
					        self.assertEqual(document2.filename, "qwe.pdf")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @override_settings(FILENAME_FORMAT="{title}")
 | 
					    @override_settings(FILENAME_FORMAT="{title}")
 | 
				
			||||||
@ -564,7 +558,7 @@ class TestFileHandling(DirectoriesMixin, TestCase):
 | 
				
			|||||||
        m.assert_not_called()
 | 
					        m.assert_not_called()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class TestFileHandlingWithArchive(DirectoriesMixin, TestCase):
 | 
					class TestFileHandlingWithArchive(DirectoriesMixin, FileSystemAssertsMixin, TestCase):
 | 
				
			||||||
    @override_settings(FILENAME_FORMAT=None)
 | 
					    @override_settings(FILENAME_FORMAT=None)
 | 
				
			||||||
    def test_create_no_format(self):
 | 
					    def test_create_no_format(self):
 | 
				
			||||||
        original = os.path.join(settings.ORIGINALS_DIR, "0000001.pdf")
 | 
					        original = os.path.join(settings.ORIGINALS_DIR, "0000001.pdf")
 | 
				
			||||||
@ -579,10 +573,10 @@ class TestFileHandlingWithArchive(DirectoriesMixin, TestCase):
 | 
				
			|||||||
            archive_checksum="B",
 | 
					            archive_checksum="B",
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.assertTrue(os.path.isfile(original))
 | 
					        self.assertIsFile(original)
 | 
				
			||||||
        self.assertTrue(os.path.isfile(archive))
 | 
					        self.assertIsFile(archive)
 | 
				
			||||||
        self.assertTrue(os.path.isfile(doc.source_path))
 | 
					        self.assertIsFile(doc.source_path)
 | 
				
			||||||
        self.assertTrue(os.path.isfile(doc.archive_path))
 | 
					        self.assertIsFile(doc.archive_path)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @override_settings(FILENAME_FORMAT="{correspondent}/{title}")
 | 
					    @override_settings(FILENAME_FORMAT="{correspondent}/{title}")
 | 
				
			||||||
    def test_create_with_format(self):
 | 
					    def test_create_with_format(self):
 | 
				
			||||||
@ -599,10 +593,10 @@ class TestFileHandlingWithArchive(DirectoriesMixin, TestCase):
 | 
				
			|||||||
            archive_filename="0000001.pdf",
 | 
					            archive_filename="0000001.pdf",
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.assertFalse(os.path.isfile(original))
 | 
					        self.assertIsNotFile(original)
 | 
				
			||||||
        self.assertFalse(os.path.isfile(archive))
 | 
					        self.assertIsNotFile(archive)
 | 
				
			||||||
        self.assertTrue(os.path.isfile(doc.source_path))
 | 
					        self.assertIsFile(doc.source_path)
 | 
				
			||||||
        self.assertTrue(os.path.isfile(doc.archive_path))
 | 
					        self.assertIsFile(doc.archive_path)
 | 
				
			||||||
        self.assertEqual(
 | 
					        self.assertEqual(
 | 
				
			||||||
            doc.source_path,
 | 
					            doc.source_path,
 | 
				
			||||||
            os.path.join(settings.ORIGINALS_DIR, "none", "my_doc.pdf"),
 | 
					            os.path.join(settings.ORIGINALS_DIR, "none", "my_doc.pdf"),
 | 
				
			||||||
@ -626,10 +620,10 @@ class TestFileHandlingWithArchive(DirectoriesMixin, TestCase):
 | 
				
			|||||||
            archive_filename="0000001.pdf",
 | 
					            archive_filename="0000001.pdf",
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.assertTrue(os.path.isfile(original))
 | 
					        self.assertIsFile(original)
 | 
				
			||||||
        self.assertFalse(os.path.isfile(archive))
 | 
					        self.assertIsNotFile(archive)
 | 
				
			||||||
        self.assertTrue(os.path.isfile(doc.source_path))
 | 
					        self.assertIsFile(doc.source_path)
 | 
				
			||||||
        self.assertFalse(os.path.isfile(doc.archive_path))
 | 
					        self.assertIsNotFile(doc.archive_path)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @override_settings(FILENAME_FORMAT="{correspondent}/{title}")
 | 
					    @override_settings(FILENAME_FORMAT="{correspondent}/{title}")
 | 
				
			||||||
    def test_move_archive_exists(self):
 | 
					    def test_move_archive_exists(self):
 | 
				
			||||||
@ -649,11 +643,11 @@ class TestFileHandlingWithArchive(DirectoriesMixin, TestCase):
 | 
				
			|||||||
            archive_filename="0000001.pdf",
 | 
					            archive_filename="0000001.pdf",
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.assertFalse(os.path.isfile(original))
 | 
					        self.assertIsNotFile(original)
 | 
				
			||||||
        self.assertFalse(os.path.isfile(archive))
 | 
					        self.assertIsNotFile(archive)
 | 
				
			||||||
        self.assertTrue(os.path.isfile(doc.source_path))
 | 
					        self.assertIsFile(doc.source_path)
 | 
				
			||||||
        self.assertTrue(os.path.isfile(doc.archive_path))
 | 
					        self.assertIsFile(doc.archive_path)
 | 
				
			||||||
        self.assertTrue(os.path.isfile(existing_archive_file))
 | 
					        self.assertIsFile(existing_archive_file)
 | 
				
			||||||
        self.assertEqual(doc.archive_filename, "none/my_doc_01.pdf")
 | 
					        self.assertEqual(doc.archive_filename, "none/my_doc_01.pdf")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @override_settings(FILENAME_FORMAT="{title}")
 | 
					    @override_settings(FILENAME_FORMAT="{title}")
 | 
				
			||||||
@ -675,8 +669,8 @@ class TestFileHandlingWithArchive(DirectoriesMixin, TestCase):
 | 
				
			|||||||
        self.assertEqual(doc.filename, "document.pdf")
 | 
					        self.assertEqual(doc.filename, "document.pdf")
 | 
				
			||||||
        self.assertEqual(doc.archive_filename, "document.pdf")
 | 
					        self.assertEqual(doc.archive_filename, "document.pdf")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.assertTrue(os.path.isfile(doc.source_path))
 | 
					        self.assertIsFile(doc.source_path)
 | 
				
			||||||
        self.assertTrue(os.path.isfile(doc.archive_path))
 | 
					        self.assertIsFile(doc.archive_path)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @override_settings(FILENAME_FORMAT="{title}")
 | 
					    @override_settings(FILENAME_FORMAT="{title}")
 | 
				
			||||||
    def test_move_archive_only(self):
 | 
					    def test_move_archive_only(self):
 | 
				
			||||||
@ -697,8 +691,8 @@ class TestFileHandlingWithArchive(DirectoriesMixin, TestCase):
 | 
				
			|||||||
        self.assertEqual(doc.filename, "document.pdf")
 | 
					        self.assertEqual(doc.filename, "document.pdf")
 | 
				
			||||||
        self.assertEqual(doc.archive_filename, "document.pdf")
 | 
					        self.assertEqual(doc.archive_filename, "document.pdf")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.assertTrue(os.path.isfile(doc.source_path))
 | 
					        self.assertIsFile(doc.source_path)
 | 
				
			||||||
        self.assertTrue(os.path.isfile(doc.archive_path))
 | 
					        self.assertIsFile(doc.archive_path)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @override_settings(FILENAME_FORMAT="{correspondent}/{title}")
 | 
					    @override_settings(FILENAME_FORMAT="{correspondent}/{title}")
 | 
				
			||||||
    @mock.patch("documents.signals.handlers.os.rename")
 | 
					    @mock.patch("documents.signals.handlers.os.rename")
 | 
				
			||||||
@ -726,10 +720,10 @@ class TestFileHandlingWithArchive(DirectoriesMixin, TestCase):
 | 
				
			|||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        m.assert_called()
 | 
					        m.assert_called()
 | 
				
			||||||
        self.assertTrue(os.path.isfile(original))
 | 
					        self.assertIsFile(original)
 | 
				
			||||||
        self.assertTrue(os.path.isfile(archive))
 | 
					        self.assertIsFile(archive)
 | 
				
			||||||
        self.assertTrue(os.path.isfile(doc.source_path))
 | 
					        self.assertIsFile(doc.source_path)
 | 
				
			||||||
        self.assertTrue(os.path.isfile(doc.archive_path))
 | 
					        self.assertIsFile(doc.archive_path)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @override_settings(FILENAME_FORMAT="{correspondent}/{title}")
 | 
					    @override_settings(FILENAME_FORMAT="{correspondent}/{title}")
 | 
				
			||||||
    def test_move_file_gone(self):
 | 
					    def test_move_file_gone(self):
 | 
				
			||||||
@ -746,10 +740,10 @@ class TestFileHandlingWithArchive(DirectoriesMixin, TestCase):
 | 
				
			|||||||
            archive_checksum="B",
 | 
					            archive_checksum="B",
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.assertFalse(os.path.isfile(original))
 | 
					        self.assertIsNotFile(original)
 | 
				
			||||||
        self.assertTrue(os.path.isfile(archive))
 | 
					        self.assertIsFile(archive)
 | 
				
			||||||
        self.assertFalse(os.path.isfile(doc.source_path))
 | 
					        self.assertIsNotFile(doc.source_path)
 | 
				
			||||||
        self.assertTrue(os.path.isfile(doc.archive_path))
 | 
					        self.assertIsFile(doc.archive_path)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @override_settings(FILENAME_FORMAT="{correspondent}/{title}")
 | 
					    @override_settings(FILENAME_FORMAT="{correspondent}/{title}")
 | 
				
			||||||
    @mock.patch("documents.signals.handlers.os.rename")
 | 
					    @mock.patch("documents.signals.handlers.os.rename")
 | 
				
			||||||
@ -777,10 +771,10 @@ class TestFileHandlingWithArchive(DirectoriesMixin, TestCase):
 | 
				
			|||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        m.assert_called()
 | 
					        m.assert_called()
 | 
				
			||||||
        self.assertTrue(os.path.isfile(original))
 | 
					        self.assertIsFile(original)
 | 
				
			||||||
        self.assertTrue(os.path.isfile(archive))
 | 
					        self.assertIsFile(archive)
 | 
				
			||||||
        self.assertTrue(os.path.isfile(doc.source_path))
 | 
					        self.assertIsFile(doc.source_path)
 | 
				
			||||||
        self.assertTrue(os.path.isfile(doc.archive_path))
 | 
					        self.assertIsFile(doc.archive_path)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @override_settings(FILENAME_FORMAT="")
 | 
					    @override_settings(FILENAME_FORMAT="")
 | 
				
			||||||
    def test_archive_deleted(self):
 | 
					    def test_archive_deleted(self):
 | 
				
			||||||
@ -797,17 +791,17 @@ class TestFileHandlingWithArchive(DirectoriesMixin, TestCase):
 | 
				
			|||||||
            archive_filename="0000001.pdf",
 | 
					            archive_filename="0000001.pdf",
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.assertTrue(os.path.isfile(original))
 | 
					        self.assertIsFile(original)
 | 
				
			||||||
        self.assertTrue(os.path.isfile(archive))
 | 
					        self.assertIsFile(archive)
 | 
				
			||||||
        self.assertTrue(os.path.isfile(doc.source_path))
 | 
					        self.assertIsFile(doc.source_path)
 | 
				
			||||||
        self.assertTrue(os.path.isfile(doc.archive_path))
 | 
					        self.assertIsFile(doc.archive_path)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        doc.delete()
 | 
					        doc.delete()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.assertFalse(os.path.isfile(original))
 | 
					        self.assertIsNotFile(original)
 | 
				
			||||||
        self.assertFalse(os.path.isfile(archive))
 | 
					        self.assertIsNotFile(archive)
 | 
				
			||||||
        self.assertFalse(os.path.isfile(doc.source_path))
 | 
					        self.assertIsNotFile(doc.source_path)
 | 
				
			||||||
        self.assertFalse(os.path.isfile(doc.archive_path))
 | 
					        self.assertIsNotFile(doc.archive_path)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @override_settings(FILENAME_FORMAT="{title}")
 | 
					    @override_settings(FILENAME_FORMAT="{title}")
 | 
				
			||||||
    def test_archive_deleted2(self):
 | 
					    def test_archive_deleted2(self):
 | 
				
			||||||
@ -833,15 +827,15 @@ class TestFileHandlingWithArchive(DirectoriesMixin, TestCase):
 | 
				
			|||||||
            checksum="C",
 | 
					            checksum="C",
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.assertTrue(os.path.isfile(doc1.source_path))
 | 
					        self.assertIsFile(doc1.source_path)
 | 
				
			||||||
        self.assertTrue(os.path.isfile(doc1.archive_path))
 | 
					        self.assertIsFile(doc1.archive_path)
 | 
				
			||||||
        self.assertTrue(os.path.isfile(doc2.source_path))
 | 
					        self.assertIsFile(doc2.source_path)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        doc2.delete()
 | 
					        doc2.delete()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.assertTrue(os.path.isfile(doc1.source_path))
 | 
					        self.assertIsFile(doc1.source_path)
 | 
				
			||||||
        self.assertTrue(os.path.isfile(doc1.archive_path))
 | 
					        self.assertIsFile(doc1.archive_path)
 | 
				
			||||||
        self.assertFalse(os.path.isfile(doc2.source_path))
 | 
					        self.assertIsNotFile(doc2.source_path)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @override_settings(FILENAME_FORMAT="{correspondent}/{title}")
 | 
					    @override_settings(FILENAME_FORMAT="{correspondent}/{title}")
 | 
				
			||||||
    def test_database_error(self):
 | 
					    def test_database_error(self):
 | 
				
			||||||
@ -862,10 +856,10 @@ class TestFileHandlingWithArchive(DirectoriesMixin, TestCase):
 | 
				
			|||||||
            m.side_effect = DatabaseError()
 | 
					            m.side_effect = DatabaseError()
 | 
				
			||||||
            doc.save()
 | 
					            doc.save()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.assertTrue(os.path.isfile(original))
 | 
					        self.assertIsFile(original)
 | 
				
			||||||
        self.assertTrue(os.path.isfile(archive))
 | 
					        self.assertIsFile(archive)
 | 
				
			||||||
        self.assertTrue(os.path.isfile(doc.source_path))
 | 
					        self.assertIsFile(doc.source_path)
 | 
				
			||||||
        self.assertTrue(os.path.isfile(doc.archive_path))
 | 
					        self.assertIsFile(doc.archive_path)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class TestFilenameGeneration(DirectoriesMixin, TestCase):
 | 
					class TestFilenameGeneration(DirectoriesMixin, TestCase):
 | 
				
			||||||
 | 
				
			|||||||
@ -13,13 +13,14 @@ from documents.file_handling import generate_filename
 | 
				
			|||||||
from documents.models import Document
 | 
					from documents.models import Document
 | 
				
			||||||
from documents.tasks import update_document_archive_file
 | 
					from documents.tasks import update_document_archive_file
 | 
				
			||||||
from documents.tests.utils import DirectoriesMixin
 | 
					from documents.tests.utils import DirectoriesMixin
 | 
				
			||||||
 | 
					from documents.tests.utils import FileSystemAssertsMixin
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
sample_file = os.path.join(os.path.dirname(__file__), "samples", "simple.pdf")
 | 
					sample_file = os.path.join(os.path.dirname(__file__), "samples", "simple.pdf")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@override_settings(FILENAME_FORMAT="{correspondent}/{title}")
 | 
					@override_settings(FILENAME_FORMAT="{correspondent}/{title}")
 | 
				
			||||||
class TestArchiver(DirectoriesMixin, TestCase):
 | 
					class TestArchiver(DirectoriesMixin, FileSystemAssertsMixin, TestCase):
 | 
				
			||||||
    def make_models(self):
 | 
					    def make_models(self):
 | 
				
			||||||
        return Document.objects.create(
 | 
					        return Document.objects.create(
 | 
				
			||||||
            checksum="A",
 | 
					            checksum="A",
 | 
				
			||||||
@ -52,8 +53,8 @@ class TestArchiver(DirectoriesMixin, TestCase):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        self.assertIsNotNone(doc.checksum)
 | 
					        self.assertIsNotNone(doc.checksum)
 | 
				
			||||||
        self.assertIsNotNone(doc.archive_checksum)
 | 
					        self.assertIsNotNone(doc.archive_checksum)
 | 
				
			||||||
        self.assertTrue(os.path.isfile(doc.archive_path))
 | 
					        self.assertIsFile(doc.archive_path)
 | 
				
			||||||
        self.assertTrue(os.path.isfile(doc.source_path))
 | 
					        self.assertIsFile(doc.source_path)
 | 
				
			||||||
        self.assertTrue(filecmp.cmp(sample_file, doc.source_path))
 | 
					        self.assertTrue(filecmp.cmp(sample_file, doc.source_path))
 | 
				
			||||||
        self.assertEqual(doc.archive_filename, "none/A.pdf")
 | 
					        self.assertEqual(doc.archive_filename, "none/A.pdf")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -70,7 +71,7 @@ class TestArchiver(DirectoriesMixin, TestCase):
 | 
				
			|||||||
        self.assertIsNotNone(doc.checksum)
 | 
					        self.assertIsNotNone(doc.checksum)
 | 
				
			||||||
        self.assertIsNone(doc.archive_checksum)
 | 
					        self.assertIsNone(doc.archive_checksum)
 | 
				
			||||||
        self.assertIsNone(doc.archive_filename)
 | 
					        self.assertIsNone(doc.archive_filename)
 | 
				
			||||||
        self.assertTrue(os.path.isfile(doc.source_path))
 | 
					        self.assertIsFile(doc.source_path)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @override_settings(FILENAME_FORMAT="{title}")
 | 
					    @override_settings(FILENAME_FORMAT="{title}")
 | 
				
			||||||
    def test_naming_priorities(self):
 | 
					    def test_naming_priorities(self):
 | 
				
			||||||
@ -104,7 +105,7 @@ class TestArchiver(DirectoriesMixin, TestCase):
 | 
				
			|||||||
        self.assertEqual(doc2.archive_filename, "document_01.pdf")
 | 
					        self.assertEqual(doc2.archive_filename, "document_01.pdf")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class TestDecryptDocuments(TestCase):
 | 
					class TestDecryptDocuments(FileSystemAssertsMixin, TestCase):
 | 
				
			||||||
    @override_settings(
 | 
					    @override_settings(
 | 
				
			||||||
        ORIGINALS_DIR=os.path.join(os.path.dirname(__file__), "samples", "originals"),
 | 
					        ORIGINALS_DIR=os.path.join(os.path.dirname(__file__), "samples", "originals"),
 | 
				
			||||||
        THUMBNAIL_DIR=os.path.join(os.path.dirname(__file__), "samples", "thumb"),
 | 
					        THUMBNAIL_DIR=os.path.join(os.path.dirname(__file__), "samples", "thumb"),
 | 
				
			||||||
@ -161,10 +162,10 @@ class TestDecryptDocuments(TestCase):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        self.assertEqual(doc.storage_type, Document.STORAGE_TYPE_UNENCRYPTED)
 | 
					        self.assertEqual(doc.storage_type, Document.STORAGE_TYPE_UNENCRYPTED)
 | 
				
			||||||
        self.assertEqual(doc.filename, "0000004.pdf")
 | 
					        self.assertEqual(doc.filename, "0000004.pdf")
 | 
				
			||||||
        self.assertTrue(os.path.isfile(os.path.join(originals_dir, "0000004.pdf")))
 | 
					        self.assertIsFile(os.path.join(originals_dir, "0000004.pdf"))
 | 
				
			||||||
        self.assertTrue(os.path.isfile(doc.source_path))
 | 
					        self.assertIsFile(doc.source_path)
 | 
				
			||||||
        self.assertTrue(os.path.isfile(os.path.join(thumb_dir, f"{doc.id:07}.webp")))
 | 
					        self.assertIsFile(os.path.join(thumb_dir, f"{doc.id:07}.webp"))
 | 
				
			||||||
        self.assertTrue(os.path.isfile(doc.thumbnail_path))
 | 
					        self.assertIsFile(doc.thumbnail_path)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        with doc.source_file as f:
 | 
					        with doc.source_file as f:
 | 
				
			||||||
            checksum = hashlib.md5(f.read()).hexdigest()
 | 
					            checksum = hashlib.md5(f.read()).hexdigest()
 | 
				
			||||||
@ -183,7 +184,7 @@ class TestMakeIndex(TestCase):
 | 
				
			|||||||
        m.assert_called_once()
 | 
					        m.assert_called_once()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class TestRenamer(DirectoriesMixin, TestCase):
 | 
					class TestRenamer(DirectoriesMixin, FileSystemAssertsMixin, TestCase):
 | 
				
			||||||
    @override_settings(FILENAME_FORMAT="")
 | 
					    @override_settings(FILENAME_FORMAT="")
 | 
				
			||||||
    def test_rename(self):
 | 
					    def test_rename(self):
 | 
				
			||||||
        doc = Document.objects.create(title="test", mime_type="image/jpeg")
 | 
					        doc = Document.objects.create(title="test", mime_type="image/jpeg")
 | 
				
			||||||
@ -201,10 +202,10 @@ class TestRenamer(DirectoriesMixin, TestCase):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        self.assertEqual(doc2.filename, "none/test.jpg")
 | 
					        self.assertEqual(doc2.filename, "none/test.jpg")
 | 
				
			||||||
        self.assertEqual(doc2.archive_filename, "none/test.pdf")
 | 
					        self.assertEqual(doc2.archive_filename, "none/test.pdf")
 | 
				
			||||||
        self.assertFalse(os.path.isfile(doc.source_path))
 | 
					        self.assertIsNotFile(doc.source_path)
 | 
				
			||||||
        self.assertFalse(os.path.isfile(doc.archive_path))
 | 
					        self.assertIsNotFile(doc.archive_path)
 | 
				
			||||||
        self.assertTrue(os.path.isfile(doc2.source_path))
 | 
					        self.assertIsFile(doc2.source_path)
 | 
				
			||||||
        self.assertTrue(os.path.isfile(doc2.archive_path))
 | 
					        self.assertIsFile(doc2.archive_path)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class TestCreateClassifier(TestCase):
 | 
					class TestCreateClassifier(TestCase):
 | 
				
			||||||
 | 
				
			|||||||
@ -23,10 +23,11 @@ from documents.models import User
 | 
				
			|||||||
from documents.sanity_checker import check_sanity
 | 
					from documents.sanity_checker import check_sanity
 | 
				
			||||||
from documents.settings import EXPORTER_FILE_NAME
 | 
					from documents.settings import EXPORTER_FILE_NAME
 | 
				
			||||||
from documents.tests.utils import DirectoriesMixin
 | 
					from documents.tests.utils import DirectoriesMixin
 | 
				
			||||||
 | 
					from documents.tests.utils import FileSystemAssertsMixin
 | 
				
			||||||
from documents.tests.utils import paperless_environment
 | 
					from documents.tests.utils import paperless_environment
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class TestExportImport(DirectoriesMixin, TestCase):
 | 
					class TestExportImport(DirectoriesMixin, FileSystemAssertsMixin, TestCase):
 | 
				
			||||||
    def setUp(self) -> None:
 | 
					    def setUp(self) -> None:
 | 
				
			||||||
        self.target = tempfile.mkdtemp()
 | 
					        self.target = tempfile.mkdtemp()
 | 
				
			||||||
        self.addCleanup(shutil.rmtree, self.target)
 | 
					        self.addCleanup(shutil.rmtree, self.target)
 | 
				
			||||||
@ -145,7 +146,7 @@ class TestExportImport(DirectoriesMixin, TestCase):
 | 
				
			|||||||
            4,
 | 
					            4,
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.assertTrue(os.path.exists(os.path.join(self.target, "manifest.json")))
 | 
					        self.assertIsFile(os.path.join(self.target, "manifest.json"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.assertEqual(
 | 
					        self.assertEqual(
 | 
				
			||||||
            self._get_document_from_manifest(manifest, self.d1.id)["fields"]["title"],
 | 
					            self._get_document_from_manifest(manifest, self.d1.id)["fields"]["title"],
 | 
				
			||||||
@ -170,13 +171,11 @@ class TestExportImport(DirectoriesMixin, TestCase):
 | 
				
			|||||||
                    self.target,
 | 
					                    self.target,
 | 
				
			||||||
                    element[document_exporter.EXPORTER_FILE_NAME],
 | 
					                    element[document_exporter.EXPORTER_FILE_NAME],
 | 
				
			||||||
                )
 | 
					                )
 | 
				
			||||||
                self.assertTrue(os.path.exists(fname))
 | 
					                self.assertIsFile(fname)
 | 
				
			||||||
                self.assertTrue(
 | 
					                self.assertIsFile(
 | 
				
			||||||
                    os.path.exists(
 | 
					                    os.path.join(
 | 
				
			||||||
                        os.path.join(
 | 
					                        self.target,
 | 
				
			||||||
                            self.target,
 | 
					                        element[document_exporter.EXPORTER_THUMBNAIL_NAME],
 | 
				
			||||||
                            element[document_exporter.EXPORTER_THUMBNAIL_NAME],
 | 
					 | 
				
			||||||
                        ),
 | 
					 | 
				
			||||||
                    ),
 | 
					                    ),
 | 
				
			||||||
                )
 | 
					                )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -194,7 +193,7 @@ class TestExportImport(DirectoriesMixin, TestCase):
 | 
				
			|||||||
                        self.target,
 | 
					                        self.target,
 | 
				
			||||||
                        element[document_exporter.EXPORTER_ARCHIVE_NAME],
 | 
					                        element[document_exporter.EXPORTER_ARCHIVE_NAME],
 | 
				
			||||||
                    )
 | 
					                    )
 | 
				
			||||||
                    self.assertTrue(os.path.exists(fname))
 | 
					                    self.assertIsFile(fname)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    with open(fname, "rb") as f:
 | 
					                    with open(fname, "rb") as f:
 | 
				
			||||||
                        checksum = hashlib.md5(f.read()).hexdigest()
 | 
					                        checksum = hashlib.md5(f.read()).hexdigest()
 | 
				
			||||||
@ -247,7 +246,7 @@ class TestExportImport(DirectoriesMixin, TestCase):
 | 
				
			|||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self._do_export()
 | 
					        self._do_export()
 | 
				
			||||||
        self.assertTrue(os.path.exists(os.path.join(self.target, "manifest.json")))
 | 
					        self.assertIsFile(os.path.join(self.target, "manifest.json"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        st_mtime_1 = os.stat(os.path.join(self.target, "manifest.json")).st_mtime
 | 
					        st_mtime_1 = os.stat(os.path.join(self.target, "manifest.json")).st_mtime
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -257,7 +256,7 @@ class TestExportImport(DirectoriesMixin, TestCase):
 | 
				
			|||||||
            self._do_export()
 | 
					            self._do_export()
 | 
				
			||||||
            m.assert_not_called()
 | 
					            m.assert_not_called()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.assertTrue(os.path.exists(os.path.join(self.target, "manifest.json")))
 | 
					        self.assertIsFile(os.path.join(self.target, "manifest.json"))
 | 
				
			||||||
        st_mtime_2 = os.stat(os.path.join(self.target, "manifest.json")).st_mtime
 | 
					        st_mtime_2 = os.stat(os.path.join(self.target, "manifest.json")).st_mtime
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Path(self.d1.source_path).touch()
 | 
					        Path(self.d1.source_path).touch()
 | 
				
			||||||
@ -269,7 +268,7 @@ class TestExportImport(DirectoriesMixin, TestCase):
 | 
				
			|||||||
            self.assertEqual(m.call_count, 1)
 | 
					            self.assertEqual(m.call_count, 1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        st_mtime_3 = os.stat(os.path.join(self.target, "manifest.json")).st_mtime
 | 
					        st_mtime_3 = os.stat(os.path.join(self.target, "manifest.json")).st_mtime
 | 
				
			||||||
        self.assertTrue(os.path.exists(os.path.join(self.target, "manifest.json")))
 | 
					        self.assertIsFile(os.path.join(self.target, "manifest.json"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.assertNotEqual(st_mtime_1, st_mtime_2)
 | 
					        self.assertNotEqual(st_mtime_1, st_mtime_2)
 | 
				
			||||||
        self.assertNotEqual(st_mtime_2, st_mtime_3)
 | 
					        self.assertNotEqual(st_mtime_2, st_mtime_3)
 | 
				
			||||||
@ -283,7 +282,7 @@ class TestExportImport(DirectoriesMixin, TestCase):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        self._do_export()
 | 
					        self._do_export()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.assertTrue(os.path.exists(os.path.join(self.target, "manifest.json")))
 | 
					        self.assertIsFile(os.path.join(self.target, "manifest.json"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        with mock.patch(
 | 
					        with mock.patch(
 | 
				
			||||||
            "documents.management.commands.document_exporter.shutil.copy2",
 | 
					            "documents.management.commands.document_exporter.shutil.copy2",
 | 
				
			||||||
@ -291,7 +290,7 @@ class TestExportImport(DirectoriesMixin, TestCase):
 | 
				
			|||||||
            self._do_export()
 | 
					            self._do_export()
 | 
				
			||||||
            m.assert_not_called()
 | 
					            m.assert_not_called()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.assertTrue(os.path.exists(os.path.join(self.target, "manifest.json")))
 | 
					        self.assertIsFile(os.path.join(self.target, "manifest.json"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.d2.checksum = "asdfasdgf3"
 | 
					        self.d2.checksum = "asdfasdgf3"
 | 
				
			||||||
        self.d2.save()
 | 
					        self.d2.save()
 | 
				
			||||||
@ -302,7 +301,7 @@ class TestExportImport(DirectoriesMixin, TestCase):
 | 
				
			|||||||
            self._do_export(compare_checksums=True)
 | 
					            self._do_export(compare_checksums=True)
 | 
				
			||||||
            self.assertEqual(m.call_count, 1)
 | 
					            self.assertEqual(m.call_count, 1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.assertTrue(os.path.exists(os.path.join(self.target, "manifest.json")))
 | 
					        self.assertIsFile(os.path.join(self.target, "manifest.json"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_update_export_deleted_document(self):
 | 
					    def test_update_export_deleted_document(self):
 | 
				
			||||||
        shutil.rmtree(os.path.join(self.dirs.media_dir, "documents"))
 | 
					        shutil.rmtree(os.path.join(self.dirs.media_dir, "documents"))
 | 
				
			||||||
@ -315,10 +314,8 @@ class TestExportImport(DirectoriesMixin, TestCase):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        self.assertTrue(len(manifest), 7)
 | 
					        self.assertTrue(len(manifest), 7)
 | 
				
			||||||
        doc_from_manifest = self._get_document_from_manifest(manifest, self.d3.id)
 | 
					        doc_from_manifest = self._get_document_from_manifest(manifest, self.d3.id)
 | 
				
			||||||
        self.assertTrue(
 | 
					        self.assertIsFile(
 | 
				
			||||||
            os.path.isfile(
 | 
					            os.path.join(self.target, doc_from_manifest[EXPORTER_FILE_NAME]),
 | 
				
			||||||
                os.path.join(self.target, doc_from_manifest[EXPORTER_FILE_NAME]),
 | 
					 | 
				
			||||||
            ),
 | 
					 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        self.d3.delete()
 | 
					        self.d3.delete()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -329,17 +326,13 @@ class TestExportImport(DirectoriesMixin, TestCase):
 | 
				
			|||||||
            manifest,
 | 
					            manifest,
 | 
				
			||||||
            self.d3.id,
 | 
					            self.d3.id,
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        self.assertTrue(
 | 
					        self.assertIsFile(
 | 
				
			||||||
            os.path.isfile(
 | 
					            os.path.join(self.target, doc_from_manifest[EXPORTER_FILE_NAME]),
 | 
				
			||||||
                os.path.join(self.target, doc_from_manifest[EXPORTER_FILE_NAME]),
 | 
					 | 
				
			||||||
            ),
 | 
					 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        manifest = self._do_export(delete=True)
 | 
					        manifest = self._do_export(delete=True)
 | 
				
			||||||
        self.assertFalse(
 | 
					        self.assertIsNotFile(
 | 
				
			||||||
            os.path.isfile(
 | 
					            os.path.join(self.target, doc_from_manifest[EXPORTER_FILE_NAME]),
 | 
				
			||||||
                os.path.join(self.target, doc_from_manifest[EXPORTER_FILE_NAME]),
 | 
					 | 
				
			||||||
            ),
 | 
					 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.assertTrue(len(manifest), 6)
 | 
					        self.assertTrue(len(manifest), 6)
 | 
				
			||||||
@ -353,20 +346,20 @@ class TestExportImport(DirectoriesMixin, TestCase):
 | 
				
			|||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        m = self._do_export(use_filename_format=True)
 | 
					        m = self._do_export(use_filename_format=True)
 | 
				
			||||||
        self.assertTrue(os.path.isfile(os.path.join(self.target, "wow1", "c.pdf")))
 | 
					        self.assertIsFile(os.path.join(self.target, "wow1", "c.pdf"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.assertTrue(os.path.exists(os.path.join(self.target, "manifest.json")))
 | 
					        self.assertIsFile(os.path.join(self.target, "manifest.json"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.d1.title = "new_title"
 | 
					        self.d1.title = "new_title"
 | 
				
			||||||
        self.d1.save()
 | 
					        self.d1.save()
 | 
				
			||||||
        self._do_export(use_filename_format=True, delete=True)
 | 
					        self._do_export(use_filename_format=True, delete=True)
 | 
				
			||||||
        self.assertFalse(os.path.isfile(os.path.join(self.target, "wow1", "c.pdf")))
 | 
					        self.assertIsNotFile(os.path.join(self.target, "wow1", "c.pdf"))
 | 
				
			||||||
        self.assertFalse(os.path.isdir(os.path.join(self.target, "wow1")))
 | 
					        self.assertIsNotDir(os.path.join(self.target, "wow1"))
 | 
				
			||||||
        self.assertTrue(os.path.isfile(os.path.join(self.target, "new_title", "c.pdf")))
 | 
					        self.assertIsFile(os.path.join(self.target, "new_title", "c.pdf"))
 | 
				
			||||||
        self.assertTrue(os.path.exists(os.path.join(self.target, "manifest.json")))
 | 
					        self.assertIsFile(os.path.join(self.target, "manifest.json"))
 | 
				
			||||||
        self.assertTrue(os.path.isfile(os.path.join(self.target, "wow2", "none.pdf")))
 | 
					        self.assertIsFile(os.path.join(self.target, "wow2", "none.pdf"))
 | 
				
			||||||
        self.assertTrue(
 | 
					        self.assertIsFile(
 | 
				
			||||||
            os.path.isfile(os.path.join(self.target, "wow2", "none_01.pdf")),
 | 
					            (os.path.join(self.target, "wow2", "none_01.pdf")),
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_export_missing_files(self):
 | 
					    def test_export_missing_files(self):
 | 
				
			||||||
@ -407,7 +400,7 @@ class TestExportImport(DirectoriesMixin, TestCase):
 | 
				
			|||||||
            f"export-{timezone.localdate().isoformat()}.zip",
 | 
					            f"export-{timezone.localdate().isoformat()}.zip",
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.assertTrue(os.path.isfile(expected_file))
 | 
					        self.assertIsFile(expected_file)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        with ZipFile(expected_file) as zip:
 | 
					        with ZipFile(expected_file) as zip:
 | 
				
			||||||
            self.assertEqual(len(zip.namelist()), 11)
 | 
					            self.assertEqual(len(zip.namelist()), 11)
 | 
				
			||||||
@ -444,7 +437,7 @@ class TestExportImport(DirectoriesMixin, TestCase):
 | 
				
			|||||||
            f"export-{timezone.localdate().isoformat()}.zip",
 | 
					            f"export-{timezone.localdate().isoformat()}.zip",
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.assertTrue(os.path.isfile(expected_file))
 | 
					        self.assertIsFile(expected_file)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        with ZipFile(expected_file) as zip:
 | 
					        with ZipFile(expected_file) as zip:
 | 
				
			||||||
            # Extras are from the directories, which also appear in the listing
 | 
					            # Extras are from the directories, which also appear in the listing
 | 
				
			||||||
 | 
				
			|||||||
@ -7,9 +7,10 @@ from django.test import TestCase
 | 
				
			|||||||
from documents.management.commands.document_thumbnails import _process_document
 | 
					from documents.management.commands.document_thumbnails import _process_document
 | 
				
			||||||
from documents.models import Document
 | 
					from documents.models import Document
 | 
				
			||||||
from documents.tests.utils import DirectoriesMixin
 | 
					from documents.tests.utils import DirectoriesMixin
 | 
				
			||||||
 | 
					from documents.tests.utils import FileSystemAssertsMixin
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class TestMakeThumbnails(DirectoriesMixin, TestCase):
 | 
					class TestMakeThumbnails(DirectoriesMixin, FileSystemAssertsMixin, TestCase):
 | 
				
			||||||
    def make_models(self):
 | 
					    def make_models(self):
 | 
				
			||||||
        self.d1 = Document.objects.create(
 | 
					        self.d1 = Document.objects.create(
 | 
				
			||||||
            checksum="A",
 | 
					            checksum="A",
 | 
				
			||||||
@ -40,9 +41,9 @@ class TestMakeThumbnails(DirectoriesMixin, TestCase):
 | 
				
			|||||||
        self.make_models()
 | 
					        self.make_models()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_process_document(self):
 | 
					    def test_process_document(self):
 | 
				
			||||||
        self.assertFalse(os.path.isfile(self.d1.thumbnail_path))
 | 
					        self.assertIsNotFile(self.d1.thumbnail_path)
 | 
				
			||||||
        _process_document(self.d1.id)
 | 
					        _process_document(self.d1.id)
 | 
				
			||||||
        self.assertTrue(os.path.isfile(self.d1.thumbnail_path))
 | 
					        self.assertIsFile(self.d1.thumbnail_path)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @mock.patch("documents.management.commands.document_thumbnails.shutil.move")
 | 
					    @mock.patch("documents.management.commands.document_thumbnails.shutil.move")
 | 
				
			||||||
    def test_process_document_invalid_mime_type(self, m):
 | 
					    def test_process_document_invalid_mime_type(self, m):
 | 
				
			||||||
@ -54,15 +55,15 @@ class TestMakeThumbnails(DirectoriesMixin, TestCase):
 | 
				
			|||||||
        m.assert_not_called()
 | 
					        m.assert_not_called()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_command(self):
 | 
					    def test_command(self):
 | 
				
			||||||
        self.assertFalse(os.path.isfile(self.d1.thumbnail_path))
 | 
					        self.assertIsNotFile(self.d1.thumbnail_path)
 | 
				
			||||||
        self.assertFalse(os.path.isfile(self.d2.thumbnail_path))
 | 
					        self.assertIsNotFile(self.d2.thumbnail_path)
 | 
				
			||||||
        call_command("document_thumbnails")
 | 
					        call_command("document_thumbnails")
 | 
				
			||||||
        self.assertTrue(os.path.isfile(self.d1.thumbnail_path))
 | 
					        self.assertTrue(self.d1.thumbnail_path)
 | 
				
			||||||
        self.assertTrue(os.path.isfile(self.d2.thumbnail_path))
 | 
					        self.assertTrue(self.d2.thumbnail_path)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_command_documentid(self):
 | 
					    def test_command_documentid(self):
 | 
				
			||||||
        self.assertFalse(os.path.isfile(self.d1.thumbnail_path))
 | 
					        self.assertIsNotFile(self.d1.thumbnail_path)
 | 
				
			||||||
        self.assertFalse(os.path.isfile(self.d2.thumbnail_path))
 | 
					        self.assertIsNotFile(self.d2.thumbnail_path)
 | 
				
			||||||
        call_command("document_thumbnails", "-d", f"{self.d1.id}")
 | 
					        call_command("document_thumbnails", "-d", f"{self.d1.id}")
 | 
				
			||||||
        self.assertTrue(os.path.isfile(self.d1.thumbnail_path))
 | 
					        self.assertIsFile(self.d1.thumbnail_path)
 | 
				
			||||||
        self.assertFalse(os.path.isfile(self.d2.thumbnail_path))
 | 
					        self.assertIsNotFile(self.d2.thumbnail_path)
 | 
				
			||||||
 | 
				
			|||||||
@ -8,6 +8,7 @@ from django.conf import settings
 | 
				
			|||||||
from django.test import override_settings
 | 
					from django.test import override_settings
 | 
				
			||||||
from documents.parsers import ParseError
 | 
					from documents.parsers import ParseError
 | 
				
			||||||
from documents.tests.utils import DirectoriesMixin
 | 
					from documents.tests.utils import DirectoriesMixin
 | 
				
			||||||
 | 
					from documents.tests.utils import FileSystemAssertsMixin
 | 
				
			||||||
from documents.tests.utils import TestMigrations
 | 
					from documents.tests.utils import TestMigrations
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -112,7 +113,7 @@ simple_png2 = os.path.join(os.path.dirname(__file__), "examples", "no-text.png")
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@override_settings(FILENAME_FORMAT="")
 | 
					@override_settings(FILENAME_FORMAT="")
 | 
				
			||||||
class TestMigrateArchiveFiles(DirectoriesMixin, TestMigrations):
 | 
					class TestMigrateArchiveFiles(DirectoriesMixin, FileSystemAssertsMixin, TestMigrations):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    migrate_from = "1011_auto_20210101_2340"
 | 
					    migrate_from = "1011_auto_20210101_2340"
 | 
				
			||||||
    migrate_to = "1012_fix_archive_files"
 | 
					    migrate_to = "1012_fix_archive_files"
 | 
				
			||||||
@ -189,7 +190,7 @@ class TestMigrateArchiveFiles(DirectoriesMixin, TestMigrations):
 | 
				
			|||||||
        for doc in Document.objects.all():
 | 
					        for doc in Document.objects.all():
 | 
				
			||||||
            if doc.archive_checksum:
 | 
					            if doc.archive_checksum:
 | 
				
			||||||
                self.assertIsNotNone(doc.archive_filename)
 | 
					                self.assertIsNotNone(doc.archive_filename)
 | 
				
			||||||
                self.assertTrue(os.path.isfile(archive_path_new(doc)))
 | 
					                self.assertIsFile(archive_path_new(doc))
 | 
				
			||||||
            else:
 | 
					            else:
 | 
				
			||||||
                self.assertIsNone(doc.archive_filename)
 | 
					                self.assertIsNone(doc.archive_filename)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -198,7 +199,7 @@ class TestMigrateArchiveFiles(DirectoriesMixin, TestMigrations):
 | 
				
			|||||||
            self.assertEqual(original_checksum, doc.checksum)
 | 
					            self.assertEqual(original_checksum, doc.checksum)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if doc.archive_checksum:
 | 
					            if doc.archive_checksum:
 | 
				
			||||||
                self.assertTrue(os.path.isfile(archive_path_new(doc)))
 | 
					                self.assertIsFile(archive_path_new(doc))
 | 
				
			||||||
                with open(archive_path_new(doc), "rb") as f:
 | 
					                with open(archive_path_new(doc), "rb") as f:
 | 
				
			||||||
                    archive_checksum = hashlib.md5(f.read()).hexdigest()
 | 
					                    archive_checksum = hashlib.md5(f.read()).hexdigest()
 | 
				
			||||||
                self.assertEqual(archive_checksum, doc.archive_checksum)
 | 
					                self.assertEqual(archive_checksum, doc.archive_checksum)
 | 
				
			||||||
@ -448,7 +449,11 @@ class TestMigrateArchiveFilesErrors(DirectoriesMixin, TestMigrations):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@override_settings(FILENAME_FORMAT="")
 | 
					@override_settings(FILENAME_FORMAT="")
 | 
				
			||||||
class TestMigrateArchiveFilesBackwards(DirectoriesMixin, TestMigrations):
 | 
					class TestMigrateArchiveFilesBackwards(
 | 
				
			||||||
 | 
					    DirectoriesMixin,
 | 
				
			||||||
 | 
					    FileSystemAssertsMixin,
 | 
				
			||||||
 | 
					    TestMigrations,
 | 
				
			||||||
 | 
					):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    migrate_from = "1012_fix_archive_files"
 | 
					    migrate_from = "1012_fix_archive_files"
 | 
				
			||||||
    migrate_to = "1011_auto_20210101_2340"
 | 
					    migrate_to = "1011_auto_20210101_2340"
 | 
				
			||||||
@ -488,13 +493,13 @@ class TestMigrateArchiveFilesBackwards(DirectoriesMixin, TestMigrations):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        for doc in Document.objects.all():
 | 
					        for doc in Document.objects.all():
 | 
				
			||||||
            if doc.archive_checksum:
 | 
					            if doc.archive_checksum:
 | 
				
			||||||
                self.assertTrue(os.path.isfile(archive_path_old(doc)))
 | 
					                self.assertIsFile(archive_path_old(doc))
 | 
				
			||||||
            with open(source_path(doc), "rb") as f:
 | 
					            with open(source_path(doc), "rb") as f:
 | 
				
			||||||
                original_checksum = hashlib.md5(f.read()).hexdigest()
 | 
					                original_checksum = hashlib.md5(f.read()).hexdigest()
 | 
				
			||||||
            self.assertEqual(original_checksum, doc.checksum)
 | 
					            self.assertEqual(original_checksum, doc.checksum)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if doc.archive_checksum:
 | 
					            if doc.archive_checksum:
 | 
				
			||||||
                self.assertTrue(os.path.isfile(archive_path_old(doc)))
 | 
					                self.assertIsFile(archive_path_old(doc))
 | 
				
			||||||
                with open(archive_path_old(doc), "rb") as f:
 | 
					                with open(archive_path_old(doc), "rb") as f:
 | 
				
			||||||
                    archive_checksum = hashlib.md5(f.read()).hexdigest()
 | 
					                    archive_checksum = hashlib.md5(f.read()).hexdigest()
 | 
				
			||||||
                self.assertEqual(archive_checksum, doc.archive_checksum)
 | 
					                self.assertEqual(archive_checksum, doc.archive_checksum)
 | 
				
			||||||
 | 
				
			|||||||
@ -13,6 +13,7 @@ from documents.sanity_checker import SanityCheckFailedException
 | 
				
			|||||||
from documents.sanity_checker import SanityCheckMessages
 | 
					from documents.sanity_checker import SanityCheckMessages
 | 
				
			||||||
from documents.tests.test_classifier import dummy_preprocess
 | 
					from documents.tests.test_classifier import dummy_preprocess
 | 
				
			||||||
from documents.tests.utils import DirectoriesMixin
 | 
					from documents.tests.utils import DirectoriesMixin
 | 
				
			||||||
 | 
					from documents.tests.utils import FileSystemAssertsMixin
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class TestIndexReindex(DirectoriesMixin, TestCase):
 | 
					class TestIndexReindex(DirectoriesMixin, TestCase):
 | 
				
			||||||
@ -41,7 +42,7 @@ class TestIndexReindex(DirectoriesMixin, TestCase):
 | 
				
			|||||||
        tasks.index_optimize()
 | 
					        tasks.index_optimize()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class TestClassifier(DirectoriesMixin, TestCase):
 | 
					class TestClassifier(DirectoriesMixin, FileSystemAssertsMixin, TestCase):
 | 
				
			||||||
    @mock.patch("documents.tasks.load_classifier")
 | 
					    @mock.patch("documents.tasks.load_classifier")
 | 
				
			||||||
    def test_train_classifier_no_auto_matching(self, load_classifier):
 | 
					    def test_train_classifier_no_auto_matching(self, load_classifier):
 | 
				
			||||||
        tasks.train_classifier()
 | 
					        tasks.train_classifier()
 | 
				
			||||||
@ -53,7 +54,7 @@ class TestClassifier(DirectoriesMixin, TestCase):
 | 
				
			|||||||
        Tag.objects.create(matching_algorithm=Tag.MATCH_AUTO, name="test")
 | 
					        Tag.objects.create(matching_algorithm=Tag.MATCH_AUTO, name="test")
 | 
				
			||||||
        tasks.train_classifier()
 | 
					        tasks.train_classifier()
 | 
				
			||||||
        load_classifier.assert_called_once()
 | 
					        load_classifier.assert_called_once()
 | 
				
			||||||
        self.assertFalse(os.path.isfile(settings.MODEL_FILE))
 | 
					        self.assertIsNotFile(settings.MODEL_FILE)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @mock.patch("documents.tasks.load_classifier")
 | 
					    @mock.patch("documents.tasks.load_classifier")
 | 
				
			||||||
    def test_train_classifier_with_auto_type(self, load_classifier):
 | 
					    def test_train_classifier_with_auto_type(self, load_classifier):
 | 
				
			||||||
@ -61,7 +62,7 @@ class TestClassifier(DirectoriesMixin, TestCase):
 | 
				
			|||||||
        DocumentType.objects.create(matching_algorithm=Tag.MATCH_AUTO, name="test")
 | 
					        DocumentType.objects.create(matching_algorithm=Tag.MATCH_AUTO, name="test")
 | 
				
			||||||
        tasks.train_classifier()
 | 
					        tasks.train_classifier()
 | 
				
			||||||
        load_classifier.assert_called_once()
 | 
					        load_classifier.assert_called_once()
 | 
				
			||||||
        self.assertFalse(os.path.isfile(settings.MODEL_FILE))
 | 
					        self.assertIsNotFile(settings.MODEL_FILE)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @mock.patch("documents.tasks.load_classifier")
 | 
					    @mock.patch("documents.tasks.load_classifier")
 | 
				
			||||||
    def test_train_classifier_with_auto_correspondent(self, load_classifier):
 | 
					    def test_train_classifier_with_auto_correspondent(self, load_classifier):
 | 
				
			||||||
@ -69,12 +70,12 @@ class TestClassifier(DirectoriesMixin, TestCase):
 | 
				
			|||||||
        Correspondent.objects.create(matching_algorithm=Tag.MATCH_AUTO, name="test")
 | 
					        Correspondent.objects.create(matching_algorithm=Tag.MATCH_AUTO, name="test")
 | 
				
			||||||
        tasks.train_classifier()
 | 
					        tasks.train_classifier()
 | 
				
			||||||
        load_classifier.assert_called_once()
 | 
					        load_classifier.assert_called_once()
 | 
				
			||||||
        self.assertFalse(os.path.isfile(settings.MODEL_FILE))
 | 
					        self.assertIsNotFile(settings.MODEL_FILE)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_train_classifier(self):
 | 
					    def test_train_classifier(self):
 | 
				
			||||||
        c = Correspondent.objects.create(matching_algorithm=Tag.MATCH_AUTO, name="test")
 | 
					        c = Correspondent.objects.create(matching_algorithm=Tag.MATCH_AUTO, name="test")
 | 
				
			||||||
        doc = Document.objects.create(correspondent=c, content="test", title="test")
 | 
					        doc = Document.objects.create(correspondent=c, content="test", title="test")
 | 
				
			||||||
        self.assertFalse(os.path.isfile(settings.MODEL_FILE))
 | 
					        self.assertIsNotFile(settings.MODEL_FILE)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        with mock.patch(
 | 
					        with mock.patch(
 | 
				
			||||||
            "documents.classifier.DocumentClassifier.preprocess_content",
 | 
					            "documents.classifier.DocumentClassifier.preprocess_content",
 | 
				
			||||||
@ -82,18 +83,18 @@ class TestClassifier(DirectoriesMixin, TestCase):
 | 
				
			|||||||
            pre_proc_mock.side_effect = dummy_preprocess
 | 
					            pre_proc_mock.side_effect = dummy_preprocess
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            tasks.train_classifier()
 | 
					            tasks.train_classifier()
 | 
				
			||||||
            self.assertTrue(os.path.isfile(settings.MODEL_FILE))
 | 
					            self.assertIsFile(settings.MODEL_FILE)
 | 
				
			||||||
            mtime = os.stat(settings.MODEL_FILE).st_mtime
 | 
					            mtime = os.stat(settings.MODEL_FILE).st_mtime
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            tasks.train_classifier()
 | 
					            tasks.train_classifier()
 | 
				
			||||||
            self.assertTrue(os.path.isfile(settings.MODEL_FILE))
 | 
					            self.assertIsFile(settings.MODEL_FILE)
 | 
				
			||||||
            mtime2 = os.stat(settings.MODEL_FILE).st_mtime
 | 
					            mtime2 = os.stat(settings.MODEL_FILE).st_mtime
 | 
				
			||||||
            self.assertEqual(mtime, mtime2)
 | 
					            self.assertEqual(mtime, mtime2)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            doc.content = "test2"
 | 
					            doc.content = "test2"
 | 
				
			||||||
            doc.save()
 | 
					            doc.save()
 | 
				
			||||||
            tasks.train_classifier()
 | 
					            tasks.train_classifier()
 | 
				
			||||||
            self.assertTrue(os.path.isfile(settings.MODEL_FILE))
 | 
					            self.assertIsFile(settings.MODEL_FILE)
 | 
				
			||||||
            mtime3 = os.stat(settings.MODEL_FILE).st_mtime
 | 
					            mtime3 = os.stat(settings.MODEL_FILE).st_mtime
 | 
				
			||||||
            self.assertNotEqual(mtime2, mtime3)
 | 
					            self.assertNotEqual(mtime2, mtime3)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -5,6 +5,7 @@ from django.conf import settings
 | 
				
			|||||||
from django.contrib.auth.models import User
 | 
					from django.contrib.auth.models import User
 | 
				
			||||||
from django.test import override_settings
 | 
					from django.test import override_settings
 | 
				
			||||||
from django.test import TestCase
 | 
					from django.test import TestCase
 | 
				
			||||||
 | 
					from rest_framework import status
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class TestViews(TestCase):
 | 
					class TestViews(TestCase):
 | 
				
			||||||
@ -28,7 +29,7 @@ class TestViews(TestCase):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    def test_login_redirect(self):
 | 
					    def test_login_redirect(self):
 | 
				
			||||||
        response = self.client.get("/")
 | 
					        response = self.client.get("/")
 | 
				
			||||||
        self.assertEqual(response.status_code, 302)
 | 
					        self.assertEqual(response.status_code, status.HTTP_302_FOUND)
 | 
				
			||||||
        self.assertEqual(response.url, "/accounts/login/?next=/")
 | 
					        self.assertEqual(response.url, "/accounts/login/?next=/")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_index(self):
 | 
					    def test_index(self):
 | 
				
			||||||
@ -52,7 +53,7 @@ class TestViews(TestCase):
 | 
				
			|||||||
            response = self.client.get(
 | 
					            response = self.client.get(
 | 
				
			||||||
                "/",
 | 
					                "/",
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
            self.assertEqual(response.status_code, 200)
 | 
					            self.assertEqual(response.status_code, status.HTTP_200_OK)
 | 
				
			||||||
            self.assertEqual(
 | 
					            self.assertEqual(
 | 
				
			||||||
                response.context_data["webmanifest"],
 | 
					                response.context_data["webmanifest"],
 | 
				
			||||||
                f"frontend/{language_actual}/manifest.webmanifest",
 | 
					                f"frontend/{language_actual}/manifest.webmanifest",
 | 
				
			||||||
 | 
				
			|||||||
@ -3,6 +3,9 @@ import shutil
 | 
				
			|||||||
import tempfile
 | 
					import tempfile
 | 
				
			||||||
from collections import namedtuple
 | 
					from collections import namedtuple
 | 
				
			||||||
from contextlib import contextmanager
 | 
					from contextlib import contextmanager
 | 
				
			||||||
 | 
					from os import PathLike
 | 
				
			||||||
 | 
					from pathlib import Path
 | 
				
			||||||
 | 
					from typing import Union
 | 
				
			||||||
from unittest import mock
 | 
					from unittest import mock
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from django.apps import apps
 | 
					from django.apps import apps
 | 
				
			||||||
@ -87,6 +90,20 @@ class DirectoriesMixin:
 | 
				
			|||||||
        remove_dirs(self.dirs)
 | 
					        remove_dirs(self.dirs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class FileSystemAssertsMixin:
 | 
				
			||||||
 | 
					    def assertIsFile(self, path: Union[PathLike, str]):
 | 
				
			||||||
 | 
					        self.assertTrue(Path(path).resolve().is_file(), f"File does not exist: {path}")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def assertIsNotFile(self, path: Union[PathLike, str]):
 | 
				
			||||||
 | 
					        self.assertFalse(Path(path).resolve().is_file(), f"File does exist: {path}")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def assertIsDir(self, path: Union[PathLike, str]):
 | 
				
			||||||
 | 
					        self.assertTrue(Path(path).resolve().is_dir(), f"Dir does not exist: {path}")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def assertIsNotDir(self, path: Union[PathLike, str]):
 | 
				
			||||||
 | 
					        self.assertFalse(Path(path).resolve().is_dir(), f"Dir does exist: {path}")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ConsumerProgressMixin:
 | 
					class ConsumerProgressMixin:
 | 
				
			||||||
    def setUp(self) -> None:
 | 
					    def setUp(self) -> None:
 | 
				
			||||||
        self.send_progress_patcher = mock.patch(
 | 
					        self.send_progress_patcher = mock.patch(
 | 
				
			||||||
 | 
				
			|||||||
@ -667,6 +667,7 @@ class PostDocumentView(GenericAPIView):
 | 
				
			|||||||
        title = serializer.validated_data.get("title")
 | 
					        title = serializer.validated_data.get("title")
 | 
				
			||||||
        created = serializer.validated_data.get("created")
 | 
					        created = serializer.validated_data.get("created")
 | 
				
			||||||
        owner_id = serializer.validated_data.get("owner")
 | 
					        owner_id = serializer.validated_data.get("owner")
 | 
				
			||||||
 | 
					        archive_serial_number = serializer.validated_data.get("archive_serial_number")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        t = int(mktime(datetime.now().timetuple()))
 | 
					        t = int(mktime(datetime.now().timetuple()))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -692,6 +693,7 @@ class PostDocumentView(GenericAPIView):
 | 
				
			|||||||
            task_id=task_id,
 | 
					            task_id=task_id,
 | 
				
			||||||
            override_created=created,
 | 
					            override_created=created,
 | 
				
			||||||
            override_owner_id=owner_id,
 | 
					            override_owner_id=owner_id,
 | 
				
			||||||
 | 
					            override_archive_serial_num=archive_serial_number,
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return Response(async_task.id)
 | 
					        return Response(async_task.id)
 | 
				
			||||||
 | 
				
			|||||||
@ -5,6 +5,7 @@ from documents.models import Tag
 | 
				
			|||||||
from documents.tests.utils import DirectoriesMixin
 | 
					from documents.tests.utils import DirectoriesMixin
 | 
				
			||||||
from paperless_mail.models import MailAccount
 | 
					from paperless_mail.models import MailAccount
 | 
				
			||||||
from paperless_mail.models import MailRule
 | 
					from paperless_mail.models import MailRule
 | 
				
			||||||
 | 
					from rest_framework import status
 | 
				
			||||||
from rest_framework.test import APITestCase
 | 
					from rest_framework.test import APITestCase
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -39,7 +40,7 @@ class TestAPIMailAccounts(DirectoriesMixin, APITestCase):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        response = self.client.get(self.ENDPOINT)
 | 
					        response = self.client.get(self.ENDPOINT)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.assertEqual(response.status_code, 200)
 | 
					        self.assertEqual(response.status_code, status.HTTP_200_OK)
 | 
				
			||||||
        self.assertEqual(response.data["count"], 1)
 | 
					        self.assertEqual(response.data["count"], 1)
 | 
				
			||||||
        returned_account1 = response.data["results"][0]
 | 
					        returned_account1 = response.data["results"][0]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -77,7 +78,7 @@ class TestAPIMailAccounts(DirectoriesMixin, APITestCase):
 | 
				
			|||||||
            data=account1,
 | 
					            data=account1,
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.assertEqual(response.status_code, 201)
 | 
					        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        returned_account1 = MailAccount.objects.get(name="Email1")
 | 
					        returned_account1 = MailAccount.objects.get(name="Email1")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -113,7 +114,7 @@ class TestAPIMailAccounts(DirectoriesMixin, APITestCase):
 | 
				
			|||||||
            f"{self.ENDPOINT}{account1.pk}/",
 | 
					            f"{self.ENDPOINT}{account1.pk}/",
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.assertEqual(response.status_code, 204)
 | 
					        self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.assertEqual(len(MailAccount.objects.all()), 0)
 | 
					        self.assertEqual(len(MailAccount.objects.all()), 0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -145,7 +146,7 @@ class TestAPIMailAccounts(DirectoriesMixin, APITestCase):
 | 
				
			|||||||
            },
 | 
					            },
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.assertEqual(response.status_code, 200)
 | 
					        self.assertEqual(response.status_code, status.HTTP_200_OK)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        returned_account1 = MailAccount.objects.get(pk=account1.pk)
 | 
					        returned_account1 = MailAccount.objects.get(pk=account1.pk)
 | 
				
			||||||
        self.assertEqual(returned_account1.name, "Updated Name 1")
 | 
					        self.assertEqual(returned_account1.name, "Updated Name 1")
 | 
				
			||||||
@ -159,7 +160,7 @@ class TestAPIMailAccounts(DirectoriesMixin, APITestCase):
 | 
				
			|||||||
            },
 | 
					            },
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.assertEqual(response.status_code, 200)
 | 
					        self.assertEqual(response.status_code, status.HTTP_200_OK)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        returned_account2 = MailAccount.objects.get(pk=account1.pk)
 | 
					        returned_account2 = MailAccount.objects.get(pk=account1.pk)
 | 
				
			||||||
        self.assertEqual(returned_account2.name, "Updated Name 2")
 | 
					        self.assertEqual(returned_account2.name, "Updated Name 2")
 | 
				
			||||||
@ -213,7 +214,7 @@ class TestAPIMailRules(DirectoriesMixin, APITestCase):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        response = self.client.get(self.ENDPOINT)
 | 
					        response = self.client.get(self.ENDPOINT)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.assertEqual(response.status_code, 200)
 | 
					        self.assertEqual(response.status_code, status.HTTP_200_OK)
 | 
				
			||||||
        self.assertEqual(response.data["count"], 1)
 | 
					        self.assertEqual(response.data["count"], 1)
 | 
				
			||||||
        returned_rule1 = response.data["results"][0]
 | 
					        returned_rule1 = response.data["results"][0]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -294,11 +295,11 @@ class TestAPIMailRules(DirectoriesMixin, APITestCase):
 | 
				
			|||||||
            data=rule1,
 | 
					            data=rule1,
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.assertEqual(response.status_code, 201)
 | 
					        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        response = self.client.get(self.ENDPOINT)
 | 
					        response = self.client.get(self.ENDPOINT)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.assertEqual(response.status_code, 200)
 | 
					        self.assertEqual(response.status_code, status.HTTP_200_OK)
 | 
				
			||||||
        self.assertEqual(response.data["count"], 1)
 | 
					        self.assertEqual(response.data["count"], 1)
 | 
				
			||||||
        returned_rule1 = response.data["results"][0]
 | 
					        returned_rule1 = response.data["results"][0]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -375,7 +376,7 @@ class TestAPIMailRules(DirectoriesMixin, APITestCase):
 | 
				
			|||||||
            f"{self.ENDPOINT}{rule1.pk}/",
 | 
					            f"{self.ENDPOINT}{rule1.pk}/",
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.assertEqual(response.status_code, 204)
 | 
					        self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.assertEqual(len(MailRule.objects.all()), 0)
 | 
					        self.assertEqual(len(MailRule.objects.all()), 0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -423,7 +424,7 @@ class TestAPIMailRules(DirectoriesMixin, APITestCase):
 | 
				
			|||||||
            },
 | 
					            },
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.assertEqual(response.status_code, 200)
 | 
					        self.assertEqual(response.status_code, status.HTTP_200_OK)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        returned_rule1 = MailRule.objects.get(pk=rule1.pk)
 | 
					        returned_rule1 = MailRule.objects.get(pk=rule1.pk)
 | 
				
			||||||
        self.assertEqual(returned_rule1.name, "Updated Name 1")
 | 
					        self.assertEqual(returned_rule1.name, "Updated Name 1")
 | 
				
			||||||
 | 
				
			|||||||
@ -14,6 +14,7 @@ from django.db import DatabaseError
 | 
				
			|||||||
from django.test import TestCase
 | 
					from django.test import TestCase
 | 
				
			||||||
from documents.models import Correspondent
 | 
					from documents.models import Correspondent
 | 
				
			||||||
from documents.tests.utils import DirectoriesMixin
 | 
					from documents.tests.utils import DirectoriesMixin
 | 
				
			||||||
 | 
					from documents.tests.utils import FileSystemAssertsMixin
 | 
				
			||||||
from imap_tools import EmailAddress
 | 
					from imap_tools import EmailAddress
 | 
				
			||||||
from imap_tools import FolderInfo
 | 
					from imap_tools import FolderInfo
 | 
				
			||||||
from imap_tools import MailboxFolderSelectError
 | 
					from imap_tools import MailboxFolderSelectError
 | 
				
			||||||
@ -241,7 +242,7 @@ def fake_magic_from_buffer(buffer, mime=False):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@mock.patch("paperless_mail.mail.magic.from_buffer", fake_magic_from_buffer)
 | 
					@mock.patch("paperless_mail.mail.magic.from_buffer", fake_magic_from_buffer)
 | 
				
			||||||
class TestMail(DirectoriesMixin, TestCase):
 | 
					class TestMail(DirectoriesMixin, FileSystemAssertsMixin, TestCase):
 | 
				
			||||||
    def setUp(self):
 | 
					    def setUp(self):
 | 
				
			||||||
        patcher = mock.patch("paperless_mail.mail.MailBox")
 | 
					        patcher = mock.patch("paperless_mail.mail.MailBox")
 | 
				
			||||||
        m = patcher.start()
 | 
					        m = patcher.start()
 | 
				
			||||||
@ -389,12 +390,12 @@ class TestMail(DirectoriesMixin, TestCase):
 | 
				
			|||||||
        args1, kwargs1 = self.async_task.call_args_list[0]
 | 
					        args1, kwargs1 = self.async_task.call_args_list[0]
 | 
				
			||||||
        args2, kwargs2 = self.async_task.call_args_list[1]
 | 
					        args2, kwargs2 = self.async_task.call_args_list[1]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.assertTrue(os.path.isfile(kwargs1["path"]), kwargs1["path"])
 | 
					        self.assertIsFile(kwargs1["path"])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.assertEqual(kwargs1["override_title"], "file_0")
 | 
					        self.assertEqual(kwargs1["override_title"], "file_0")
 | 
				
			||||||
        self.assertEqual(kwargs1["override_filename"], "file_0.pdf")
 | 
					        self.assertEqual(kwargs1["override_filename"], "file_0.pdf")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.assertTrue(os.path.isfile(kwargs2["path"]), kwargs1["path"])
 | 
					        self.assertIsFile(kwargs2["path"])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.assertEqual(kwargs2["override_title"], "file_1")
 | 
					        self.assertEqual(kwargs2["override_title"], "file_1")
 | 
				
			||||||
        self.assertEqual(kwargs2["override_filename"], "file_1.pdf")
 | 
					        self.assertEqual(kwargs2["override_filename"], "file_1.pdf")
 | 
				
			||||||
@ -435,7 +436,7 @@ class TestMail(DirectoriesMixin, TestCase):
 | 
				
			|||||||
        self.assertEqual(self.async_task.call_count, 1)
 | 
					        self.assertEqual(self.async_task.call_count, 1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        args, kwargs = self.async_task.call_args
 | 
					        args, kwargs = self.async_task.call_args
 | 
				
			||||||
        self.assertTrue(os.path.isfile(kwargs["path"]), kwargs["path"])
 | 
					        self.assertIsFile(kwargs["path"])
 | 
				
			||||||
        self.assertEqual(kwargs["override_filename"], "f1.pdf")
 | 
					        self.assertEqual(kwargs["override_filename"], "f1.pdf")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_handle_disposition(self):
 | 
					    def test_handle_disposition(self):
 | 
				
			||||||
 | 
				
			|||||||
@ -4,10 +4,11 @@ from unittest import mock
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
from django.test import TestCase
 | 
					from django.test import TestCase
 | 
				
			||||||
from documents.parsers import ParseError
 | 
					from documents.parsers import ParseError
 | 
				
			||||||
 | 
					from documents.tests.utils import FileSystemAssertsMixin
 | 
				
			||||||
from paperless_mail.parsers import MailDocumentParser
 | 
					from paperless_mail.parsers import MailDocumentParser
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class TestParser(TestCase):
 | 
					class TestParser(FileSystemAssertsMixin, TestCase):
 | 
				
			||||||
    SAMPLE_FILES = os.path.join(os.path.dirname(__file__), "samples")
 | 
					    SAMPLE_FILES = os.path.join(os.path.dirname(__file__), "samples")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def setUp(self) -> None:
 | 
					    def setUp(self) -> None:
 | 
				
			||||||
@ -331,7 +332,7 @@ class TestParser(TestCase):
 | 
				
			|||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @mock.patch("paperless_mail.parsers.MailDocumentParser.generate_pdf")
 | 
					    @mock.patch("paperless_mail.parsers.MailDocumentParser.generate_pdf")
 | 
				
			||||||
    def test_parse_simple_eml(self, n):
 | 
					    def test_parse_simple_eml(self, m: mock.MagicMock):
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        GIVEN:
 | 
					        GIVEN:
 | 
				
			||||||
            - Fresh start
 | 
					            - Fresh start
 | 
				
			||||||
@ -361,8 +362,8 @@ class TestParser(TestCase):
 | 
				
			|||||||
            self.parser.date,
 | 
					            self.parser.date,
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # Just check if file exists, the unittest for generate_pdf() goes deeper.
 | 
					        # Just check if tried to generate archive, the unittest for generate_pdf() goes deeper.
 | 
				
			||||||
        self.assertTrue(os.path.isfile(self.parser.archive_path))
 | 
					        m.assert_called()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @mock.patch("paperless_mail.parsers.parser.from_buffer")
 | 
					    @mock.patch("paperless_mail.parsers.parser.from_buffer")
 | 
				
			||||||
    def test_tika_parse_unsuccessful(self, mock_from_buffer: mock.MagicMock):
 | 
					    def test_tika_parse_unsuccessful(self, mock_from_buffer: mock.MagicMock):
 | 
				
			||||||
@ -494,7 +495,7 @@ class TestParser(TestCase):
 | 
				
			|||||||
        mock_response.content = b"Content"
 | 
					        mock_response.content = b"Content"
 | 
				
			||||||
        mock_post.return_value = mock_response
 | 
					        mock_post.return_value = mock_response
 | 
				
			||||||
        pdf_path = self.parser.generate_pdf(os.path.join(self.SAMPLE_FILES, "html.eml"))
 | 
					        pdf_path = self.parser.generate_pdf(os.path.join(self.SAMPLE_FILES, "html.eml"))
 | 
				
			||||||
        self.assertTrue(os.path.isfile(pdf_path))
 | 
					        self.assertIsFile(pdf_path)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        mock_generate_pdf_from_mail.assert_called_once_with(
 | 
					        mock_generate_pdf_from_mail.assert_called_once_with(
 | 
				
			||||||
            self.parser.get_parsed(None),
 | 
					            self.parser.get_parsed(None),
 | 
				
			||||||
 | 
				
			|||||||
@ -7,13 +7,14 @@ from urllib.request import urlopen
 | 
				
			|||||||
import pytest
 | 
					import pytest
 | 
				
			||||||
from django.test import TestCase
 | 
					from django.test import TestCase
 | 
				
			||||||
from documents.parsers import run_convert
 | 
					from documents.parsers import run_convert
 | 
				
			||||||
 | 
					from documents.tests.utils import FileSystemAssertsMixin
 | 
				
			||||||
from imagehash import average_hash
 | 
					from imagehash import average_hash
 | 
				
			||||||
from paperless_mail.parsers import MailDocumentParser
 | 
					from paperless_mail.parsers import MailDocumentParser
 | 
				
			||||||
from pdfminer.high_level import extract_text
 | 
					from pdfminer.high_level import extract_text
 | 
				
			||||||
from PIL import Image
 | 
					from PIL import Image
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class TestParserLive(TestCase):
 | 
					class TestParserLive(FileSystemAssertsMixin, TestCase):
 | 
				
			||||||
    SAMPLE_FILES = os.path.join(os.path.dirname(__file__), "samples")
 | 
					    SAMPLE_FILES = os.path.join(os.path.dirname(__file__), "samples")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def setUp(self) -> None:
 | 
					    def setUp(self) -> None:
 | 
				
			||||||
@ -85,7 +86,7 @@ class TestParserLive(TestCase):
 | 
				
			|||||||
            os.path.join(self.SAMPLE_FILES, "simple_text.eml"),
 | 
					            os.path.join(self.SAMPLE_FILES, "simple_text.eml"),
 | 
				
			||||||
            "message/rfc822",
 | 
					            "message/rfc822",
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        self.assertTrue(os.path.isfile(thumb))
 | 
					        self.assertIsFile(thumb)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        expected = os.path.join(self.SAMPLE_FILES, "simple_text.eml.pdf.webp")
 | 
					        expected = os.path.join(self.SAMPLE_FILES, "simple_text.eml.pdf.webp")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -161,7 +162,7 @@ class TestParserLive(TestCase):
 | 
				
			|||||||
            self.parser.generate_pdf,
 | 
					            self.parser.generate_pdf,
 | 
				
			||||||
            [os.path.join(self.SAMPLE_FILES, "html.eml")],
 | 
					            [os.path.join(self.SAMPLE_FILES, "html.eml")],
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        self.assertTrue(os.path.isfile(pdf_path))
 | 
					        self.assertIsFile(pdf_path)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        extracted = extract_text(pdf_path)
 | 
					        extracted = extract_text(pdf_path)
 | 
				
			||||||
        expected = (
 | 
					        expected = (
 | 
				
			||||||
@ -232,7 +233,7 @@ class TestParserLive(TestCase):
 | 
				
			|||||||
            output_file=converted,
 | 
					            output_file=converted,
 | 
				
			||||||
            logging_group=None,
 | 
					            logging_group=None,
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        self.assertTrue(os.path.isfile(converted))
 | 
					        self.assertIsFile(converted)
 | 
				
			||||||
        thumb_hash = self.imagehash(converted)
 | 
					        thumb_hash = self.imagehash(converted)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # The created pdf is not reproducible. But the converted image should always look the same.
 | 
					        # The created pdf is not reproducible. But the converted image should always look the same.
 | 
				
			||||||
@ -337,7 +338,7 @@ class TestParserLive(TestCase):
 | 
				
			|||||||
            output_file=converted,
 | 
					            output_file=converted,
 | 
				
			||||||
            logging_group=None,
 | 
					            logging_group=None,
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        self.assertTrue(os.path.isfile(converted))
 | 
					        self.assertIsFile(converted)
 | 
				
			||||||
        thumb_hash = self.imagehash(converted)
 | 
					        thumb_hash = self.imagehash(converted)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # The created pdf is not reproducible. But the converted image should always look the same.
 | 
					        # The created pdf is not reproducible. But the converted image should always look the same.
 | 
				
			||||||
 | 
				
			|||||||
@ -10,6 +10,7 @@ from django.test import TestCase
 | 
				
			|||||||
from documents.parsers import ParseError
 | 
					from documents.parsers import ParseError
 | 
				
			||||||
from documents.parsers import run_convert
 | 
					from documents.parsers import run_convert
 | 
				
			||||||
from documents.tests.utils import DirectoriesMixin
 | 
					from documents.tests.utils import DirectoriesMixin
 | 
				
			||||||
 | 
					from documents.tests.utils import FileSystemAssertsMixin
 | 
				
			||||||
from paperless_tesseract.parsers import post_process_text
 | 
					from paperless_tesseract.parsers import post_process_text
 | 
				
			||||||
from paperless_tesseract.parsers import RasterisedDocumentParser
 | 
					from paperless_tesseract.parsers import RasterisedDocumentParser
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -36,7 +37,7 @@ class FakeImageFile(ContextManager):
 | 
				
			|||||||
        return os.path.basename(self.fname)
 | 
					        return os.path.basename(self.fname)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class TestParser(DirectoriesMixin, TestCase):
 | 
					class TestParser(DirectoriesMixin, FileSystemAssertsMixin, TestCase):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    SAMPLE_FILES = os.path.join(os.path.dirname(__file__), "samples")
 | 
					    SAMPLE_FILES = os.path.join(os.path.dirname(__file__), "samples")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -88,7 +89,7 @@ class TestParser(DirectoriesMixin, TestCase):
 | 
				
			|||||||
            os.path.join(self.SAMPLE_FILES, "simple-digital.pdf"),
 | 
					            os.path.join(self.SAMPLE_FILES, "simple-digital.pdf"),
 | 
				
			||||||
            "application/pdf",
 | 
					            "application/pdf",
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        self.assertTrue(os.path.isfile(thumb))
 | 
					        self.assertIsFile(thumb)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @mock.patch("documents.parsers.run_convert")
 | 
					    @mock.patch("documents.parsers.run_convert")
 | 
				
			||||||
    def test_thumbnail_fallback(self, m):
 | 
					    def test_thumbnail_fallback(self, m):
 | 
				
			||||||
@ -105,7 +106,7 @@ class TestParser(DirectoriesMixin, TestCase):
 | 
				
			|||||||
            os.path.join(self.SAMPLE_FILES, "simple-digital.pdf"),
 | 
					            os.path.join(self.SAMPLE_FILES, "simple-digital.pdf"),
 | 
				
			||||||
            "application/pdf",
 | 
					            "application/pdf",
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        self.assertTrue(os.path.isfile(thumb))
 | 
					        self.assertIsFile(thumb)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_thumbnail_encrypted(self):
 | 
					    def test_thumbnail_encrypted(self):
 | 
				
			||||||
        parser = RasterisedDocumentParser(uuid.uuid4())
 | 
					        parser = RasterisedDocumentParser(uuid.uuid4())
 | 
				
			||||||
@ -113,7 +114,7 @@ class TestParser(DirectoriesMixin, TestCase):
 | 
				
			|||||||
            os.path.join(self.SAMPLE_FILES, "encrypted.pdf"),
 | 
					            os.path.join(self.SAMPLE_FILES, "encrypted.pdf"),
 | 
				
			||||||
            "application/pdf",
 | 
					            "application/pdf",
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        self.assertTrue(os.path.isfile(thumb))
 | 
					        self.assertIsFile(thumb)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_get_dpi(self):
 | 
					    def test_get_dpi(self):
 | 
				
			||||||
        parser = RasterisedDocumentParser(None)
 | 
					        parser = RasterisedDocumentParser(None)
 | 
				
			||||||
@ -132,7 +133,7 @@ class TestParser(DirectoriesMixin, TestCase):
 | 
				
			|||||||
            "application/pdf",
 | 
					            "application/pdf",
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.assertTrue(os.path.isfile(parser.archive_path))
 | 
					        self.assertIsFile(parser.archive_path)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.assertContainsStrings(parser.get_text(), ["This is a test document."])
 | 
					        self.assertContainsStrings(parser.get_text(), ["This is a test document."])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -144,7 +145,7 @@ class TestParser(DirectoriesMixin, TestCase):
 | 
				
			|||||||
            "application/pdf",
 | 
					            "application/pdf",
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.assertTrue(os.path.isfile(parser.archive_path))
 | 
					        self.assertIsFile(parser.archive_path)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.assertContainsStrings(
 | 
					        self.assertContainsStrings(
 | 
				
			||||||
            parser.get_text(),
 | 
					            parser.get_text(),
 | 
				
			||||||
@ -225,7 +226,7 @@ class TestParser(DirectoriesMixin, TestCase):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        parser.parse(os.path.join(self.SAMPLE_FILES, "simple.png"), "image/png")
 | 
					        parser.parse(os.path.join(self.SAMPLE_FILES, "simple.png"), "image/png")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.assertTrue(os.path.isfile(parser.archive_path))
 | 
					        self.assertIsFile(parser.archive_path)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.assertContainsStrings(parser.get_text(), ["This is a test document."])
 | 
					        self.assertContainsStrings(parser.get_text(), ["This is a test document."])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -241,7 +242,7 @@ class TestParser(DirectoriesMixin, TestCase):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            parser.parse(dest_file, "image/png")
 | 
					            parser.parse(dest_file, "image/png")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            self.assertTrue(os.path.isfile(parser.archive_path))
 | 
					            self.assertIsFile(parser.archive_path)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            self.assertContainsStrings(parser.get_text(), ["This is a test document."])
 | 
					            self.assertContainsStrings(parser.get_text(), ["This is a test document."])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -273,7 +274,7 @@ class TestParser(DirectoriesMixin, TestCase):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        parser.parse(os.path.join(self.SAMPLE_FILES, "simple-no-dpi.png"), "image/png")
 | 
					        parser.parse(os.path.join(self.SAMPLE_FILES, "simple-no-dpi.png"), "image/png")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.assertTrue(os.path.isfile(parser.archive_path))
 | 
					        self.assertIsFile(parser.archive_path)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.assertContainsStrings(
 | 
					        self.assertContainsStrings(
 | 
				
			||||||
            parser.get_text().lower(),
 | 
					            parser.get_text().lower(),
 | 
				
			||||||
@ -286,7 +287,7 @@ class TestParser(DirectoriesMixin, TestCase):
 | 
				
			|||||||
            os.path.join(self.SAMPLE_FILES, "multi-page-digital.pdf"),
 | 
					            os.path.join(self.SAMPLE_FILES, "multi-page-digital.pdf"),
 | 
				
			||||||
            "application/pdf",
 | 
					            "application/pdf",
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        self.assertTrue(os.path.isfile(parser.archive_path))
 | 
					        self.assertIsFile(parser.archive_path)
 | 
				
			||||||
        self.assertContainsStrings(
 | 
					        self.assertContainsStrings(
 | 
				
			||||||
            parser.get_text().lower(),
 | 
					            parser.get_text().lower(),
 | 
				
			||||||
            ["page 1", "page 2", "page 3"],
 | 
					            ["page 1", "page 2", "page 3"],
 | 
				
			||||||
@ -299,7 +300,7 @@ class TestParser(DirectoriesMixin, TestCase):
 | 
				
			|||||||
            os.path.join(self.SAMPLE_FILES, "multi-page-digital.pdf"),
 | 
					            os.path.join(self.SAMPLE_FILES, "multi-page-digital.pdf"),
 | 
				
			||||||
            "application/pdf",
 | 
					            "application/pdf",
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        self.assertTrue(os.path.isfile(parser.archive_path))
 | 
					        self.assertIsFile(parser.archive_path)
 | 
				
			||||||
        self.assertContainsStrings(
 | 
					        self.assertContainsStrings(
 | 
				
			||||||
            parser.get_text().lower(),
 | 
					            parser.get_text().lower(),
 | 
				
			||||||
            ["page 1", "page 2", "page 3"],
 | 
					            ["page 1", "page 2", "page 3"],
 | 
				
			||||||
@ -312,7 +313,7 @@ class TestParser(DirectoriesMixin, TestCase):
 | 
				
			|||||||
            os.path.join(self.SAMPLE_FILES, "multi-page-digital.pdf"),
 | 
					            os.path.join(self.SAMPLE_FILES, "multi-page-digital.pdf"),
 | 
				
			||||||
            "application/pdf",
 | 
					            "application/pdf",
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        self.assertTrue(os.path.isfile(parser.archive_path))
 | 
					        self.assertIsFile(parser.archive_path)
 | 
				
			||||||
        self.assertContainsStrings(
 | 
					        self.assertContainsStrings(
 | 
				
			||||||
            parser.get_text().lower(),
 | 
					            parser.get_text().lower(),
 | 
				
			||||||
            ["page 1", "page 2", "page 3"],
 | 
					            ["page 1", "page 2", "page 3"],
 | 
				
			||||||
@ -325,7 +326,7 @@ class TestParser(DirectoriesMixin, TestCase):
 | 
				
			|||||||
            os.path.join(self.SAMPLE_FILES, "multi-page-digital.pdf"),
 | 
					            os.path.join(self.SAMPLE_FILES, "multi-page-digital.pdf"),
 | 
				
			||||||
            "application/pdf",
 | 
					            "application/pdf",
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        self.assertTrue(os.path.isfile(parser.archive_path))
 | 
					        self.assertIsFile(parser.archive_path)
 | 
				
			||||||
        self.assertContainsStrings(
 | 
					        self.assertContainsStrings(
 | 
				
			||||||
            parser.get_text().lower(),
 | 
					            parser.get_text().lower(),
 | 
				
			||||||
            ["page 1", "page 2", "page 3"],
 | 
					            ["page 1", "page 2", "page 3"],
 | 
				
			||||||
@ -338,7 +339,7 @@ class TestParser(DirectoriesMixin, TestCase):
 | 
				
			|||||||
            os.path.join(self.SAMPLE_FILES, "multi-page-images.pdf"),
 | 
					            os.path.join(self.SAMPLE_FILES, "multi-page-images.pdf"),
 | 
				
			||||||
            "application/pdf",
 | 
					            "application/pdf",
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        self.assertTrue(os.path.isfile(parser.archive_path))
 | 
					        self.assertIsFile(parser.archive_path)
 | 
				
			||||||
        self.assertContainsStrings(
 | 
					        self.assertContainsStrings(
 | 
				
			||||||
            parser.get_text().lower(),
 | 
					            parser.get_text().lower(),
 | 
				
			||||||
            ["page 1", "page 2", "page 3"],
 | 
					            ["page 1", "page 2", "page 3"],
 | 
				
			||||||
@ -362,7 +363,7 @@ class TestParser(DirectoriesMixin, TestCase):
 | 
				
			|||||||
            os.path.join(self.SAMPLE_FILES, "multi-page-images.pdf"),
 | 
					            os.path.join(self.SAMPLE_FILES, "multi-page-images.pdf"),
 | 
				
			||||||
            "application/pdf",
 | 
					            "application/pdf",
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        self.assertTrue(os.path.isfile(parser.archive_path))
 | 
					        self.assertIsFile(parser.archive_path)
 | 
				
			||||||
        self.assertContainsStrings(parser.get_text().lower(), ["page 1", "page 2"])
 | 
					        self.assertContainsStrings(parser.get_text().lower(), ["page 1", "page 2"])
 | 
				
			||||||
        self.assertNotIn("page 3", parser.get_text().lower())
 | 
					        self.assertNotIn("page 3", parser.get_text().lower())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -384,7 +385,7 @@ class TestParser(DirectoriesMixin, TestCase):
 | 
				
			|||||||
            os.path.join(self.SAMPLE_FILES, "multi-page-images.pdf"),
 | 
					            os.path.join(self.SAMPLE_FILES, "multi-page-images.pdf"),
 | 
				
			||||||
            "application/pdf",
 | 
					            "application/pdf",
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        self.assertTrue(os.path.isfile(parser.archive_path))
 | 
					        self.assertIsFile(parser.archive_path)
 | 
				
			||||||
        self.assertContainsStrings(parser.get_text().lower(), ["page 1"])
 | 
					        self.assertContainsStrings(parser.get_text().lower(), ["page 1"])
 | 
				
			||||||
        self.assertNotIn("page 2", parser.get_text().lower())
 | 
					        self.assertNotIn("page 2", parser.get_text().lower())
 | 
				
			||||||
        self.assertNotIn("page 3", parser.get_text().lower())
 | 
					        self.assertNotIn("page 3", parser.get_text().lower())
 | 
				
			||||||
@ -455,7 +456,7 @@ class TestParser(DirectoriesMixin, TestCase):
 | 
				
			|||||||
            "application/pdf",
 | 
					            "application/pdf",
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        self.assertIsNotNone(parser.archive_path)
 | 
					        self.assertIsNotNone(parser.archive_path)
 | 
				
			||||||
        self.assertTrue(os.path.isfile(parser.archive_path))
 | 
					        self.assertIsFile(parser.archive_path)
 | 
				
			||||||
        self.assertContainsStrings(
 | 
					        self.assertContainsStrings(
 | 
				
			||||||
            parser.get_text().lower(),
 | 
					            parser.get_text().lower(),
 | 
				
			||||||
            ["page 1", "page 2", "page 3", "page 4", "page 5", "page 6"],
 | 
					            ["page 1", "page 2", "page 3", "page 4", "page 5", "page 6"],
 | 
				
			||||||
@ -486,7 +487,7 @@ class TestParser(DirectoriesMixin, TestCase):
 | 
				
			|||||||
            "application/pdf",
 | 
					            "application/pdf",
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        self.assertIsNotNone(parser.archive_path)
 | 
					        self.assertIsNotNone(parser.archive_path)
 | 
				
			||||||
        self.assertTrue(os.path.isfile(parser.archive_path))
 | 
					        self.assertIsFile(parser.archive_path)
 | 
				
			||||||
        self.assertContainsStrings(
 | 
					        self.assertContainsStrings(
 | 
				
			||||||
            parser.get_text().lower(),
 | 
					            parser.get_text().lower(),
 | 
				
			||||||
            [
 | 
					            [
 | 
				
			||||||
@ -556,7 +557,7 @@ class TestParser(DirectoriesMixin, TestCase):
 | 
				
			|||||||
            os.path.join(self.SAMPLE_FILES, "multi-page-images.tiff"),
 | 
					            os.path.join(self.SAMPLE_FILES, "multi-page-images.tiff"),
 | 
				
			||||||
            "image/tiff",
 | 
					            "image/tiff",
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        self.assertTrue(os.path.isfile(parser.archive_path))
 | 
					        self.assertIsFile(parser.archive_path)
 | 
				
			||||||
        self.assertContainsStrings(
 | 
					        self.assertContainsStrings(
 | 
				
			||||||
            parser.get_text().lower(),
 | 
					            parser.get_text().lower(),
 | 
				
			||||||
            ["page 1", "page 2", "page 3"],
 | 
					            ["page 1", "page 2", "page 3"],
 | 
				
			||||||
@ -580,7 +581,7 @@ class TestParser(DirectoriesMixin, TestCase):
 | 
				
			|||||||
                tmp_file.name,
 | 
					                tmp_file.name,
 | 
				
			||||||
                "image/tiff",
 | 
					                "image/tiff",
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
            self.assertTrue(os.path.isfile(parser.archive_path))
 | 
					            self.assertIsFile(parser.archive_path)
 | 
				
			||||||
            self.assertContainsStrings(
 | 
					            self.assertContainsStrings(
 | 
				
			||||||
                parser.get_text().lower(),
 | 
					                parser.get_text().lower(),
 | 
				
			||||||
                ["page 1", "page 2", "page 3"],
 | 
					                ["page 1", "page 2", "page 3"],
 | 
				
			||||||
@ -608,7 +609,7 @@ class TestParser(DirectoriesMixin, TestCase):
 | 
				
			|||||||
                tmp_file.name,
 | 
					                tmp_file.name,
 | 
				
			||||||
                "image/tiff",
 | 
					                "image/tiff",
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
            self.assertTrue(os.path.isfile(parser.archive_path))
 | 
					            self.assertIsFile(parser.archive_path)
 | 
				
			||||||
            self.assertContainsStrings(
 | 
					            self.assertContainsStrings(
 | 
				
			||||||
                parser.get_text().lower(),
 | 
					                parser.get_text().lower(),
 | 
				
			||||||
                ["page 1", "page 2", "page 3"],
 | 
					                ["page 1", "page 2", "page 3"],
 | 
				
			||||||
@ -689,40 +690,40 @@ class TestParser(DirectoriesMixin, TestCase):
 | 
				
			|||||||
        self.assertIn("ةﯾﻠﺧﺎدﻻ ةرازو", parser.get_text())
 | 
					        self.assertIn("ةﯾﻠﺧﺎدﻻ ةرازو", parser.get_text())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class TestParserFileTypes(DirectoriesMixin, TestCase):
 | 
					class TestParserFileTypes(DirectoriesMixin, FileSystemAssertsMixin, TestCase):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    SAMPLE_FILES = os.path.join(os.path.dirname(__file__), "samples")
 | 
					    SAMPLE_FILES = os.path.join(os.path.dirname(__file__), "samples")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_bmp(self):
 | 
					    def test_bmp(self):
 | 
				
			||||||
        parser = RasterisedDocumentParser(None)
 | 
					        parser = RasterisedDocumentParser(None)
 | 
				
			||||||
        parser.parse(os.path.join(self.SAMPLE_FILES, "simple.bmp"), "image/bmp")
 | 
					        parser.parse(os.path.join(self.SAMPLE_FILES, "simple.bmp"), "image/bmp")
 | 
				
			||||||
        self.assertTrue(os.path.isfile(parser.archive_path))
 | 
					        self.assertIsFile(parser.archive_path)
 | 
				
			||||||
        self.assertIn("this is a test document", parser.get_text().lower())
 | 
					        self.assertIn("this is a test document", parser.get_text().lower())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_jpg(self):
 | 
					    def test_jpg(self):
 | 
				
			||||||
        parser = RasterisedDocumentParser(None)
 | 
					        parser = RasterisedDocumentParser(None)
 | 
				
			||||||
        parser.parse(os.path.join(self.SAMPLE_FILES, "simple.jpg"), "image/jpeg")
 | 
					        parser.parse(os.path.join(self.SAMPLE_FILES, "simple.jpg"), "image/jpeg")
 | 
				
			||||||
        self.assertTrue(os.path.isfile(parser.archive_path))
 | 
					        self.assertIsFile(parser.archive_path)
 | 
				
			||||||
        self.assertIn("this is a test document", parser.get_text().lower())
 | 
					        self.assertIn("this is a test document", parser.get_text().lower())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @override_settings(OCR_IMAGE_DPI=200)
 | 
					    @override_settings(OCR_IMAGE_DPI=200)
 | 
				
			||||||
    def test_gif(self):
 | 
					    def test_gif(self):
 | 
				
			||||||
        parser = RasterisedDocumentParser(None)
 | 
					        parser = RasterisedDocumentParser(None)
 | 
				
			||||||
        parser.parse(os.path.join(self.SAMPLE_FILES, "simple.gif"), "image/gif")
 | 
					        parser.parse(os.path.join(self.SAMPLE_FILES, "simple.gif"), "image/gif")
 | 
				
			||||||
        self.assertTrue(os.path.isfile(parser.archive_path))
 | 
					        self.assertIsFile(parser.archive_path)
 | 
				
			||||||
        self.assertIn("this is a test document", parser.get_text().lower())
 | 
					        self.assertIn("this is a test document", parser.get_text().lower())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_tiff(self):
 | 
					    def test_tiff(self):
 | 
				
			||||||
        parser = RasterisedDocumentParser(None)
 | 
					        parser = RasterisedDocumentParser(None)
 | 
				
			||||||
        parser.parse(os.path.join(self.SAMPLE_FILES, "simple.tif"), "image/tiff")
 | 
					        parser.parse(os.path.join(self.SAMPLE_FILES, "simple.tif"), "image/tiff")
 | 
				
			||||||
        self.assertTrue(os.path.isfile(parser.archive_path))
 | 
					        self.assertIsFile(parser.archive_path)
 | 
				
			||||||
        self.assertIn("this is a test document", parser.get_text().lower())
 | 
					        self.assertIn("this is a test document", parser.get_text().lower())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @override_settings(OCR_IMAGE_DPI=72)
 | 
					    @override_settings(OCR_IMAGE_DPI=72)
 | 
				
			||||||
    def test_webp(self):
 | 
					    def test_webp(self):
 | 
				
			||||||
        parser = RasterisedDocumentParser(None)
 | 
					        parser = RasterisedDocumentParser(None)
 | 
				
			||||||
        parser.parse(os.path.join(self.SAMPLE_FILES, "document.webp"), "image/webp")
 | 
					        parser.parse(os.path.join(self.SAMPLE_FILES, "document.webp"), "image/webp")
 | 
				
			||||||
        self.assertTrue(os.path.isfile(parser.archive_path))
 | 
					        self.assertIsFile(parser.archive_path)
 | 
				
			||||||
        # OCR consistent mangles this space, oh well
 | 
					        # OCR consistent mangles this space, oh well
 | 
				
			||||||
        self.assertIn(
 | 
					        self.assertIn(
 | 
				
			||||||
            "this is awebp document, created 11/14/2022.",
 | 
					            "this is awebp document, created 11/14/2022.",
 | 
				
			||||||
 | 
				
			|||||||
@ -2,10 +2,11 @@ import os
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
from django.test import TestCase
 | 
					from django.test import TestCase
 | 
				
			||||||
from documents.tests.utils import DirectoriesMixin
 | 
					from documents.tests.utils import DirectoriesMixin
 | 
				
			||||||
 | 
					from documents.tests.utils import FileSystemAssertsMixin
 | 
				
			||||||
from paperless_text.parsers import TextDocumentParser
 | 
					from paperless_text.parsers import TextDocumentParser
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class TestTextParser(DirectoriesMixin, TestCase):
 | 
					class TestTextParser(DirectoriesMixin, FileSystemAssertsMixin, TestCase):
 | 
				
			||||||
    def test_thumbnail(self):
 | 
					    def test_thumbnail(self):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        parser = TextDocumentParser(None)
 | 
					        parser = TextDocumentParser(None)
 | 
				
			||||||
@ -15,7 +16,7 @@ class TestTextParser(DirectoriesMixin, TestCase):
 | 
				
			|||||||
            os.path.join(os.path.dirname(__file__), "samples", "test.txt"),
 | 
					            os.path.join(os.path.dirname(__file__), "samples", "test.txt"),
 | 
				
			||||||
            "text/plain",
 | 
					            "text/plain",
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        self.assertTrue(os.path.isfile(f))
 | 
					        self.assertIsFile(f)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_parse(self):
 | 
					    def test_parse(self):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -8,6 +8,7 @@ from django.test import TestCase
 | 
				
			|||||||
from documents.parsers import ParseError
 | 
					from documents.parsers import ParseError
 | 
				
			||||||
from paperless_tika.parsers import TikaDocumentParser
 | 
					from paperless_tika.parsers import TikaDocumentParser
 | 
				
			||||||
from requests import Response
 | 
					from requests import Response
 | 
				
			||||||
 | 
					from rest_framework import status
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class TestTikaParser(TestCase):
 | 
					class TestTikaParser(TestCase):
 | 
				
			||||||
@ -26,7 +27,7 @@ class TestTikaParser(TestCase):
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
        response = Response()
 | 
					        response = Response()
 | 
				
			||||||
        response._content = b"PDF document"
 | 
					        response._content = b"PDF document"
 | 
				
			||||||
        response.status_code = 200
 | 
					        response.status_code = status.HTTP_200_OK
 | 
				
			||||||
        post.return_value = response
 | 
					        post.return_value = response
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        file = os.path.join(self.parser.tempdir, "input.odt")
 | 
					        file = os.path.join(self.parser.tempdir, "input.odt")
 | 
				
			||||||
@ -74,7 +75,7 @@ class TestTikaParser(TestCase):
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
        response = Response()
 | 
					        response = Response()
 | 
				
			||||||
        response._content = b"PDF document"
 | 
					        response._content = b"PDF document"
 | 
				
			||||||
        response.status_code = 500
 | 
					        response.status_code = status.HTTP_500_INTERNAL_SERVER_ERROR
 | 
				
			||||||
        post.return_value = response
 | 
					        post.return_value = response
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        file = os.path.join(self.parser.tempdir, "input.odt")
 | 
					        file = os.path.join(self.parser.tempdir, "input.odt")
 | 
				
			||||||
@ -98,7 +99,7 @@ class TestTikaParser(TestCase):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        response = Response()
 | 
					        response = Response()
 | 
				
			||||||
        response._content = b"PDF document"
 | 
					        response._content = b"PDF document"
 | 
				
			||||||
        response.status_code = 200
 | 
					        response.status_code = status.HTTP_200_OK
 | 
				
			||||||
        post.return_value = response
 | 
					        post.return_value = response
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        for setting, expected_key in [
 | 
					        for setting, expected_key in [
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user