This commit is contained in:
shamoon
2024-01-30 23:41:23 -08:00
parent d6d0071175
commit c9dd407cbe
12 changed files with 337 additions and 18 deletions

View File

@@ -1,4 +1,7 @@
import hashlib
import itertools
import logging
import os
from django.db.models import Q
@@ -9,6 +12,9 @@ from documents.models import StoragePath
from documents.permissions import set_permissions_for_object
from documents.tasks import bulk_update_documents
from documents.tasks import update_document_archive_file
from paperless import settings
logger = logging.getLogger("paperless.bulk_edit")
def set_correspondent(doc_ids, correspondent):
@@ -146,3 +152,30 @@ def set_permissions(doc_ids, set_permissions, owner=None, merge=False):
bulk_update_documents.delay(document_ids=affected_docs)
return "OK"
def rotate(doc_ids: list[int], degrees: int):
qs = Document.objects.filter(id__in=doc_ids)
affected_docs = []
import pikepdf
for doc in qs:
try:
with pikepdf.open(doc.source_path, allow_overwriting_input=True) as pdf:
for page in pdf.pages:
page.rotate(degrees, relative=True)
pdf.save()
doc.checksum = hashlib.md5(doc.source_file.read()).hexdigest()
doc.save()
update_document_archive_file.delay(
document_id=doc.id,
)
logger.info(f"Rotated document {doc.id} ({path}) by {degrees} degrees")
affected_docs.append(doc.id)
except Exception as e:
logger.exception(f"Error rotating document {doc.id}", e)
if len(affected_docs) > 0:
bulk_update_documents.delay(document_ids=affected_docs)
return "OK"

View File

@@ -869,6 +869,7 @@ class BulkEditSerializer(DocumentListSerializer, SetPermissionsMixin):
"delete",
"redo_ocr",
"set_permissions",
"rotate",
],
label="Method",
write_only=True,
@@ -906,6 +907,8 @@ class BulkEditSerializer(DocumentListSerializer, SetPermissionsMixin):
return bulk_edit.redo_ocr
elif method == "set_permissions":
return bulk_edit.set_permissions
elif method == "rotate":
return bulk_edit.rotate
else:
raise serializers.ValidationError("Unsupported method.")
@@ -984,6 +987,16 @@ class BulkEditSerializer(DocumentListSerializer, SetPermissionsMixin):
if "merge" not in parameters:
parameters["merge"] = False
def _validate_parameters_rotate(self, parameters):
try:
if (
"degrees" not in parameters
or not float(parameters["degrees"]).is_integer()
):
raise serializers.ValidationError("invalid rotation degrees")
except ValueError:
raise serializers.ValidationError("invalid rotation degrees")
def validate(self, attrs):
method = attrs["method"]
parameters = attrs["parameters"]
@@ -1000,6 +1013,8 @@ class BulkEditSerializer(DocumentListSerializer, SetPermissionsMixin):
self._validate_storage_path(parameters)
elif method == bulk_edit.set_permissions:
self._validate_parameters_set_permissions(parameters)
elif method == bulk_edit.rotate:
self._validate_parameters_rotate(parameters)
return attrs

View File

@@ -781,3 +781,58 @@ class TestBulkEditAPI(DirectoriesMixin, APITestCase):
self.assertEqual(response.status_code, status.HTTP_200_OK)
m.assert_called_once()
@mock.patch("documents.serialisers.bulk_edit.rotate")
def test_rotate(self, m):
m.return_value = "OK"
response = self.client.post(
"/api/documents/bulk_edit/",
json.dumps(
{
"documents": [self.doc2.id, self.doc3.id],
"method": "rotate",
"parameters": {"degrees": 90},
},
),
content_type="application/json",
)
self.assertEqual(response.status_code, status.HTTP_200_OK)
m.assert_called_once()
args, kwargs = m.call_args
self.assertCountEqual(args[0], [self.doc2.id, self.doc3.id])
self.assertEqual(kwargs["degrees"], 90)
@mock.patch("documents.serialisers.bulk_edit.rotate")
def test_rotate_invalid_params(self, m):
response = self.client.post(
"/api/documents/bulk_edit/",
json.dumps(
{
"documents": [self.doc2.id, self.doc3.id],
"method": "rotate",
"parameters": {"degrees": "foo"},
},
),
content_type="application/json",
)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
response = self.client.post(
"/api/documents/bulk_edit/",
json.dumps(
{
"documents": [self.doc2.id, self.doc3.id],
"method": "rotate",
"parameters": {"degrees": 90.5},
},
),
content_type="application/json",
)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
m.assert_not_called()

View File

@@ -891,7 +891,8 @@ class BulkEditView(GenericAPIView, PassUserMixin):
document_objs = Document.objects.filter(pk__in=documents)
has_perms = (
all((doc.owner == user or doc.owner is None) for doc in document_objs)
if method == bulk_edit.set_permissions
if method
in [bulk_edit.set_permissions, bulk_edit.delete, bulk_edit.rotate]
else all(
has_perms_owner_aware(user, "change_document", doc)
for doc in document_objs