report pdf + export options + work

This commit is contained in:
JMARyA 2024-12-06 15:07:03 +01:00
parent 3caecf63f6
commit 696e603476
Signed by: jmarya
GPG key ID: 901B2ADDF27C2263
9 changed files with 2579 additions and 12 deletions

View file

@ -1,12 +1,35 @@
import json import json
from weasyprint import HTML, CSS
from io import BytesIO
from pdf2image import convert_from_bytes
from PIL import Image
from django.conf import settings
from django.template.loader import render_to_string
from .models import Berichtsheft
from io import BytesIO
def gen_doc(template: str, vars: dict) -> str: def gen_doc_html(report: Berichtsheft):
definition = json.loads(open(f"{template}.json").read()) return render_to_string(f"report_template/{report.kind}.html", report.vars())
content = open(f"{template}.html").read()
for var in definition["vars"]:
var_r = var["name"].upper()
content = content.replace(f"[[{var_r}]]", vars[var["name"]])
return content def gen_doc_pdf(report: Berichtsheft):
main_content = gen_doc_html(report)
pdf_buffer = BytesIO()
HTML(
string=main_content,
).write_pdf(pdf_buffer, stylesheets=[CSS(string="@page { size: A4; margin: 1cm }")])
pdf_buffer.seek(0)
return pdf_buffer
def gen_doc_png(report: Berichtsheft):
pdf_buffer = gen_doc_pdf(report)
pages = convert_from_bytes(pdf_buffer.getvalue(), 300)
for page in pages:
img_byte_array = BytesIO()
page.save(img_byte_array, format="PNG")
img_byte_array.seek(0)
return img_byte_array

View file

@ -1,6 +1,8 @@
from django.db import models from django.db import models
from django.contrib.auth.models import AbstractUser from django.contrib.auth.models import AbstractUser
from django.contrib.auth.models import Group as BuiltinGroup from django.contrib.auth.models import Group as BuiltinGroup
from .util import get_week_range
import base64
class User(AbstractUser): class User(AbstractUser):
@ -49,6 +51,28 @@ class Berichtsheft(models.Model):
approvals = Approval.objects.filter(report=self.id) approvals = Approval.objects.filter(report=self.id)
return len(approvals) >= 2 return len(approvals) >= 2
def vars(self):
start_date, end_date = get_week_range(self.year, self.week)
vars = {
"user": self.user,
"kind": self.kind,
"num": self.num,
"year": self.year,
"week": self.week,
"department": self.department,
"start_date": start_date,
"end_date": end_date,
"image": base64.b64encode(self.image).decode("utf-8")
if self.image is not None
else None,
}
for key, val in self.content.items():
vars[key] = val
return vars
class Approval(models.Model): class Approval(models.Model):
id = models.AutoField(primary_key=True) id = models.AutoField(primary_key=True)

View file

@ -2,7 +2,7 @@
STYLE = { STYLE = {
"red_btn": "text-white bg-red-700 hover:bg-red-800 focus:outline-none focus:ring-4 focus:ring-red-300 font-medium rounded-full text-sm px-5 py-2.5 text-center me-2 mb-2 dark:bg-red-600 dark:hover:bg-red-700 dark:focus:ring-red-900", "red_btn": "text-white bg-red-700 hover:bg-red-800 focus:outline-none focus:ring-4 focus:ring-red-300 font-medium rounded-full text-sm px-5 py-2.5 text-center me-2 mb-2 dark:bg-red-600 dark:hover:bg-red-700 dark:focus:ring-red-900",
"card": "bg-white drop-shadow-md p-4 mx-auto mt-5 aspect-[2/3] hover:drop-shadow-xl hover:scale-[0.85] scale-[0.8] lg:hover:scale-[0.95] lg:scale-[0.9] transition-all duration-50 transform ease-in-out w-60 sm:w-80 flex items-center justify-center", "card": "bg-white drop-shadow-md mx-auto mt-5 aspect-[2/3] hover:drop-shadow-xl hover:scale-[0.85] scale-[0.8] lg:hover:scale-[0.95] lg:scale-[0.9] transition-all duration-50 transform ease-in-out w-60 sm:w-80 flex items-center justify-center",
"text-input": "w-full p-2 border border-gray-300 rounded-lg focus:ring focus:ring-blue-400 mb-5", "text-input": "w-full p-2 border border-gray-300 rounded-lg focus:ring focus:ring-blue-400 mb-5",
} }

View file

