Skip to content
Snippets Groups Projects
Commit f75031e3 authored by Jonathan Weth's avatar Jonathan Weth :keyboard:
Browse files

Merge branch '1-not-mensa-specific' into 'master'

Resolve "Not mensa-specific"

Closes #1 and #2

See merge request AlekSIS/onboarding/AlekSIS-App-Resint!14
parents a41e41c2 691131e7
No related branches found
No related tags found
1 merge request!14Resolve "Not mensa-specific"
Pipeline #24567 passed
{% extends "core/base.html" %}
{% load static i18n rules %}
{% block page_title %}
{% trans "Posters" %}
{% endblock %}
{% block browser_title %}
{% trans "Posters" %}
{% endblock %}
{% block content %}
<div class="row">
{% for group in poster_groups %}
<div class="col s12 m6 l4 xl3">
<div class="card">
<div class="card-content">
<div class="card-title">{{ group.name }}</div>
{% with current_poster=group.current_poster %}
{% if current_poster %}
<p class="margin-bottom">
<i class="material-icons left">picture_as_pdf</i>
<a href="{{ current_poster.pdf.url }}" target="_blank">
{% blocktrans with week=current_poster.week year=current_poster.year %}
Week {{ week }}/{{ year }}
{% endblocktrans %}
</a>
</p>
<p>
<i class="material-icons left">schedule</i>
{{ current_poster.valid_from }}–{{ current_poster.valid_to }}
</p>
{% else %}
<p>
<i class="material-icons left">picture_as_pdf</i>
{% trans "There is no poster for this week." %}
</p>
{% endif %}
{% endwith %}
</div>
<div class="card-action">
<a href="{% url "poster_show_current" group.slug %}" target="_blank">
{% trans "Show current PDF" %}
</a>
</div>
</div>
</div>
{% endfor %}
</div>
{% has_perm "resint.upload_poster_rule" user as can_upload_poster %}
{% if can_upload_poster %}
<a class="waves-effect waves-light btn green right" href="{% url "poster_upload" %}">
<i class="material-icons left">add</i>
{% trans "Upload new poster" %}
</a>
{% endif %}
<h2>{% trans "All uploaded posters" %}</h2>
<table>
<thead>
<tr>
<th>{% trans "Group" %}</th>
<th>{% trans "Week" %}</th>
<th>{% trans "Valid from ... to" %}</th>
<th>{% trans "Actions" %}</th>
</tr>
</thead>
{% for poster in poster_list %}
<tr>
<td>{{ poster.group }}</td>
<td>{{ poster.week }}/{{ poster.year }}</td>
<td>{{ poster.valid_from }}–{{ poster.valid_to }}</td>
<td>
<a class="btn-flat waves-effect waves-green" href="{{ poster.pdf.url }}" target="_blank">
<i class="material-icons left">picture_as_pdf</i> {% trans "Show" %}
</a>
{% has_perm "resint.edit_poster_rule" user poster as can_edit_poster %}
{% if can_edit_poster %}
<a class="btn-flat orange-text waves-effect waves-orange" href="{% url "poster_edit" poster.id %}">
<i class="material-icons left">edit</i> {% trans "Edit" %}
</a>
{% endif %}
{% has_perm "resint.delete_poster_rule" user poster as can_delete_poster %}
{% if can_delete_poster %}
<a class="btn-flat red-text waves-effect waves-red" href="{% url "poster_delete" poster.id %}">
<i class="material-icons left">delete</i> {% trans "Delete" %}
</a>
{% endif %}
</td>
</tr>
{% endfor %}
</table>
{% endblock %}
{% extends 'core/base.html' %}
{% load material_form i18n %}
{% block page_title %}
{% trans "Upload poster" %}
{% endblock %}
{% block browser_title %}
{% trans "Upload poster" %}
{% endblock %}
{% block content %}
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{% form form=form %}{% endform %}
<button type="submit" class="waves-effect waves-light btn green">
<i class="material-icons left">save</i>{% blocktrans %}Upload poster{% endblocktrans %}
</button>
</form>
{% endblock %}
{% extends "core/base.html" %}
{% load msg_box i18n material_form %}
{% block page_title %}{% trans "Upload poster" %}{% endblock %}
{% block content %}
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{% form form=form %}{% endform %}
<button class="waves-effect waves-light btn green" type="submit">
<i class="material-icons left">cloud_upload</i>
{% trans "Upload and publish poster" %}
</button>
</form>
<p>
<a href="{% url 'poster_index' %}" class="waves-effect waves-teal btn-flat">{% trans "Back to overview" %}</a>
</p>
{% endblock %}
from django.urls import path
from . import views
from .views import (
PosterCurrentView,
PosterDeleteView,
PosterEditView,
PosterGroupCreateView,
PosterGroupDeleteView,
PosterGroupEditView,
PosterGroupListView,
PosterListView,
PosterUploadView,
)
urlpatterns = [
path('', views.index, name="poster_index"),
path('upload/', views.upload, name="poster_upload"),
path('delete/<int:id>', views.delete, name="poster_delete"),
path('current.pdf', views.show_current, name="poster_show_current"),
path('<str:msg>', views.index, name="poster_index_msg"),
path("", PosterListView.as_view(), name="poster_index"),
path("upload/", PosterUploadView.as_view(), name="poster_upload"),
path("<int:pk>/edit/", PosterEditView.as_view(), name="poster_edit"),
path("<int:pk>/delete/", PosterDeleteView.as_view(), name="poster_delete"),
path("<str:slug>.pdf", PosterCurrentView.as_view(), name="poster_show_current"),
path("groups/", PosterGroupListView.as_view(), name="poster_group_list"),
path("groups/create/", PosterGroupCreateView.as_view(), name="create_poster_group"),
path("groups/<int:pk>/edit/", PosterGroupEditView.as_view(), name="edit_poster_group"),
path("groups/<int:pk>/delete/", PosterGroupDeleteView.as_view(), name="delete_poster_group"),
]
import os
from typing import Any, Dict
from django.conf import settings
from django.contrib.auth.decorators import login_required, permission_required
from django.http import FileResponse
from django.shortcuts import get_object_or_404, redirect, render
from django.utils import timezone
from django.utils.translation import gettext_lazy as _
from django.db.models import QuerySet
from django.http import FileResponse, HttpRequest
from django.urls import reverse_lazy
from django.utils.translation import gettext as _
from django.views import View
from django.views.generic.detail import SingleObjectMixin
from django.views.generic.list import ListView
from calendarweek import CalendarWeek
from constance import config
from guardian.shortcuts import get_objects_for_user
from rules.contrib.views import PermissionRequiredMixin
from aleksis.core.util import messages
from aleksis.core.mixins import AdvancedCreateView, AdvancedDeleteView, AdvancedEditView
from .forms import PosterUploadForm
from .models import Poster
from .settings import BASE_DIR
from .forms import PosterGroupForm, PosterUploadForm
from .models import Poster, PosterGroup
@login_required
@permission_required("resint.add_poster")
def upload(request):
if request.method == 'POST':
form = PosterUploadForm(request.POST, request.FILES)
if form.is_valid():
form.save()
class PosterGroupListView(PermissionRequiredMixin, ListView):
"""Show a list of all poster groups."""
messages.success(request, _("The poster was uploaded successfully."))
return redirect('poster_index')
else:
form = PosterUploadForm()
return render(request, 'resint/upload.html', {
'form': form
})
template_name = "resint/group/list.html"
model = PosterGroup
permission_required = "resint.view_postergroups_rule"
def get_queryset(self) -> QuerySet:
qs = super().get_queryset()
if self.request.user.has_perm("resint.view_postergroup"):
return qs
return get_objects_for_user(self.request.user, "resint.view_postergroup", qs)
@login_required
@permission_required("resint.add_poster")
def delete(request, id):
poster = get_object_or_404(Poster, pk=id)
poster.delete()
messages.success(request, _("The poster was deleted successfully."))
return redirect("poster_index")
class PosterGroupCreateView(PermissionRequiredMixin, AdvancedCreateView):
"""Create a new poster group."""
model = PosterGroup
success_url = reverse_lazy("poster_group_list")
template_name = "resint/group/create.html"
success_message = _("The poster group has been saved.")
form_class = PosterGroupForm
permission_required = "resint.create_postergroup_rule"
@login_required
@permission_required("poster.add_poster")
def index(request):
posters = Poster.objects.all().order_by("calendar_week", "year")
return render(request, 'resint/index.html', {"posters": posters})
class PosterGroupEditView(PermissionRequiredMixin, AdvancedEditView):
"""Edit an existing poster group."""
def return_pdf(filename):
"""Read and response a PDF file"""
model = PosterGroup
success_url = reverse_lazy("poster_group_list")
template_name = "resint/group/edit.html"
success_message = _("The poster group has been saved.")
form_class = PosterGroupForm
permission_required = "resint.edit_postergroup_rule"
file = open(filename, "rb")
return FileResponse(file, content_type="application/pdf")
class PosterGroupDeleteView(PermissionRequiredMixin, AdvancedDeleteView):
"""Delete a poster group."""
def return_default_pdf():
"""Response the default PDF"""
model = PosterGroup
success_url = reverse_lazy("poster_group_list")
success_message = _("The poster group has been deleted.")
template_name = "core/pages/delete.html"
permission_required = "resint.delete_postergroup_rule"
return return_pdf(os.path.join(BASE_DIR, "default.pdf"))
class PosterListView(PermissionRequiredMixin, ListView):
"""Show a list of all uploaded posters."""
def show_current(request):
# Get current date with year and calendar week
current_date = timezone.datetime.now()
cw = CalendarWeek.from_date(current_date)
template_name = "resint/poster/list.html"
model = Poster
permission_required = "resint.view_posters_rule"
# Create datetime with the friday of the week and the toggle time
friday = cw[int(config.RESINT_NEW_WEEK_DAY)]
friday = timezone.datetime.combine(friday, config.RESINT_NEW_WEEK_TIME)
def get_queryset(self) -> QuerySet:
qs = Poster.objects.all().order_by("-year", "-week")
# Check whether to show the poster of the next week or the current week
if current_date > friday:
cw += 1
if self.request.user.has_perm("resint.view_poster"):
return qs
# Look for matching PDF in DB
try:
obj = Poster.objects.get(year=cw.year, calendar_week=cw.week)
return return_pdf(os.path.join(settings.MEDIA_ROOT, str(obj.pdf)))
allowed_groups = get_objects_for_user(
self.request.user, "resint.view_poster_of_group", PosterGroup
)
posters = get_objects_for_user(self.request.user, "resint.view_poster", qs)
return qs.filter(group__in=allowed_groups) | posters
# Or show the default PDF
except Poster.DoesNotExist:
return return_default_pdf()
def get_context_data(self, **kwargs: Any) -> Dict[str, Any]:
context = super().get_context_data(**kwargs)
context["poster_groups"] = PosterGroup.objects.all().order_by("name")
return context
class PosterUploadView(PermissionRequiredMixin, AdvancedCreateView):
"""Upload a new poster."""
model = Poster
success_url = reverse_lazy("poster_index")
template_name = "resint/poster/upload.html"
success_message = _("The poster has been uploaded.")
form_class = PosterUploadForm
permission_required = "resint.upload_poster_rule"
def get_form_kwargs(self) -> Dict[str, Any]:
return {"request": self.request}
class PosterEditView(PermissionRequiredMixin, AdvancedEditView):
"""Edit an uploaded poster."""
model = Poster
success_url = reverse_lazy("poster_index")
template_name = "resint/poster/edit.html"
success_message = _("The poster has been changed.")
form_class = PosterUploadForm
permission_required = "resint.edit_poster_rule"
def get_form_kwargs(self) -> Dict[str, Any]:
return {"request": self.request}
class PosterDeleteView(PermissionRequiredMixin, AdvancedDeleteView):
"""Delete an uploaded poster."""
model = Poster
success_url = reverse_lazy("poster_index")
success_message = _("The poster has been deleted.")
template_name = "core/pages/delete.html"
permission_required = "resint.delete_poster_rule"
class PosterCurrentView(PermissionRequiredMixin, SingleObjectMixin, View):
"""Show the poster which is currently valid."""
model = PosterGroup
permission_required = "resint.view_poster_pdf"
def get(self, request: HttpRequest, *args: Any, **kwargs: Any) -> FileResponse:
group = self.get_object()
current_poster = group.current_poster
file = current_poster.pdf if current_poster else group.default_pdf
return FileResponse(file, content_type="application/pdf")
This diff is collapsed.
[tool.poetry]
name = "AlekSIS-App-Resint"
version = "2.0a1"
version = "2.0b0"
packages = [
{ include = "aleksis" }
]
readme = "README.rst"
include = ["CHANGELOG.rst", "LICENCE.rst", "aleksis/**/*.mo"]
description = "AlekSIS (School Information System) — App Resint (Public poster)"
authors = ["Julian Leucker <leuckeju@katharineum.de>", "Jonathan Weth <wethjo@katharineum.de>"]
authors = [
"Julian Leucker <leuckeju@katharineum.de>",
"Frank Poetzsch-Heffter <p-h@katharineum.de>",
"Jonathan Weth <dev@jonathanweth.de>"
]
license = "EUPL-1.2"
homepage = "https://aleksis.edugit.io/"
repository = "https://edugit.org/AlekSIS/AlekSIS-App-resint"
documentation = "https://aleksis.edugit.io/AlekSIS/docs/html/"
repository = "https://edugit.org/AlekSIS/onboarding/AlekSIS-App-Resint"
classifiers = [
"Development Status :: 3 - Alpha",
"Environment :: Web Environment",
"Intended Audience :: Education",
"Topic :: Education"
......@@ -24,8 +28,8 @@ url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
secondary = true
[tool.poetry.dependencies]
python = "^3.7"
AlekSIS = "^2.0a2"
python = "^3.9"
AlekSIS-Core = "^2.0rc"
[tool.poetry.dev-dependencies]
aleksis-builddeps = "*"
......
......@@ -9,9 +9,11 @@ whitelist_externals = poetry
skip_install = true
envdir = {toxworkdir}/globalenv
commands_pre =
- poetry install
poetry install
poetry run aleksis-admin yarn install
poetry run aleksis-admin collectstatic --no-input
commands =
- poetry run pytest --cov=. {posargs} aleksis/
poetry run pytest --cov=. {posargs} aleksis/
[testenv:selenium]
setenv =
......@@ -33,6 +35,8 @@ commands =
[testenv:build]
commands_pre =
poetry install
poetry run sh -c "cd aleksis; aleksis-admin compilemessages"
commands = poetry build
[testenv:docs]
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment