diff --git a/lunes_cms/cmsv2/admin.py b/lunes_cms/cmsv2/admin.py index 60909df7..6505f7b5 100644 --- a/lunes_cms/cmsv2/admin.py +++ b/lunes_cms/cmsv2/admin.py @@ -5,12 +5,96 @@ from __future__ import absolute_import, unicode_literals -from django.contrib import admin +from datetime import datetime +from typing import Any + +from django.contrib import admin, messages from django.contrib.auth.models import User +from django.http import HttpRequest, HttpResponse +from django.shortcuts import redirect +from django.template.response import TemplateResponse +from django.utils.translation import gettext_lazy as _ + +from lunes_cms.core import settings from .admins import FeedbackAdmin, JobAdmin, LunesUserAdmin, UnitAdmin, WordAdmin -from .models import Feedback, Job, Unit, Word +from .models import Feedback, Job, Review, Unit, Word +from .models.static import ReviewStatus, Roles + +_default_index = admin.AdminSite.index + + +def index( + self: admin.AdminSite, + request: HttpRequest, + extra_context: dict[str, Any] | None = None, +) -> HttpResponse: + """ + Renders an empty dashboard for members of :attr:`Roles.EXPERT_GROUP` + instead of Jazzmin's default app-list dashboard. + + :param self: A handle to the :class:`admin.AdminSite` + :param request: current user request + :param extra_context: additional context forwarded to the default dashboard + + :return: the rendered dashboard response + """ + if request.user.groups.filter(name=Roles.EXPERT_GROUP).exists(): + reviews = Review.objects.filter( + reviewer=request.user, completed_at__isnull=True + ).order_by("assigned_at") + review_id = request.GET.get("review") + current_review = ( + reviews.filter(pk=review_id).first() if review_id else None + ) or reviews.first() + next_review = None + if current_review: + next_review = reviews.filter( + assigned_at__gt=current_review.assigned_at + ).first() + context = { + **self.each_context(request), + "reviews": reviews, + "current_review": current_review, + "next_review": next_review, + "base_url": settings.MEDIA_URL, + **(extra_context or {}), + } + + if current_review: + if request.POST.get("approve"): + current_review.review_status = ReviewStatus.APPROVED + current_review.completed_at = datetime.now() + current_review.save() + messages.success( + request, + (_("The word {} has been approved.").format(current_review.word)), + ) + if next_review is not None: + return redirect(f"{request.path}?review={next_review.id}") + return TemplateResponse(request, "admin/index_empty.html", context) + + if request.POST.get("reject"): + current_review.review_status = ReviewStatus.REJECTED + current_review.completed_at = datetime.now() + current_review.save() + if next_review is not None: + return redirect(f"{request.path}?review={next_review.id}") + return TemplateResponse(request, "admin/index_empty.html", context) + + if request.POST.get("not_assessed"): + current_review.review_status = ReviewStatus.CANNOT_BE_ASSESSED + current_review.completed_at = datetime.now() + current_review.save() + if next_review is not None: + return redirect(f"{request.path}?review={next_review.id}") + return TemplateResponse(request, "admin/index_empty.html", context) + + return TemplateResponse(request, "admin/index_empty.html", context) + return _default_index(self, request, extra_context) + +admin.AdminSite.index = index # type: ignore[method-assign,assignment] admin.site.register(Job, JobAdmin) admin.site.register(Unit, UnitAdmin) admin.site.register(Word, WordAdmin) diff --git a/lunes_cms/cmsv2/models/static.py b/lunes_cms/cmsv2/models/static.py index 7a0c7c87..da258ed9 100644 --- a/lunes_cms/cmsv2/models/static.py +++ b/lunes_cms/cmsv2/models/static.py @@ -82,6 +82,7 @@ class Roles: DEFAULT_GROUP_NAME = None ADMIN_GROUP = "Lunes" REVIEWER_GROUP = _("Reviewer") + EXPERT_GROUP = "Lunes Expert:innen (v2)" class Permissions: diff --git a/lunes_cms/cmsv2/templates/admin/index_empty.html b/lunes_cms/cmsv2/templates/admin/index_empty.html new file mode 100644 index 00000000..c953f61c --- /dev/null +++ b/lunes_cms/cmsv2/templates/admin/index_empty.html @@ -0,0 +1,66 @@ +{% extends "admin/base_site.html" %} +{% load i18n %} +{% block content %} +
+

{% translate "Good day" %}, {{ user.get_full_name }}

+

{{ reviews|length }} {% translate "open reviews" %}

