mirror of
				https://github.com/paperless-ngx/paperless-ngx.git
				synced 2025-11-03 03:16:10 -06:00 
			
		
		
		
	API now supports setting metadata when POSTing documents.
This commit is contained in:
		@@ -216,9 +216,7 @@ be instructed to consume the document from there.
 | 
			
		||||
The endpoint supports the following optional form fields:
 | 
			
		||||
 | 
			
		||||
*   ``title``: Specify a title that the consumer should use for the document.
 | 
			
		||||
*   ``correspondent``: Specify a correspondent that the consumer should use for the document.
 | 
			
		||||
    Case sensitive. If the specified correspondent does not exist, it will be created with this
 | 
			
		||||
    name and default settings.
 | 
			
		||||
*   ``correspondent``: Specify the ID of a correspondent that the consumer should use for the document.
 | 
			
		||||
*   ``document_type``: Similar to correspondent.
 | 
			
		||||
*   ``tags``: Similar to correspondent. Specify this multiple times to have multiple tags added
 | 
			
		||||
    to the document.
 | 
			
		||||
 
 | 
			
		||||
@@ -8,6 +8,9 @@ Changelog
 | 
			
		||||
paperless-ng 0.9.5
 | 
			
		||||
##################
 | 
			
		||||
 | 
			
		||||
Apart from the API, this finalizes the changes I wanted to get into paperless before 1.0. The next releases will
 | 
			
		||||
focus on fixing bugs, minor changes to the UI, and possibly some changes to the API.
 | 
			
		||||
 | 
			
		||||
* OCR
 | 
			
		||||
 | 
			
		||||
  * Paperless now uses `OCRmyPDF <https://github.com/jbarlow83/OCRmyPDF>`_ to perform OCR on documents.
 | 
			
		||||
@@ -33,10 +36,15 @@ paperless-ng 0.9.5
 | 
			
		||||
  * The endpoint for uploading documents now supports specifying custom titles, correspondents, tags and types.
 | 
			
		||||
    This can be used by clients to override the default behavior of paperless.
 | 
			
		||||
  * The document endpoint of API now serves document in this form:
 | 
			
		||||
    * correspondents, document types and tags are referenced by their ID in the fields ``correspondent``, ``document_type`` and ``tags``. The ``*_id`` versions are gone. These fields are read/write.
 | 
			
		||||
    * in addition to that, ``*_object`` fields serve nested objects. Read only. Don't rely on these, they will probably get removed once I figure out how to better handle asynchronous data in the front end.
 | 
			
		||||
 | 
			
		||||
* Some minor improvements to the front end, such as document count in the document list, better visibility of the current view, and improvements to the filter behavior.
 | 
			
		||||
    * correspondents, document types and tags are referenced by their ID in the fields ``correspondent``, ``document_type`` and ``tags``. The ``*_id`` versions are gone. These fields are read/write.
 | 
			
		||||
    * paperless does not serve nested tags, correspondents or types anymore.
 | 
			
		||||
 | 
			
		||||
* Front end
 | 
			
		||||
 | 
			
		||||
  * Paperless does some basic caching of correspondents, tags and types and will only request them from the server when necessary or when entirely reloading the page.
 | 
			
		||||
  * Document lists should be somewhat faster now, especially when lots of tags/correspondents where present.
 | 
			
		||||
  * Some minor improvements to the front end, such as document count in the document list, better highlighting of the current page, and improvements to the filter behavior.
 | 
			
		||||
 | 
			
		||||
