From 45c07b183cef0578685309c21f1db1d10b183745 Mon Sep 17 00:00:00 2001 From: Stefan Wehrmeyer Date: Fri, 28 Jun 2024 16:30:55 +0200 Subject: [PATCH] Add basic paperless import interface --- fragdenstaat_de/fds_paperless/__init__.py | 0 fragdenstaat_de/fds_paperless/forms.py | 57 +++++++++++++++++++ fragdenstaat_de/fds_paperless/paperless.py | 37 ++++++++++++ .../fds_paperless/add_postal_message.html | 1 + fragdenstaat_de/fds_paperless/urls.py | 16 ++++++ fragdenstaat_de/fds_paperless/views.py | 44 ++++++++++++++ fragdenstaat_de/settings/base.py | 4 ++ fragdenstaat_de/theme/urls.py | 1 + 8 files changed, 160 insertions(+) create mode 100644 fragdenstaat_de/fds_paperless/__init__.py create mode 100644 fragdenstaat_de/fds_paperless/forms.py create mode 100644 fragdenstaat_de/fds_paperless/paperless.py create mode 100644 fragdenstaat_de/fds_paperless/templates/fds_paperless/add_postal_message.html create mode 100644 fragdenstaat_de/fds_paperless/urls.py create mode 100644 fragdenstaat_de/fds_paperless/views.py diff --git a/fragdenstaat_de/fds_paperless/__init__.py b/fragdenstaat_de/fds_paperless/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/fragdenstaat_de/fds_paperless/forms.py b/fragdenstaat_de/fds_paperless/forms.py new file mode 100644 index 000000000..fee06056b --- /dev/null +++ b/fragdenstaat_de/fds_paperless/forms.py @@ -0,0 +1,57 @@ +import re +from datetime import date + +from django import forms +from django.core.files.base import ContentFile + +from froide.foirequest.forms.message import PostalReplyForm +from froide.helper.widgets import BootstrapCheckboxSelectMultiple + +from .paperless import get_document_data + + +class PaperlessPostalReplyForm(PostalReplyForm): + files = None + paperless_ids = forms.MultipleChoiceField( + label="Paperless Dokumente", + widget=BootstrapCheckboxSelectMultiple, + required=True, + ) + + def __init__(self, *args, **kwargs): + paperless_docs = kwargs.pop("paperless_docs") + super().__init__(*args, **kwargs) + self.fields["paperless_ids"].choices = [ + ( + doc["id"], + "{date}: ({title}) - {content}...".format( + date=doc["added"], + title=doc["title"], + content=doc["content"][:100], + ), + ) + for doc in paperless_docs + ] + if paperless_docs: + DATE_RE = re.compile(r"(\d{1,2})\.(\d{1,2})\.(\d{4})") + match = DATE_RE.search(paperless_docs[0]["content"]) + if match: + self.fields["date"].initial = date( + int(match.group(3)), int(match.group(2)), int(match.group(1)) + ) + + def clean(self): + # Do not enforce presence of either files or text + pass + + def save(self): + message = super().save() + files = [] + for paperless_id in self.cleaned_data["paperless_ids"]: + meta_data, file_contents = get_document_data(paperless_id) + file = ContentFile(file_contents, name=meta_data["original_file_name"]) + file.content_type = "application/pdf" + files.append(file) + + self.save_attachments(files, message) + return message diff --git a/fragdenstaat_de/fds_paperless/paperless.py b/fragdenstaat_de/fds_paperless/paperless.py new file mode 100644 index 000000000..f0a76621d --- /dev/null +++ b/fragdenstaat_de/fds_paperless/paperless.py @@ -0,0 +1,37 @@ +from datetime import timedelta + +from django.conf import settings +from django.utils import timezone + +import requests + + +def get_paperless_client(): + session = requests.Session() + session.headers.update({"Authorization": settings.PAPERLESS_API_TOKEN}) + return session + + +def get_document_data(paperless_document_id): + API_URL = settings.PAPERLESS_API_URL + "/documents/{}/".format( + paperless_document_id + ) + client = get_paperless_client() + meta_data = client.get(API_URL).json() + + DOCUMENT_URL = settings.PAPERLESS_API_URL + "/documents/{}/download/".format( + paperless_document_id + ) + file_data = client.get(DOCUMENT_URL).content + + return meta_data, file_data + + +def list_documents(): + client = get_paperless_client() + last_week = timezone.now() - timedelta(days=7) + API_URL = settings.PAPERLESS_API_URL + "/documents/?created__date__gt={}".format( + last_week.date() + ) + data = client.get(API_URL).json() + return list(reversed(data["results"])) diff --git a/fragdenstaat_de/fds_paperless/templates/fds_paperless/add_postal_message.html b/fragdenstaat_de/fds_paperless/templates/fds_paperless/add_postal_message.html new file mode 100644 index 000000000..44a621afc --- /dev/null +++ b/fragdenstaat_de/fds_paperless/templates/fds_paperless/add_postal_message.html @@ -0,0 +1 @@ +{% extends "foirequest/upload_postal_message.html" %} diff --git a/fragdenstaat_de/fds_paperless/urls.py b/fragdenstaat_de/fds_paperless/urls.py new file mode 100644 index 000000000..1b7f54064 --- /dev/null +++ b/fragdenstaat_de/fds_paperless/urls.py @@ -0,0 +1,16 @@ +from django.urls import path + +from .views import add_postal_message, paperless_start + +urlpatterns = [ + path( + "/", + paperless_start, + name="paperless_index", + ), + path( + "import//", + add_postal_message, + name="paperless_import", + ), +] diff --git a/fragdenstaat_de/fds_paperless/views.py b/fragdenstaat_de/fds_paperless/views.py new file mode 100644 index 000000000..19ea4d620 --- /dev/null +++ b/fragdenstaat_de/fds_paperless/views.py @@ -0,0 +1,44 @@ +from django.shortcuts import get_object_or_404, redirect, render + +from froide.foirequest.auth import can_write_foirequest +from froide.foirequest.decorators import allow_write_foirequest +from froide.foirequest.models import FoiRequest +from froide.helper.utils import render_403 + +from .forms import PaperlessPostalReplyForm +from .paperless import list_documents + + +def paperless_start(request, pk): + foirequest = get_object_or_404(FoiRequest, pk=pk) + if not can_write_foirequest(foirequest, request): + return render_403(request) + return redirect("paperless_import", slug=foirequest.slug) + + +@allow_write_foirequest +def add_postal_message(request, foirequest): + paperless_docs = list_documents() + if request.method == "POST": + form = PaperlessPostalReplyForm( + request.POST, foirequest=foirequest, paperless_docs=paperless_docs + ) + if form.is_valid(): + message = form.save() + FoiRequest.message_received.send( + sender=foirequest, message=message, user=request.user + ) + return redirect(message) + else: + form = PaperlessPostalReplyForm( + foirequest=foirequest, paperless_docs=paperless_docs + ) + + return render( + request, + "fds_paperless/add_postal_message.html", + { + "object": foirequest, + "form": form, + }, + ) diff --git a/fragdenstaat_de/settings/base.py b/fragdenstaat_de/settings/base.py index d6d3302d2..bf1f852ac 100644 --- a/fragdenstaat_de/settings/base.py +++ b/fragdenstaat_de/settings/base.py @@ -60,6 +60,7 @@ def INSTALLED_APPS(self): "fragdenstaat_de.fds_mailing.apps.FdsMailingConfig", "fragdenstaat_de.fds_ogimage.apps.FdsOgImageConfig", "fragdenstaat_de.fds_fximport.apps.FdsFxImportConfig", + "fragdenstaat_de.fds_paperless", # Additional CMS plugins "djangocms_text_ckeditor", "djangocms_picture", @@ -844,3 +845,6 @@ def FROIDE_CONFIG(self): DJANGOCMS_ICON_SETS = [ ("fontawesome4", "fa", "Font Awesome 4", "4.7.0"), ] + + PAPERLESS_API_URL = os.environ.get("PAPERLESS_API_URL", "") + PAPERLESS_API_TOKEN = os.environ.get("PAPERLESS_API_TOKEN", "") diff --git a/fragdenstaat_de/theme/urls.py b/fragdenstaat_de/theme/urls.py index 81a248321..06d530d73 100644 --- a/fragdenstaat_de/theme/urls.py +++ b/fragdenstaat_de/theme/urls.py @@ -64,6 +64,7 @@ path("payment/", include("froide_payment.urls")), path("contractor/", include("contractor.urls")), path("fax/", include("froide_fax.urls")), + path("paperless/", include("fragdenstaat_de.fds_paperless.urls")), path("newsletter/update/", include("fragdenstaat_de.fds_newsletter.urls")), path("newsletter/archive/", include("fragdenstaat_de.fds_mailing.urls")), path(