image feature + refactor

This commit is contained in:
JMARyA 2024-12-06 12:10:30 +01:00
parent 73e019de9a
commit d64efb663a
Signed by: jmarya
GPG key ID: 901B2ADDF27C2263
13 changed files with 221 additions and 37 deletions

View file

@ -3,7 +3,7 @@ import json
import base64
from core.models import Berichtsheft
from core.reports import DailyReport, WeeklyReport
from core.reports import DailyReport, WeeklyReport, choose_report_kind
import core.util
@ -41,12 +41,8 @@ class AzureUser:
# TODO : Implement
return "weekly"
def get_report_kind_form(self, request=None):
match self.get_report_kind():
case "weekly":
return WeeklyReport(request)
case "daily":
return DailyReport(request)
def get_report_kind_form(self, request=None, files=None):
return choose_report_kind(self.get_report_kind(), request, files)
def latest_report(self):
return self.reports().order_by("-year", "-week").first()

View file

@ -0,0 +1,17 @@
# Generated by Django 4.2.17 on 2024-12-06 09:38
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("core", "0011_berichtsheftdraft"),
]
operations = [
migrations.AddField(
model_name="berichtsheft",
name="image",
field=models.BinaryField(blank=True, null=True),
),
]

View file

@ -39,6 +39,7 @@ class Berichtsheft(models.Model):
content = models.JSONField()
needs_rewrite = models.BooleanField(default=False)
created = models.DateTimeField(auto_now_add=True)
image = models.BinaryField(null=True, blank=True)
def __str__(self):
return f"Berichtsheft: {self.user}, Year: {self.year}, Week: {self.week}"

View file

