report pdf + export options + work
This commit is contained in:
parent
3caecf63f6
commit
696e603476
9 changed files with 2579 additions and 12 deletions
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
2464
core/templates/report_template/weekly.html
Normal file
2464
core/templates/report_template/weekly.html
Normal file
File diff suppressed because it is too large
Load diff
|
@ -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"),
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -3,3 +3,5 @@ psycopg2
|
||||||
markdown
|
markdown
|
||||||
bleach
|
bleach
|
||||||
pillow
|
pillow
|
||||||
|
weasyprint
|
||||||
|
pdf2image
|
Loading…
Add table
Reference in a new issue