diff --git a/README.rst b/README.rst index c3fb19c3020e7b9458cfc3498abfe5284d1a1b8e..d96f30822fc6b5fb69216d81c8711f0299f0125d 100644 --- a/README.rst +++ b/README.rst @@ -31,7 +31,7 @@ Licence Copyright © 2019 Tom Teichler <tom.teichler@teckids.org> Copyright © 2019 Hangzhi Yu <yuha@katharineum.de> - Licenced under the EUPL, version 1.2 or later + Licenced under the EUPL, version 1.2 or later, by Teckids e.V. (Bonn, Germany). Please see the LICENCE.rst file accompanying this distribution for the full licence text or on the `European Union Public Licence`_ website diff --git a/aleksis/apps/chronos/__init__.py b/aleksis/apps/chronos/__init__.py index bed63d688b366a2a7a5ea587652167d345823993..482bbcdf18b41d99b02aa17beb45d5560060bee9 100644 --- a/aleksis/apps/chronos/__init__.py +++ b/aleksis/apps/chronos/__init__.py @@ -4,5 +4,3 @@ try: __version__ = pkg_resources.get_distribution("AlekSIS-App-Chronos").version except Exception: __version__ = "unknown" - -default_app_config = "aleksis.apps.chronos.apps.ChronosConfig" diff --git a/aleksis/apps/chronos/migrations/0001_initial.py b/aleksis/apps/chronos/migrations/0001_initial.py index 5d6f1b8f87925290f2319a8ac4805b7526504322..1b1c0ee95b474e02006d6d7c983bc3619176fca7 100644 --- a/aleksis/apps/chronos/migrations/0001_initial.py +++ b/aleksis/apps/chronos/migrations/0001_initial.py @@ -26,7 +26,7 @@ class Migration(migrations.Migration): fields=[ ( "id", - models.AutoField( + models.BigAutoField( auto_created=True, primary_key=True, serialize=False, @@ -35,7 +35,7 @@ class Migration(migrations.Migration): ), ( "extended_data", - django.contrib.postgres.fields.jsonb.JSONField( + models.JSONField( default=dict, editable=False ), ), @@ -56,7 +56,7 @@ class Migration(migrations.Migration): fields=[ ( "id", - models.AutoField( + models.BigAutoField( auto_created=True, primary_key=True, serialize=False, @@ -65,7 +65,7 @@ class Migration(migrations.Migration): ), ( "extended_data", - django.contrib.postgres.fields.jsonb.JSONField( + models.JSONField( default=dict, editable=False ), ), @@ -87,7 +87,7 @@ class Migration(migrations.Migration): fields=[ ( "id", - models.AutoField( + models.BigAutoField( auto_created=True, primary_key=True, serialize=False, @@ -96,7 +96,7 @@ class Migration(migrations.Migration): ), ( "extended_data", - django.contrib.postgres.fields.jsonb.JSONField( + models.JSONField( default=dict, editable=False ), ), @@ -126,7 +126,7 @@ class Migration(migrations.Migration): fields=[ ( "id", - models.AutoField( + models.BigAutoField( auto_created=True, primary_key=True, serialize=False, @@ -135,7 +135,7 @@ class Migration(migrations.Migration): ), ( "extended_data", - django.contrib.postgres.fields.jsonb.JSONField( + models.JSONField( default=dict, editable=False ), ), @@ -165,7 +165,7 @@ class Migration(migrations.Migration): fields=[ ( "id", - models.AutoField( + models.BigAutoField( auto_created=True, primary_key=True, serialize=False, @@ -174,7 +174,7 @@ class Migration(migrations.Migration): ), ( "extended_data", - django.contrib.postgres.fields.jsonb.JSONField( + models.JSONField( default=dict, editable=False ), ), @@ -202,7 +202,7 @@ class Migration(migrations.Migration): fields=[ ( "id", - models.AutoField( + models.BigAutoField( auto_created=True, primary_key=True, serialize=False, @@ -211,7 +211,7 @@ class Migration(migrations.Migration): ), ( "extended_data", - django.contrib.postgres.fields.jsonb.JSONField( + models.JSONField( default=dict, editable=False ), ), @@ -258,7 +258,7 @@ class Migration(migrations.Migration): fields=[ ( "id", - models.AutoField( + models.BigAutoField( auto_created=True, primary_key=True, serialize=False, @@ -267,7 +267,7 @@ class Migration(migrations.Migration): ), ( "extended_data", - django.contrib.postgres.fields.jsonb.JSONField( + models.JSONField( default=dict, editable=False ), ), @@ -312,7 +312,7 @@ class Migration(migrations.Migration): fields=[ ( "id", - models.AutoField( + models.BigAutoField( auto_created=True, primary_key=True, serialize=False, @@ -321,7 +321,7 @@ class Migration(migrations.Migration): ), ( "extended_data", - django.contrib.postgres.fields.jsonb.JSONField( + models.JSONField( default=dict, editable=False ), ), @@ -400,7 +400,7 @@ class Migration(migrations.Migration): fields=[ ( "id", - models.AutoField( + models.BigAutoField( auto_created=True, primary_key=True, serialize=False, @@ -409,7 +409,7 @@ class Migration(migrations.Migration): ), ( "extended_data", - django.contrib.postgres.fields.jsonb.JSONField( + models.JSONField( default=dict, editable=False ), ), @@ -465,7 +465,7 @@ class Migration(migrations.Migration): fields=[ ( "id", - models.AutoField( + models.BigAutoField( auto_created=True, primary_key=True, serialize=False, @@ -474,7 +474,7 @@ class Migration(migrations.Migration): ), ( "extended_data", - django.contrib.postgres.fields.jsonb.JSONField( + models.JSONField( default=dict, editable=False ), ), @@ -507,7 +507,7 @@ class Migration(migrations.Migration): fields=[ ( "id", - models.AutoField( + models.BigAutoField( auto_created=True, primary_key=True, serialize=False, @@ -516,7 +516,7 @@ class Migration(migrations.Migration): ), ( "extended_data", - django.contrib.postgres.fields.jsonb.JSONField( + models.JSONField( default=dict, editable=False ), ), @@ -676,7 +676,7 @@ class Migration(migrations.Migration): fields=[ ( "id", - models.AutoField( + models.BigAutoField( auto_created=True, primary_key=True, serialize=False, @@ -685,7 +685,7 @@ class Migration(migrations.Migration): ), ( "extended_data", - django.contrib.postgres.fields.jsonb.JSONField( + models.JSONField( default=dict, editable=False ), ), @@ -717,7 +717,7 @@ class Migration(migrations.Migration): fields=[ ( "id", - models.AutoField( + models.BigAutoField( auto_created=True, primary_key=True, serialize=False, @@ -726,7 +726,7 @@ class Migration(migrations.Migration): ), ( "extended_data", - django.contrib.postgres.fields.jsonb.JSONField( + models.JSONField( default=dict, editable=False ), ), @@ -808,7 +808,7 @@ class Migration(migrations.Migration): fields=[ ( "id", - models.AutoField( + models.BigAutoField( auto_created=True, primary_key=True, serialize=False, @@ -817,7 +817,7 @@ class Migration(migrations.Migration): ), ( "extended_data", - django.contrib.postgres.fields.jsonb.JSONField( + models.JSONField( default=dict, editable=False ), ), @@ -878,7 +878,7 @@ class Migration(migrations.Migration): fields=[ ( "id", - models.AutoField( + models.BigAutoField( auto_created=True, primary_key=True, serialize=False, @@ -887,7 +887,7 @@ class Migration(migrations.Migration): ), ( "extended_data", - django.contrib.postgres.fields.jsonb.JSONField( + models.JSONField( default=dict, editable=False ), ), @@ -995,7 +995,7 @@ class Migration(migrations.Migration): fields=[ ( "id", - models.AutoField( + models.BigAutoField( auto_created=True, primary_key=True, serialize=False, @@ -1004,7 +1004,7 @@ class Migration(migrations.Migration): ), ( "extended_data", - django.contrib.postgres.fields.jsonb.JSONField( + models.JSONField( default=dict, editable=False ), ), @@ -1039,7 +1039,7 @@ class Migration(migrations.Migration): fields=[ ( "id", - models.AutoField( + models.BigAutoField( auto_created=True, primary_key=True, serialize=False, @@ -1048,7 +1048,7 @@ class Migration(migrations.Migration): ), ( "extended_data", - django.contrib.postgres.fields.jsonb.JSONField( + models.JSONField( default=dict, editable=False ), ), diff --git a/aleksis/apps/chronos/migrations/0002_school_term_validity.py b/aleksis/apps/chronos/migrations/0002_school_term_validity.py index f890a222288307a8a7e93942fa43af2950724548..947ccad0a618cc00f2697d17c9d4f8e4d60b0d65 100644 --- a/aleksis/apps/chronos/migrations/0002_school_term_validity.py +++ b/aleksis/apps/chronos/migrations/0002_school_term_validity.py @@ -53,7 +53,7 @@ class Migration(migrations.Migration): fields=[ ( "id", - models.AutoField( + models.BigAutoField( auto_created=True, primary_key=True, serialize=False, @@ -62,7 +62,7 @@ class Migration(migrations.Migration): ), ( "extended_data", - django.contrib.postgres.fields.jsonb.JSONField( + models.JSONField( default=dict, editable=False ), ), 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..688c009f6fd3c9a1dbee9378cab422d69defd730 --- /dev/null +++ b/aleksis/apps/chronos/static/css/chronos/timetable_print.css @@ -0,0 +1,32 @@ +.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: 7px; +} diff --git a/aleksis/apps/chronos/templates/chronos/timetable.html b/aleksis/apps/chronos/templates/chronos/timetable.html index 07b8402d8c770ba225a16d6f563d8276f0827021..9201367e5373557fbd468e69403d77ccedf27edb 100644 --- a/aleksis/apps/chronos/templates/chronos/timetable.html +++ b/aleksis/apps/chronos/templates/chronos/timetable.html @@ -35,9 +35,8 @@ </h5> {% 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"> + <a class="waves-effect waves-teal btn-flat btn-flat-medium right hide-on-small-and-down" href="{% url "timetable_print" type.value pk %}" id="print"> <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 new file mode 100644 index 0000000000000000000000000000000000000000..4769673610019ad98538d11db579a9cbe6c4601c --- /dev/null +++ b/aleksis/apps/chronos/templates/chronos/timetable_print.html @@ -0,0 +1,55 @@ +{% 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 page_title %} + {% trans "Timetable" %} <i>{{ el.short_name }}</i> +{% endblock %} +{% block content %} + <div class="timetable-plan"> + {# Week days #} + <div class="row"> + <div class="col s2"> + + </div> + {% for weekday in weekdays_short %} + <div class="col s2"> + <div class="card timetable-title-card"> + <div class="card-content"> + <span class="card-title"> + {{ weekday.name }} + </span> + </div> + </div> + </div> + {% endfor %} + </div> + + {% for row in timetable %} + <div class="row"> + <div class="col s2"> + {% if row.type == "period" %} + {% include "chronos/partials/period_time.html" with period=row.period periods=periods %} + {% endif %} + </div> + + {% for col in row.cols %} + {# A lesson #} + <div class="col s2"> + {% if row.type == "period" %} + {% include "chronos/partials/elements.html" with elements=col %} + {% else %} + {% include "chronos/partials/supervision.html" with supervision=col %} + {% endif %} + </div> + {% endfor %} + </div> + {% endfor %} + </div> + +{% endblock %} diff --git a/aleksis/apps/chronos/urls.py b/aleksis/apps/chronos/urls.py index 666183ec6a43d80b897b05643288a77ee8b615b7..fc6d9a868e871b303a6dd0e1b6a8cdfc50be63ed 100644 --- a/aleksis/apps/chronos/urls.py +++ b/aleksis/apps/chronos/urls.py @@ -16,6 +16,12 @@ urlpatterns = [ views.timetable, name="timetable_by_week", ), + path( + "timetable/<str:type_>/<int:pk>/print/", + views.timetable, + {"is_print": True}, + name="timetable_print", + ), path( "timetable/<str:type_>/<int:pk>/<str:regular>/", views.timetable, name="timetable_regular", ), 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 b90f0b0ccf8ed0143197e49a9d50af62e467b9f8..50ebab5bf75762dd21f072c4a7282f0d9b4e3e0e 100644 --- a/aleksis/apps/chronos/views.py +++ b/aleksis/apps/chronos/views.py @@ -15,6 +15,7 @@ from rules.contrib.views import permission_required from aleksis.core.models import Announcement, Group, Person from aleksis.core.util import messages from aleksis.core.util.core_helpers import get_site_preferences, has_person +from aleksis.core.util.pdf import render_pdf from .forms import LessonSubstitutionForm from .managers import TimetableType @@ -124,12 +125,16 @@ def timetable( year: Optional[int] = None, week: Optional[int] = None, regular: 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: @@ -185,7 +190,13 @@ def timetable( "timetable_by_week", args=[type_.value, pk, week_next.year, week_next.week] ) - return render(request, "chronos/timetable.html", context) + if is_print: + context["back_url"] = reverse( + "timetable_by_week", args=[type_.value, pk, wanted_week.year, wanted_week.week], + ) + return render_pdf(request, "chronos/timetable_print.html", context) + else: + return render(request, "chronos/timetable.html", context) @permission_required("chronos.view_lessons_day") @@ -346,9 +357,8 @@ def substitutions( wanted_day, "substitutions_by_date" ) - template_name = "chronos/substitutions.html" + return render(request, "chronos/substitutions.html", context) else: context["days"] = day_contexts - template_name = "chronos/substitutions_print.html" - return render(request, template_name, context) + return render_pdf(request, "chronos/substitutions_print.html", context)