@ -1,6 +1,6 @@
from django import forms
from core.styles import label_span
from core.styles import label_span, STYLE
class WeeklyReport(forms.Form):
@ -10,7 +10,7 @@ class WeeklyReport(forms.Form):
widget=forms.TextInput(
attrs={
"placeholder": "Abteilung",
"class": "w-full p-2 border border-gray-300 rounded-lg focus:ring focus:ring-blue-400 mb-5",
"class": STYLE["text-input"],
}
),
)
@ -19,7 +19,7 @@ class WeeklyReport(forms.Form):
max_length=300,
widget=forms.Textarea(
attrs={
"class": "w-full p-2 border border-gray-300 rounded-lg focus:ring focus:ring-blue-400 mb-5",
"class": STYLE["text-input"],
"rows": 5,
"placeholder": "Betriebliche Tätigkeiten",
}
@ -30,7 +30,7 @@ class WeeklyReport(forms.Form):
max_length=600,
widget=forms.Textarea(
attrs={
"class": "w-full p-2 border border-gray-300 rounded-lg focus:ring focus:ring-blue-400 mb-5",
"class": STYLE["text-input"],
"rows": 10,
"placeholder": "Thema der Woche",
}
@ -41,12 +41,31 @@ class WeeklyReport(forms.Form):
max_length=300,
widget=forms.Textarea(
attrs={
"class": "w-full p-2 border border-gray-300 rounded-lg focus:ring focus:ring-blue-400 mb-5",
"class": STYLE["text-input"],
"rows": 5,
"placeholder": "Berufsschule",
}
),
)
img = forms.ImageField(
label=label_span("Bild"),
required=False,
widget=forms.ClearableFileInput(
attrs={
"class": "block w-full p-2 text-sm text-gray-900 border border-gray-300 rounded-lg cursor-pointer bg-gray-50 dark:text-gray-400 focus:outline-none dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400",
"accept": "image/*",
}
),
)
@property
def display_names(self):
return {
"department": self.fields["department"].label,
"company_text": self.fields["company_text"].label,
"week_topic": self.fields["week_topic"].label,
"school_text": self.fields["school_text"].label,
}
def content_values(self) -> dict:
if self.is_valid():
@ -57,11 +76,121 @@ class WeeklyReport(forms.Form):
}
class DailyReport:
department = forms.CharField(label="Abteilung", max_length=150)
week_topic = forms.CharField(label="Thema der Woche", max_length=600)
monday_text = forms.CharField(label="Berufsschule", max_length=300)
tuesday_text = forms.CharField(label="Dienstag", max_length=300)
wednesday_text = forms.CharField(label="Mittwoch", max_length=300)
thursday_text = forms.CharField(label="Donnerstag", max_length=300)
friday_text = forms.CharField(label="Freitag", max_length=300)
class DailyReport(forms.Form):
department = forms.CharField(
label=label_span("Abteilung"),
max_length=150,
widget=forms.TextInput(
attrs={
"placeholder": "Abteilung",
"class": STYLE["text-input"],
}
),
)
week_topic = forms.CharField(
label=label_span("Thema der Woche"),
max_length=600,
widget=forms.Textarea(
attrs={
"class": STYLE["text-input"],
"rows": 5,
"placeholder": "Thema der Woche",
}
),
)
monday_text = forms.CharField(
label=label_span("Montag"),
max_length=300,
widget=forms.Textarea(
attrs={
"class": STYLE["text-input"],
"rows": 5,
"placeholder": "Montag",
}
),
)
tuesday_text = forms.CharField(
label=label_span("Dienstag"),
max_length=300,
widget=forms.Textarea(
attrs={
"class": STYLE["text-input"],
"rows": 5,
"placeholder": "Dienstag",
}
),
)
wednesday_text = forms.CharField(
label=label_span("Mittwoch"),
max_length=300,
widget=forms.Textarea(
attrs={
"class": STYLE["text-input"],
"rows": 5,
"placeholder": "Mittwoch,",
}
),
)
thursday_text = forms.CharField(
label=label_span("Donnerstag"),
max_length=300,
widget=forms.Textarea(
attrs={
"class": STYLE["text-input"],
"rows": 5,
"placeholder": "Donnerstag",
}
),
)
friday_text = forms.CharField(
label=label_span("Freitag"),
max_length=300,
widget=forms.Textarea(
attrs={
"class": STYLE["text-input"],
"rows": 5,
"placeholder": "Freitag",
}
),
)
img = forms.ImageField(
label=label_span("Bild"),
required=False,
widget=forms.ClearableFileInput(
attrs={
"class": "block w-full p-2 text-sm text-gray-900 border border-gray-300 rounded-lg cursor-pointer bg-gray-50 dark:text-gray-400 focus:outline-none dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400",
"accept": "image/*",
}
),
)
@property
def display_names(self) -> dict:
return {
"department": self.fields["department"].label,
"week_topic": self.fields["week_topic"].label,
"monday_text": self.fields["monday_text"].label,
"tuesday_text": self.fields["tuesday_text"].label,
"wednesday_text": self.fields["wednesday_text"].label,
"thursday_text": self.fields["thursday_text"].label,
"friday_text": self.fields["friday_text"].label,
}
def content_values(self) -> dict:
if self.is_valid():
return {
"week_topic": self.cleaned_data["week_topic"],
"monday_text": self.cleaned_data["monday_text"],
"tuesday_text": self.cleaned_data["tuesday_text"],
"wednesday_text": self.cleaned_data["wednesday_text"],
"thursday_text": self.cleaned_data["thursday_text"],
"friday_text": self.cleaned_data["friday_text"],
}
def choose_report_kind(kind, request=None, files=None):
match kind:
case "weekly":
return WeeklyReport(request, files)
case "daily":
return DailyReport(request, files)

View file

@ -3,6 +3,7 @@
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",
"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",
"text-input": "w-full p-2 border border-gray-300 rounded-lg focus:ring focus:ring-blue-400 mb-5",
}

View file

@ -2,7 +2,7 @@
{% if late_reports > 1 %}
<div class="bg-yellow-100 border-l-4 border-yellow-500 text-yellow-700 p-4 mr-10 ml-10 mt-6" role="alert">
<div class="bg-yellow-100 border-l-4 border-yellow-500 text-yellow-700 p-4 mr-10 ml-10 mt-2" role="alert">
<p class="font-bold"> Du bist nicht aktuell! </p>
<p> Du hast noch {{ late_reports }} Berichtshefte nachzuschreiben. </p>
</div>

View file

