image feature + refactor
This commit is contained in:
parent
73e019de9a
commit
d64efb663a
13 changed files with 221 additions and 37 deletions
|
@ -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()
|
||||
|
|
17
core/migrations/0012_berichtsheft_image.py
Normal file
17
core/migrations/0012_berichtsheft_image.py
Normal 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),
|
||||
),
|
||||
]
|
|
@ -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}"
|
||||
|
|
155
core/reports.py
155
core/reports.py
|
@ -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)
|
||||
|
|
|
@ -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",
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -12,17 +12,30 @@
|
|||
</span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
||||
<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>
|
||||
|
|
|
@ -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()">
|
||||
☰
|
||||
</button>
|
||||
|
||||
|
@ -38,11 +38,9 @@
|
|||
</div>
|
||||
|
||||
<!-- Script to toggle the sidebar -->
|
||||
<script>
|
||||
const menuToggle = document.getElementById('menu-toggle');
|
||||
const sidepanel = document.getElementById('sidepanel');
|
||||
|
||||
<script>
|
||||
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>
|
||||
|
|
|
@ -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
11
core/templatetags/b64.py
Normal 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")
|
|
@ -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"]
|
||||
)
|
||||
|
|
|
@ -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}",
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
Django~=4.2.11
|
||||
psycopg2
|
||||
markdown
|
||||
bleach
|
||||
bleach
|
||||
pillow
|
Loading…
Add table
Reference in a new issue