+ {% if reviews %} +
Aktuell zu prüfende Vokabel
+
+
{{ current_review.word.word }}, {{ current_review.word.singular_article_as_text }}
+
{% if current_review.word.word_type %}{{ current_review.word.get_word_type_display }} {% else %} {% translate "At the moment there is no word type for this word." %} •{% endif %} + {% if current_review.word.units %} + {% translate "Units" %}: + {% for unit in current_review.word.units.all %} + {{ unit }} + {% endfor %} + {% else %} + {% translate "This word hasn't been assigned to any units yet." %} + {% endif %} +
+
+
+ {% if current_review.word.image %} +
+ +
+ {% else %} + {% translate "There is no image for this word yet." %} + {% endif %} +
+
+ {% if current_review.word.audio %} +
+ + +
+ {% else %} + {% translate "There is no audio for this word yet." %} + {% endif %} +
+
+
+ {% csrf_token %} +
+ +
+
+ + + +
+
+
+ {% if next_review %} + {% translate "Show next word" %} + {% endif %} + {% else %} +

{% translate "Currently there are no words to check" %}

+ {% endif %} +
+{% endblock %} diff --git a/lunes_cms/core/audio.py b/lunes_cms/core/audio.py index 74407e0e..9008deeb 100644 --- a/lunes_cms/core/audio.py +++ b/lunes_cms/core/audio.py @@ -36,6 +36,20 @@ def probe_ffmpeg(path: str) -> None: run_ffmpeg("-v", "error", "-i", path, "-map", "0:a:0", "-f", "null", "-") +def generate_waveform_image(audio_path: str, output_path: str) -> None: + """Render a static waveform preview of an audio file to a PNG via ffmpeg.""" + run_ffmpeg( + "-y", + "-i", + audio_path, + "-filter_complex", + "showwavespic=s=600x120:colors=0d6efd", + "-frames:v", + "1", + output_path, + ) + + #: True-peak ceiling and loudness range used alongside the integrated #: loudness target (EBU R128 defaults, they rarely need tuning) _LOUDNORM_TRUE_PEAK = -1.5 diff --git a/lunes_cms/locale/de/LC_MESSAGES/django.po b/lunes_cms/locale/de/LC_MESSAGES/django.po index 7d401a63..1810789e 100644 --- a/lunes_cms/locale/de/LC_MESSAGES/django.po +++ b/lunes_cms/locale/de/LC_MESSAGES/django.po @@ -956,6 +956,7 @@ msgstr "Plural" #: cmsv2/admins/word_export_resource.py cmsv2/models/unit.py #: cmsv2/templates/admin/cmsv2/assign_units_to_user.html +#: cmsv2/templates/admin/index_empty.html msgid "Units" msgstr "Einheit" @@ -1242,6 +1243,62 @@ msgstr "Import starten" msgid "Generate Audio" msgstr "Audio generieren" +#: cmsv2/templates/admin/index_empty.html +msgid "Good day" +msgstr "" + +#: cmsv2/templates/admin/index_empty.html +#, fuzzy +#| msgid "Image Preview" +msgid "open reviews" +msgstr "Bild Vorschau" + +#: cmsv2/templates/admin/index_empty.html +msgid "At the moment there is no word type for this word." +msgstr "" + +#: cmsv2/templates/admin/index_empty.html +#, fuzzy +#| msgid "This word is assigned to no training set." +msgid "This word hasn't been assigned to any units yet." +msgstr "Das word ist zu keinem Modul zugeordnet." + +#: cmsv2/templates/admin/index_empty.html +#, fuzzy +#| msgid "No definition is provided for this word." +msgid "There is no image for this word yet." +msgstr "Für dieses Wort ist keine Definition hinterlegt." + +#: cmsv2/templates/admin/index_empty.html +msgid "There is no audio for this word yet." +msgstr "" + +#: cmsv2/templates/admin/index_empty.html +#, fuzzy +#| msgid "Approved" +msgid "Approve" +msgstr "Genehmigt" + +#: cmsv2/templates/admin/index_empty.html +msgid "Request change" +msgstr "" + +#: cmsv2/templates/admin/index_empty.html +msgid "Reject" +msgstr "" + +#: cmsv2/templates/admin/index_empty.html +msgid "Can't be assessed" +msgstr "" + +#: cmsv2/templates/admin/index_empty.html +msgid "Show next word" +msgstr "" + +#: cmsv2/templates/admin/index_empty.html +msgid "Currently there are no words to check" +msgstr "" + #: cmsv2/utils.py msgid "Generate example sentence" msgstr "Beispielsatz generieren" diff --git a/lunes_cms/static/css/corporate_identity.css b/lunes_cms/static/css/corporate_identity.css index aaee65b8..f261e70e 100644 --- a/lunes_cms/static/css/corporate_identity.css +++ b/lunes_cms/static/css/corporate_identity.css @@ -1,3 +1,7 @@ +:root{ + --success: #0adb75; +} + /*primary buttons*/ .btn-primary { background-color: #000e38; @@ -257,6 +261,14 @@ fieldset.module { width: 100%; } +.mw-48 { + max-width: 48rem; +} + +.border-green { + border-color: #0adb75; +} + .related-widget-wrapper { display: flex; align-items: stretch;