@ -16,13 +16,26 @@
<div class="border-t border-gray-200 mt-4 pt-4">
<h2 class="text-lg font-medium text-gray-800 mb-2">Content:</h2>
<dl class="space-y-2">
{% load markdown %}
<div class="flex items-start space-x-4 justify-between">
<div>
{% load access markdown %}
{% for key, value in report.content.items %}
<div class="justify-between items-start">
<h4 class="text-sm font-medium text-gray-600">{{ key }}</h4>
<span class="text-sm text-gray-800 mb-4">{{ value|markdown|safe }}</span>
<h4 class="text-sm font-medium text-gray-600">{{ form.display_names|access:key|safe }}</h4>
<div class="text-sm text-gray-800 mb-4">{{ value|markdown|safe }}</div>
</div>
{% endfor %}
</div>
{% if report.image is not None %}
{% load b64 %}
<div class="w-36">
<img src="data:image/*;base64,{{ report.image|b64 }}" width="128"
class="-mt-8 w-32 h-32 border-2 border-black cursor-pointer rounded-lg shadow-lg hover:shadow-2xl transition-all duration-300">
</div>
{% endif %}
</div>
</dl>
</div>
</main>

View file

@ -8,7 +8,7 @@
<!-- Main wrapper that will hold the header, sidebar, and content -->
<div class="flex flex-col min-h-screen">
<header class="w-full bg-red-600 text-white flex py-4 shadow-md max-h-20 h-full max-h-20">
<button id="menu-toggle" class="text-2xl focus:outline-none ml-5">
<button id="menu-toggle" class="text-2xl focus:outline-none ml-5" onclick="toggleSidepanel()">
&#9776;
</button>
@ -39,10 +39,8 @@
<!-- Script to toggle the sidebar -->
<script>
const menuToggle = document.getElementById('menu-toggle');
const sidepanel = document.getElementById('sidepanel');
function toggleSidepanel() {
const sidepanel = document.getElementById('sidepanel');
if (sidepanel.classList.contains('w-0')) {
sidepanel.classList.remove('w-0');
sidepanel.classList.add('w-60');
@ -59,8 +57,6 @@
});
}
}
menuToggle.addEventListener('click', () => toggleSidepanel());
</script>
</body>

View file

@ -12,7 +12,7 @@
</p>
</div>
<form method="post" class="space-y-4">
<form method="post" class="space-y-4" enctype="multipart/form-data">
{% load access set_content %}
@ -24,7 +24,11 @@
{% if field.id_for_label == "id_department" %}
{% if draft is not None %}
{{ field|set_content:draft.department }}
{% else %}
{{ field }}
{% endif %}
{% else %}
{% with content=draft.content|access:field.id_for_label %}

11
core/templatetags/b64.py Normal file
View file

@ -0,0 +1,11 @@
from django import template
import base64
register = template.Library()
@register.filter(name="b64")
def b64(value):
if value is None:
return ""
return base64.b64encode(value).decode("utf-8")

View file

@ -7,4 +7,6 @@ register = template.Library()
@register.filter(name="markdown")
def markdown_tag(value):
return markdown.markdown(bleach.clean(value, tags=[], attributes=[]))
return markdown.markdown(
bleach.clean(value, tags=[], attributes=[]), extensions=["nl2br"]
)

View file

@ -8,6 +8,7 @@ from django.core.paginator import Paginator
from django.template.loader import render_to_string
from core.styles import STYLE
from .util import is_htmx_request, title, htmx_request
from .reports import choose_report_kind
import datetime
import json
@ -24,7 +25,7 @@ def write_new_report(request):
def write_new_report_post(request):
user = AzureUser(request)
report_form = user.get_report_kind_form(request.POST)
report_form = user.get_report_kind_form(request.POST, request.FILES)
if not report_form.is_valid():
return HttpResponse("Bad Request", status=400)
@ -35,6 +36,9 @@ def write_new_report_post(request):
user.reports().order_by("-year", "-week").first()
)
upload = report_form.cleaned_data["img"]
img = upload.read() if upload is not None else None
report = Berichtsheft(
user=user.id,
kind=user.get_report_kind(),
@ -43,8 +47,13 @@ def write_new_report_post(request):
week=int(current_week),
department=report_form.cleaned_data["department"],
content=report_form.content_values(),
image=img,
)
report.save()
# Clear draft
BerichtsheftDraft.objects.filter(user=user.id).delete()
return redirect("/")
@ -159,12 +168,16 @@ def report_detail_page(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)
return htmx_request(
request, "report.html", {"report": report}, f"Berichtsheft {report.num}"
request,
"report.html",
{"report": report, "form": form},
f"Berichtsheft {report.num}",
)

View file

@ -2,3 +2,4 @@ Django~=4.2.11
psycopg2
markdown
bleach
pillow