diff --git a/aleksis/apps/chronos/static/css/chronos/timetable.css b/aleksis/apps/chronos/static/css/chronos/timetable.css index bbdbb5b82c664ca908c2cd2088188c0a64395716..cf1e474d766b78bb0765ef7a03a94faebd89cdfe 100644 --- a/aleksis/apps/chronos/static/css/chronos/timetable.css +++ b/aleksis/apps/chronos/static/css/chronos/timetable.css @@ -81,7 +81,7 @@ table.substitutions td, table.substitutions th { } .lesson-with-sub { - border: 3px solid red; + border: 3px solid #ff0000; border-radius: 3px; } diff --git a/aleksis/apps/chronos/static/css/chronos/timetable_print.css b/aleksis/apps/chronos/static/css/chronos/timetable_print.css new file mode 100644 index 0000000000000000000000000000000000000000..429aee5677ac806fb602c11acfd5c73496e46015 --- /dev/null +++ b/aleksis/apps/chronos/static/css/chronos/timetable_print.css @@ -0,0 +1,38 @@ +body { + font-family: sans-serif !important; +} + +.timetable-plan .row, .timetable-plan .col { + display: flex; + padding: 0rem; +} + +.timetable-plan .row { + margin-bottom: 0rem; +} + +.lesson-card, .timetable-title-card { + margin: 0; + display: flex; + flex-grow: 1; + min-height: 40px; + box-shadow: none; + border: 1px solid black; + margin-right: -1px; + margin-top: -1px; + border-radius: 0px; + font-size: 11px; +} +.lesson-card .card-content > div { + padding: 1px; +} + +.card .card-title { + font-size: 18px; +} + +.timetable-title-card .card-content { + padding: 10px; +} + + diff --git a/aleksis/apps/chronos/templates/chronos/timetable.html b/aleksis/apps/chronos/templates/chronos/timetable.html index 07b8402d8c770ba225a16d6f563d8276f0827021..3c5722d4766797201c51672df75c44b1313ee5b9 100644 --- a/aleksis/apps/chronos/templates/chronos/timetable.html +++ b/aleksis/apps/chronos/templates/chronos/timetable.html @@ -37,7 +37,7 @@ </div> {# Show print button only if not on mobile #} <div class="col s4 m6 l4 xl3 right align-right no-print"> - <a class="waves-effect waves-teal btn-flat btn-flat-medium right hide-on-small-and-down" id="print"> + <a class="waves-effect waves-teal btn-flat btn-flat-medium right hide-on-small-and-down" href="{% url "timetable_print" type.value pk %}"> <i class="material-icons center">print</i> </a> </div> diff --git a/aleksis/apps/chronos/templates/chronos/timetable_print.html b/aleksis/apps/chronos/templates/chronos/timetable_print.html index eb0d3e165bd64459ae5f0c8e98cac81af9566bac..1d287eb431c7e06d74e2683fd157efa1be505ffc 100644 --- a/aleksis/apps/chronos/templates/chronos/timetable_print.html +++ b/aleksis/apps/chronos/templates/chronos/timetable_print.html @@ -1,78 +1,29 @@ -{# -*- engine:django -*- #} - -{% extends 'core/base.html' %} +{% extends 'core/base_print.html' %} {% load data_helpers static i18n %} {% block extra_head %} <link rel="stylesheet" href="{% static 'css/chronos/timetable.css' %}"> + <link rel="stylesheet" href="{% static 'css/chronos/timetable_print.css' %}"> {% endblock %} -{% block browser_title %}{% blocktrans %}Timetable{% endblocktrans %}{% endblock %} {% block content %} - {% if smart %} - <script type="text/javascript" src="{% static "js/helper.js" %}"></script> - {{ week_select|json_script:"week_select" }} - <script type="text/javascript" src="{% static "js/chronos/week_select.js" %}"></script> - {% endif %} - <div class="row no-margin"> - <div class="col s8 m6 l8 xl9"> - <h4> + <h5> {% trans "Timetable" %} <i>{{ el }}</i> - </h4> + </h5> {# Show class teacher and deputy class teacher #} {% if type.value == "group" and el.owners.all %} - <h5>{% trans "Group teachers:" %} + <p class="no-margin">{% trans "Group teachers:" %} {% for teacher in el.owners.all %} - <span data-position="bottom" class="tooltipped" - data-tooltip="{{ teacher }}"> - <a href="{% url "timetable" "teacher" teacher.pk %}"> - {{ teacher.short_name }}</a></span>{% if not forloop.last %},{% endif %} + {{ teacher.short_name }}{% if not forloop.last %},{% endif %} {% endfor %} - </h5> + </p> {% endif %} - </div> - {# Show print button only if not on mobile #} - <div class="col s4 m6 l4 xl3 right align-right no-print"> - <a class="waves-effect waves-teal btn-flat btn-flat-medium right hide-on-small-and-down" id="print"> - <i class="material-icons center">print</i> - </a> - </div> - </div> - <div class="row"> - {% if smart %} - {# Show if smart #} - {# Toggle button to regular and smart plan badge #} - <div class="row s12 m6 left"> - <span class="badge new primary-color left smart-plan-badge">{% trans "SMART PLAN" %}</span> - - <a class="waves-effect waves-light btn-flat no-print" - href="{% url "timetable_regular" type.value pk "regular" %}"> - <i class="material-icons left">slideshow</i> - {% trans "Show regular timetable" %} - </a> - </div> - - {# Week select #} - {% include "chronos/partials/week_select.html" with wanted_week=week %} - - {% else %} - {# Show if regular #} - <a class="waves-effect waves-light btn-flat no-print" - href="{% url "timetable" type.value pk %}"> - <i class="material-icons left">slideshow</i> - {% trans "Show SMART PLAN" %} - </a> - {% endif %} - </div> - - {% include "core/partials/announcements.html" with announcements=announcements show_interval=1 %} - {# show full timetable on tablets, laptops and pcs #} - <div class="timetable-plan hide-on-small-and-down"> + <div class="timetable-plan"> {# Week days #} <div class="row"> @@ -81,45 +32,19 @@ </div> {# Show short weekdays on tablets #} {% for weekday in weekdays_short %} - <div class="col s2 hide-on-large-only"> - <div class="card timetable-title-card"> - <div class="card-content"> - <span class="card-title"> - {{ weekday.name }} - </span> - {% if smart %} - {{ weekday.date }} - {% if weekday.holiday %} - <br/>{% include "chronos/partials/holiday.html" with holiday=weekday.holiday %} - {% endif %} - {% endif %} - </div> - </div> - </div> - {% endfor %} - - {# Show long weekdays elsewere #} - {% for weekday in weekdays %} - <div class="col s2 hide-on-med-only"> + <div class="col s2"> <div class="card timetable-title-card"> <div class="card-content"> <span class="card-title"> {{ weekday.name }} </span> - {% if smart %} - {{ weekday.date }} - {% if weekday.holiday %} - <br/>{% include "chronos/partials/holiday.html" with holiday=weekday.holiday %} - {% endif %} - {% endif %} </div> </div> </div> {% endfor %} </div> - {# Lessons #} - {% for row in timetable %} + {% for row in timetable %} <div class="row"> <div class="col s2"> {% if row.type == "period" %} @@ -141,42 +66,4 @@ {% endfor %} </div> - {# show 5 seperate ones on mobiles #} - <div class="timetable-plan hide-on-med-and-up"> - {% for weekday in weekdays %} - <div class="card timetable-mobile-title-card"> - <div class="card-content"> - <span class="card-title"> - {{ weekday.name }} - </span> - {% if smart %} - {{ weekday.date }} - {% if weekday.holiday %} - <br/>{% include "chronos/partials/holiday.html" with holiday=weekday.holiday %} - {% endif %} - {% endif %} - </div> - </div> - {% for row in timetable %} - <div class="row"> - <div class="col s4"> - {% include "chronos/partials/period_time.html" with period=row.period periods=periods %} - </div> - - {% for col in row.cols %} - {% if forloop.counter0 == weekday.key %} - <div class="col s8"> - {# A lesson #} - {% if row.type == "period" %} - {% include "chronos/partials/elements.html" with elements=col %} - {% else %} - {% include "chronos/partials/supervision.html" with supervision=col %} - {% endif %} - </div> - {% endif %} - {% endfor %} - </div> - {% endfor %} - {% endfor %} - </div> {% endblock %} diff --git a/aleksis/apps/chronos/urls.py b/aleksis/apps/chronos/urls.py index 22e97b8a6202424f9fc4d4596e15ed99f1713f91..685eacb2d052cea72968bebd956be8a564532aab 100644 --- a/aleksis/apps/chronos/urls.py +++ b/aleksis/apps/chronos/urls.py @@ -10,25 +10,26 @@ urlpatterns = [ views.my_timetable, name="my_timetable_by_date", ), - path("timetable/<str:type_>/<int:pk>/", views.timetable, name="timetable"), path( - "timetable/<str:type_>/<int:pk>/<int:year>/<int:week>/", + "timetable/<str:type_>/<int:pk>/", views.timetable, - name="timetable_by_week", + name="timetable", ), path( - "timetable/<str:type_>/<int:pk>/<str:regular>/", views.timetable, name="timetable_regular", + "timetable/<str:type_>/<int:pk>/<int:year>/<int:week>/", + views.timetable, + name="timetable_by_week", ), - path("timetable/<str:type_>/<int:pk>/<str:print_plan>/", views.timetable, name="timetable_print"), path( - "timetable/<str:type_>/<int:pk>/<int:year>/<int:week>/<str:print_plan>/", + "timetable/<str:type_>/<int:pk>/print/", views.timetable, - name="timetable_by_week_print", + {"is_print": True}, + name="timetable_print", ), path( - "timetable/<str:type_>/<int:pk>/<str:regular>/<str:print_plan>/", + "timetable/<str:type_>/<int:pk>/<str:regular>/", views.timetable, - name="timetable_regular_print", + name="timetable_regular", ), path("lessons/", views.lessons_day, name="lessons_day"), path( diff --git a/aleksis/apps/chronos/util/chronos_helpers.py b/aleksis/apps/chronos/util/chronos_helpers.py index 7f929ca8bd9dac7b0da1c79b63c7751ca9a3649b..797eefcd95a18d9a3a930f24e8a7c50af9c92642 100644 --- a/aleksis/apps/chronos/util/chronos_helpers.py +++ b/aleksis/apps/chronos/util/chronos_helpers.py @@ -17,6 +17,8 @@ def get_el_by_pk( week: Optional[int] = None, regular: Optional[str] = None, prefetch: bool = False, + *args, + **kwargs, ): if type_ == TimetableType.GROUP.value: return get_object_or_404( diff --git a/aleksis/apps/chronos/views.py b/aleksis/apps/chronos/views.py index 0629dd5e2426b4efcd9c2354e99dadbe848487ad..64b634f39149a04cc3bb77b28c5addb87b44305e 100644 --- a/aleksis/apps/chronos/views.py +++ b/aleksis/apps/chronos/views.py @@ -1,13 +1,17 @@ +import os from datetime import datetime, timedelta from typing import Optional +import subprocess +from tempfile import mkstemp from django.db.models import Count, Q -from django.http import HttpRequest, HttpResponse, HttpResponseNotFound +from django.http import HttpRequest, HttpResponse, HttpResponseNotFound, FileResponse from django.shortcuts import get_object_or_404, redirect, render from django.urls import reverse from django.utils import timezone from django.utils.translation import ugettext as _ from django.views.decorators.cache import never_cache +from django.template.loader import get_template, render_to_string from django_tables2 import RequestConfig from rules.contrib.views import permission_required @@ -24,6 +28,7 @@ from .util.build import build_substitutions_list, build_timetable, build_weekday from .util.chronos_helpers import get_el_by_pk, get_substitution_by_id from .util.date import CalendarWeek, get_weeks_for_year from .util.js import date_unix +from aleksis.core.settings import STATIC_ROOT @permission_required("chronos.view_timetable_overview") @@ -124,13 +129,16 @@ def timetable( year: Optional[int] = None, week: Optional[int] = None, regular: Optional[str] = None, - print_plan: Optional[str] = None, + is_print: bool = False, ) -> HttpResponse: """View a selected timetable for a person, group or room.""" context = {} is_smart = regular != "regular" + if is_print: + is_smart = False + el = get_el_by_pk(request, type_, pk, prefetch=True) if type(el) == HttpResponseNotFound: @@ -186,9 +194,23 @@ def timetable( "timetable_by_week", args=[type_.value, pk, week_next.year, week_next.week] ) - if print_plan == "print": - return render(request, "chronos/timetable_print.html", context) - return render(request, "chronos/timetable.html", context) + if is_print: + template_name = "chronos/timetable_print.html" + template = get_template(template_name) + + html_template = render_to_string(template_name, context).replace("/static", STATIC_ROOT) + f, path = mkstemp(".html") + print(path) + with open(path, "w") as f: + f.write(html_template) + f2, path2 = mkstemp(".pdf") + subprocess.run(["electron-pdf", path, path2]) + file = open(path2, "rb") + return FileResponse(file, content_type="application/pdf") + else: + template_name = "chronos/timetable.html" + + return render(request, template_name, context) @permission_required("chronos.view_lessons_day")