mirror of
				https://github.com/paperless-ngx/paperless-ngx.git
				synced 2025-10-30 03:56:23 -05: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
	 jonaswinkler
					jonaswinkler