* Fixes:
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -126,22 +126,26 @@ class PostDocumentSerializer(serializers.Serializer):
 | 
			
		||||
        required=False,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    correspondent = serializers.CharField(
 | 
			
		||||
    correspondent = serializers.PrimaryKeyRelatedField(
 | 
			
		||||
        queryset=Correspondent.objects.all(),
 | 
			
		||||
        label="Correspondent",
 | 
			
		||||
        allow_null=True,
 | 
			
		||||
        write_only=True,
 | 
			
		||||
        required=False,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    document_type = serializers.CharField(
 | 
			
		||||
    document_type = serializers.PrimaryKeyRelatedField(
 | 
			
		||||
        queryset=DocumentType.objects.all(),
 | 
			
		||||
        label="Document type",
 | 
			
		||||
        allow_null=True,
 | 
			
		||||
        write_only=True,
 | 
			
		||||
        required=False,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    tags = serializers.ListField(
 | 
			
		||||
        child=serializers.CharField(),
 | 
			
		||||
    tags = serializers.PrimaryKeyRelatedField(
 | 
			
		||||
        many=True,
 | 
			
		||||
        queryset=Tag.objects.all(),
 | 
			
		||||
        label="Tags",
 | 
			
		||||
        source="tag",
 | 
			
		||||
        write_only=True,
 | 
			
		||||
        required=False,
 | 
			
		||||
    )
 | 
			
		||||
@@ -170,24 +174,19 @@ class PostDocumentSerializer(serializers.Serializer):
 | 
			
		||||
 | 
			
		||||
        correspondent = attrs.get('correspondent')
 | 
			
		||||
        if correspondent:
 | 
			
		||||
            c, _ = Correspondent.objects.get_or_create(name=correspondent)
 | 
			
		||||
            attrs['correspondent_id'] = c.id
 | 
			
		||||
            attrs['correspondent_id'] = correspondent.id
 | 
			
		||||
        else:
 | 
			
		||||
            attrs['correspondent_id'] = None
 | 
			
		||||
 | 
			
		||||
        document_type = attrs.get('document_type')
 | 
			
		||||
        if document_type:
 | 
			
		||||
            dt, _ = DocumentType.objects.get_or_create(name=document_type)
 | 
			
		||||
            attrs['document_type_id'] = dt.id
 | 
			
		||||
            attrs['document_type_id'] = document_type.id
 | 
			
		||||
        else:
 | 
			
		||||
            attrs['document_type_id'] = None
 | 
			
		||||
 | 
			
		||||
        tags = attrs.get('tag')
 | 
			
		||||
        tags = attrs.get('tags')
 | 
			
		||||
        if tags:
 | 
			
		||||
            tag_ids = []
 | 
			
		||||
            for tag in tags:
 | 
			
		||||
                tag, _ = Tag.objects.get_or_create(name=tag)
 | 
			
		||||
                tag_ids.append(tag.id)
 | 
			
		||||
            tag_ids = [tag.id for tag in tags]
 | 
			
		||||
            attrs['tag_ids'] = tag_ids
 | 
			
		||||
        else:
 | 
			
		||||
            attrs['tag_ids'] = None
 | 
			
		||||
 
 | 
			
		||||
@@ -410,7 +410,7 @@ class TestDocumentApi(DirectoriesMixin, APITestCase):
 | 
			
		||||
    def test_upload_with_correspondent(self, async_task):
 | 
			
		||||
        c = Correspondent.objects.create(name="test-corres")
 | 
			
		||||
        with open(os.path.join(os.path.dirname(__file__), "samples", "simple.pdf"), "rb") as f:
 | 
			
		||||
            response = self.client.post("/api/documents/post_document/", {"document": f, "correspondent": "test-corres"})
 | 
			
		||||
            response = self.client.post("/api/documents/post_document/", {"document": f, "correspondent": c.id})
 | 
			
		||||
        self.assertEqual(response.status_code, 200)
 | 
			
		||||
 | 
			
		||||
        async_task.assert_called_once()
 | 
			
		||||
@@ -420,23 +420,18 @@ class TestDocumentApi(DirectoriesMixin, APITestCase):
 | 
			
		||||
        self.assertEqual(kwargs['override_correspondent_id'], c.id)
 | 
			
		||||
 | 
			
		||||
    @mock.patch("documents.views.async_task")
 | 
			
		||||
    def test_upload_with_new_correspondent(self, async_task):
 | 
			
		||||
    def test_upload_with_invalid_correspondent(self, async_task):
 | 
			
		||||
        with open(os.path.join(os.path.dirname(__file__), "samples", "simple.pdf"), "rb") as f:
 | 
			
		||||
            response = self.client.post("/api/documents/post_document/", {"document": f, "correspondent": "test-corres2"})
 | 
			
		||||
        self.assertEqual(response.status_code, 200)
 | 
			
		||||
            response = self.client.post("/api/documents/post_document/", {"document": f, "correspondent": 3456})
 | 
			
		||||
        self.assertEqual(response.status_code, 400)
 | 
			
		||||
 | 
			
		||||
        async_task.assert_called_once()
 | 
			
		||||
 | 
			
		||||
        args, kwargs = async_task.call_args
 | 
			
		||||
 | 
			
		||||
        c = Correspondent.objects.get(name="test-corres2")
 | 
			
		||||
        self.assertEqual(kwargs['override_correspondent_id'], c.id)
 | 
			
		||||
        async_task.assert_not_called()
 | 
			
		||||
 | 
			
		||||
    @mock.patch("documents.views.async_task")
 | 
			
		||||
    def test_upload_with_document_type(self, async_task):
 | 
			
		||||
        dt = DocumentType.objects.create(name="invoice")
 | 
			
		||||
        with open(os.path.join(os.path.dirname(__file__), "samples", "simple.pdf"), "rb") as f:
 | 
			
		||||
            response = self.client.post("/api/documents/post_document/", {"document": f, "document_type": "invoice"})
 | 
			
		||||
            response = self.client.post("/api/documents/post_document/", {"document": f, "document_type": dt.id})
 | 
			
		||||
        self.assertEqual(response.status_code, 200)
 | 
			
		||||
 | 
			
		||||
        async_task.assert_called_once()
 | 
			
		||||
@@ -446,30 +441,37 @@ class TestDocumentApi(DirectoriesMixin, APITestCase):
 | 
			
		||||
        self.assertEqual(kwargs['override_document_type_id'], dt.id)
 | 
			
		||||
 | 
			
		||||
    @mock.patch("documents.views.async_task")
 | 
			
		||||
    def test_upload_with_new_document_type(self, async_task):
 | 
			
		||||
    def test_upload_with_invalid_document_type(self, async_task):
 | 
			
		||||
        with open(os.path.join(os.path.dirname(__file__), "samples", "simple.pdf"), "rb") as f:
 | 
			
		||||
            response = self.client.post("/api/documents/post_document/", {"document": f, "document_type": "invoice2"})
 | 
			
		||||
        self.assertEqual(response.status_code, 200)
 | 
			
		||||
            response = self.client.post("/api/documents/post_document/", {"document": f, "document_type": 34578})
 | 
			
		||||
        self.assertEqual(response.status_code, 400)
 | 
			
		||||
 | 
			
		||||
        async_task.assert_called_once()
 | 
			
		||||
 | 
			
		||||
        args, kwargs = async_task.call_args
 | 
			
		||||
 | 
			
		||||
        dt = DocumentType.objects.get(name="invoice2")
 | 
			
		||||
        self.assertEqual(kwargs['override_document_type_id'], dt.id)
 | 
			
		||||
        async_task.assert_not_called()
 | 
			
		||||
 | 
			
		||||
    @mock.patch("documents.views.async_task")
 | 
			
		||||
    def test_upload_with_tags(self, async_task):
 | 
			
		||||
        t1 = Tag.objects.create(name="tag1")
 | 
			
		||||
        t2 = Tag.objects.create(name="tag2")
 | 
			
		||||
        with open(os.path.join(os.path.dirname(__file__), "samples", "simple.pdf"), "rb") as f:
 | 
			
		||||
            response = self.client.post(
 | 
			
		||||
                "/api/documents/post_document/",
 | 
			
		||||
                {"document": f, "tags": ["tag1", "tag2"]})
 | 
			
		||||
                {"document": f, "tags": [t2.id, t1.id]})
 | 
			
		||||
        self.assertEqual(response.status_code, 200)
 | 
			
		||||
 | 
			
		||||
        async_task.assert_called_once()
 | 
			
		||||
 | 
			
		||||
        args, kwargs = async_task.call_args
 | 
			
		||||
 | 
			
		||||
        t2 = Tag.objects.get(name="tag2")
 | 
			
		||||
        self.assertCountEqual(kwargs['override_tag_ids'], [t1.id, t2.id])
 | 
			
		||||
 | 
			
		||||
    @mock.patch("documents.views.async_task")
 | 
			
		||||
    def test_upload_with_invalid_tags(self, async_task):
 | 
			
		||||
        t1 = Tag.objects.create(name="tag1")
 | 
			
		||||
        t2 = Tag.objects.create(name="tag2")
 | 
			
		||||
        with open(os.path.join(os.path.dirname(__file__), "samples", "simple.pdf"), "rb") as f:
 | 
			
		||||
            response = self.client.post(
 | 
			
		||||
                "/api/documents/post_document/",
 | 
			
		||||
                {"document": f, "tags": [t2.id, t1.id, 734563]})
 | 
			
		||||
        self.assertEqual(response.status_code, 400)
 | 
			
		||||
 | 
			
		||||
        async_task.assert_not_called()
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user