@ -8,5 +8,6 @@ hx-get="/report/{{ report.id }}" hx-target="#main_content" hx-push-url="true" hx
> >
</div> </div>
{% endif %} {{ report }} {% endif %}
<img src="/report/{{ report.id }}/png" class="w-full h-full object-cover">
</a> </a>

View file

@ -1,7 +1,8 @@
{{ title|safe }} {{ title|safe }}
<main class="flex-grow w-full max-w-4xl mx-auto p-6 bg-white shadow-lg rounded-lg mt-6"> <main class="flex-grow w-full max-w-4xl mx-auto p-6 bg-white shadow-lg rounded-lg mt-6">
<div class="mb-4"> <div class="mb-4 flex justify-between">
<div>
<p class="text-lg font-medium text-gray-700"> <p class="text-lg font-medium text-gray-700">
Berichtsheft <span class="font-semibold">{{ report.year }}</span> / <span class="font-semibold">{{ report.week }}</span> Berichtsheft <span class="font-semibold">{{ report.year }}</span> / <span class="font-semibold">{{ report.week }}</span>
</p> </p>
@ -12,6 +13,12 @@
</span> </span>
</p> </p>
</div> </div>
<div class="mt-3">
<a href="/report/{{ report.id }}/pdf" class="{{ STYLE.red_btn }}">
Download
</a>
</div>
</div>
<div class="border-t border-gray-200 mt-4 pt-4"> <div class="border-t border-gray-200 mt-4 pt-4">
<h2 class="text-lg font-medium text-gray-800 mb-2">Content:</h2> <h2 class="text-lg font-medium text-gray-800 mb-2">Content:</h2>

File diff suppressed because it is too large Load diff

View file

@ -5,6 +5,9 @@ urlpatterns = [
path("", views.index, name="index"), path("", views.index, name="index"),
path("write", views.write_new_report, name="write"), path("write", views.write_new_report, name="write"),
path("report/<int:report_id>", views.report_detail_page, name="report_detail"), path("report/<int:report_id>", views.report_detail_page, name="report_detail"),
path("report/<int:report_id>/pdf", views.report_pdf_route, name="report_pdf"),
path("report/<int:report_id>/html", views.report_html_route, name="report_html"),
path("report/<int:report_id>/png", views.report_png_route, name="report_png"),
path("reports", views.reports_list, name="reports_list"), path("reports", views.reports_list, name="reports_list"),
path("draft", views.report_draft_update, name="report_draft_update"), path("draft", views.report_draft_update, name="report_draft_update"),
path("settings", views.settings_page, name="settings"), path("settings", views.settings_page, name="settings"),

View file

@ -9,6 +9,7 @@ from django.template.loader import render_to_string
from core.styles import STYLE from core.styles import STYLE
from .util import is_htmx_request, title, htmx_request from .util import is_htmx_request, title, htmx_request
from .reports import choose_report_kind from .reports import choose_report_kind
from .doc_gen import gen_doc_html, gen_doc_pdf, gen_doc_png
import datetime import datetime
import json import json
@ -176,7 +177,7 @@ def report_detail_page(request, report_id):
return htmx_request( return htmx_request(
request, request,
"report.html", "report.html",
{"report": report, "form": form}, {"report": report, "form": form, "STYLE": STYLE},
f"Berichtsheft {report.num}", f"Berichtsheft {report.num}",
) )
@ -224,3 +225,45 @@ def settings_page(request):
{"user": user}, {"user": user},
"Einstellungen", "Einstellungen",
) )
def report_pdf_route(request, report_id):
user = AzureUser(request)
report = get_object_or_404(Berichtsheft, id=report_id)
form = choose_report_kind(report.kind)
if report.user != user.id:
return HttpResponse("Nah", status=401)
pdf_buffer = gen_doc_pdf(report)
response = HttpResponse(pdf_buffer, content_type="application/pdf")
return response
def report_html_route(request, report_id):
user = AzureUser(request)
report = get_object_or_404(Berichtsheft, id=report_id)
form = choose_report_kind(report.kind)
if report.user != user.id:
return HttpResponse("Nah", status=401)
html = gen_doc_html(report)
response = HttpResponse(html)
return response
def report_png_route(request, report_id):
user = AzureUser(request)
report = get_object_or_404(Berichtsheft, id=report_id)
form = choose_report_kind(report.kind)
if report.user != user.id:
return HttpResponse("Nah", status=401)
png = gen_doc_png(report)
response = HttpResponse(png, content_type="image/png")
return response

View file

@ -3,3 +3,5 @@ psycopg2
markdown markdown
bleach bleach
pillow pillow
weasyprint
pdf2image