diff --git a/aleksis/apps/alsijil/apps.py b/aleksis/apps/alsijil/apps.py index dec1d99274f1511bd523a9c24239835f7d51386f..25aa203003ea54063a0e27fd531314e1ebb03b9b 100644 --- a/aleksis/apps/alsijil/apps.py +++ b/aleksis/apps/alsijil/apps.py @@ -9,7 +9,7 @@ class AlsijilConfig(AppConfig): "Repository": "https://edugit.org/AlekSIS/official/AlekSIS-App-Alsijil/", } licence = "EUPL-1.2+" - copyright = ( + copyright_info = ( ([2019], "Dominik George", "dominik.george@teckids.org"), ([2019], "mirabilos", "thorsten.glaser@teckids.org"), ([2019], "Tom Teichler", "tom.teichler@teckids.org"), diff --git a/aleksis/apps/alsijil/forms.py b/aleksis/apps/alsijil/forms.py index 350bf018d7a45c8d340d72df7309be9f19f699d2..48c809cdefa7847e630809e4aa10054029cabb5e 100644 --- a/aleksis/apps/alsijil/forms.py +++ b/aleksis/apps/alsijil/forms.py @@ -1,6 +1,8 @@ from datetime import datetime +from aleksis.apps.chronos.managers import TimetableType from django import forms +from django.core.exceptions import ValidationError from django.db.models import Count from django.utils.translation import gettext_lazy as _ @@ -50,15 +52,24 @@ class SelectForm(forms.Form): required=False, widget=Select2Widget, ) - room = forms.ModelChoiceField( - queryset=Room.objects.annotate(lessons_count=Count("lesson_periods")).filter( - lessons_count__gt=0 - ), - label=_("Room"), - required=False, - widget=Select2Widget, - ) + def clean(self) -> dict: + data = super().clean() + + if data.get("group") and not data.get("teacher") : + type_ = TimetableType.GROUP + instance = data["group"] + elif data.get("teacher") and not data.get("group"): + type_ = TimetableType.TEACHER + instance = data["teacher"] + elif not data.get("teacher") and not data.get("group"): + return data + else: + raise ValidationError(_("You can't select a group and a teacher both.")) + + data["type_"] = type_ + data["instance"] = instance + return data PersonalNoteFormSet = forms.modelformset_factory( PersonalNote, form=PersonalNoteForm, max_num=0, extra=0 diff --git a/aleksis/apps/alsijil/migrations/0001_initial.py b/aleksis/apps/alsijil/migrations/0001_initial.py index f7fb4a0c056dd4af708dae04555368a3d7546fe6..3b396eaaa92bbf3bdef22379f94ad6055094eab7 100644 --- a/aleksis/apps/alsijil/migrations/0001_initial.py +++ b/aleksis/apps/alsijil/migrations/0001_initial.py @@ -1,9 +1,10 @@ -# Generated by Django 2.2.5 on 2019-09-03 18:30 +# Generated by Django 3.0.6 on 2020-05-29 10:29 -import django.db.models.deletion +import aleksis.apps.alsijil.models +import django.contrib.postgres.fields.jsonb +import django.contrib.sites.managers from django.db import migrations, models - -import aleksis.core.util.core_helpers +import django.db.models.deletion class Migration(migrations.Migration): @@ -11,114 +12,74 @@ class Migration(migrations.Migration): initial = True dependencies = [ - ("chronos", "0001_initial"), - ("core", "0001_initial"), + ('core', '0001_initial'), + ('chronos', '0001_initial'), + ('sites', '0002_alter_domain_unique'), ] operations = [ migrations.CreateModel( - name="PersonalNote", + name='PersonalNoteFilter', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('extended_data', django.contrib.postgres.fields.jsonb.JSONField(default=dict, editable=False)), + ('identifier', models.CharField(max_length=30, unique=True, validators=[aleksis.apps.alsijil.models.isidentifier], verbose_name='Identifier')), + ('description', models.CharField(blank=True, max_length=60, unique=True, verbose_name='Description')), + ('regex', models.CharField(max_length=100, unique=True, verbose_name='Match expression')), + ('site', models.ForeignKey(default=1, editable=False, on_delete=django.db.models.deletion.CASCADE, to='sites.Site')), + ], + options={ + 'verbose_name': 'Personal note filter', + 'verbose_name_plural': 'Personal note filters', + 'ordering': ['identifier'], + }, + managers=[ + ('objects', django.contrib.sites.managers.CurrentSiteManager()), + ], + ), + migrations.CreateModel( + name='PersonalNote', fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ("week", models.IntegerField()), - ("absent", models.BooleanField(default=False)), - ("late", models.IntegerField(default=0)), - ("excused", models.BooleanField(default=False)), - ("remarks", models.CharField(blank=True, max_length=200)), - ( - "lesson_period", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - to="chronos.LessonPeriod", - ), - ), - ( - "person", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - related_name="personal_notes", - to="core.Person", - ), - ), - ( - "school", - models.ForeignKey( - default=1, - on_delete=django.db.models.deletion.CASCADE, - to="core.School", - ), - ), + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('extended_data', django.contrib.postgres.fields.jsonb.JSONField(default=dict, editable=False)), + ('week', models.IntegerField()), + ('absent', models.BooleanField(default=False)), + ('late', models.IntegerField(default=0)), + ('excused', models.BooleanField(default=False)), + ('remarks', models.CharField(blank=True, max_length=200)), + ('lesson_period', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='personal_notes', to='chronos.LessonPeriod')), + ('person', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='personal_notes', to='core.Person')), + ('site', models.ForeignKey(default=1, editable=False, on_delete=django.db.models.deletion.CASCADE, to='sites.Site')), ], options={ - "ordering": [ - "lesson_period__lesson__date_start", - "week", - "lesson_period__period__weekday", - "lesson_period__period__period", - "person__last_name", - "person__first_name", - ], - "unique_together": {("school", "lesson_period", "week", "person")}, + 'verbose_name': 'Personal note', + 'verbose_name_plural': 'Personal notes', + 'ordering': ['lesson_period__lesson__date_start', 'week', 'lesson_period__period__weekday', 'lesson_period__period__period', 'person__last_name', 'person__first_name'], + 'unique_together': {('lesson_period', 'week', 'person')}, }, + managers=[ + ('objects', django.contrib.sites.managers.CurrentSiteManager()), + ], ), migrations.CreateModel( - name="LessonDocumentation", + name='LessonDocumentation', fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ("week", models.IntegerField()), - ( - "topic", - models.CharField( - blank=True, max_length=200, verbose_name="Lesson topic" - ), - ), - ( - "homework", - models.CharField( - blank=True, max_length=200, verbose_name="Homework" - ), - ), - ( - "lesson_period", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - related_name="documentations", - to="chronos.LessonPeriod", - ), - ), - ( - "school", - models.ForeignKey( - default=1, - on_delete=django.db.models.deletion.CASCADE, - to="core.School", - ), - ), + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('extended_data', django.contrib.postgres.fields.jsonb.JSONField(default=dict, editable=False)), + ('week', models.IntegerField()), + ('topic', models.CharField(blank=True, max_length=200, verbose_name='Lesson topic')), + ('homework', models.CharField(blank=True, max_length=200, verbose_name='Homework')), + ('lesson_period', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='documentations', to='chronos.LessonPeriod')), + ('site', models.ForeignKey(default=1, editable=False, on_delete=django.db.models.deletion.CASCADE, to='sites.Site')), ], options={ - "ordering": [ - "lesson_period__lesson__date_start", - "week", - "lesson_period__period__weekday", - "lesson_period__period__period", - ], - "unique_together": {("school", "lesson_period", "week")}, + 'verbose_name': 'Lesson documentation', + 'verbose_name_plural': 'Lesson documentations', + 'ordering': ['lesson_period__lesson__date_start', 'week', 'lesson_period__period__weekday', 'lesson_period__period__period'], + 'unique_together': {('lesson_period', 'week')}, }, + managers=[ + ('objects', django.contrib.sites.managers.CurrentSiteManager()), + ], ), ] diff --git a/aleksis/apps/alsijil/migrations/0002_add_personal_note_filter.py b/aleksis/apps/alsijil/migrations/0002_add_personal_note_filter.py deleted file mode 100644 index 13782db55d6165bc17f3e1880164ef39e109395a..0000000000000000000000000000000000000000 --- a/aleksis/apps/alsijil/migrations/0002_add_personal_note_filter.py +++ /dev/null @@ -1,66 +0,0 @@ -# Generated by Django 2.2.5 on 2019-11-20 14:21 - -import django.db.models.deletion -from django.db import migrations, models - -import aleksis.apps.alsijil.models -import aleksis.core.util.core_helpers - - -class Migration(migrations.Migration): - - dependencies = [ - ("core", "0002_activity_notification"), - ("alsijil", "0001_initial"), - ] - - operations = [ - migrations.CreateModel( - name="PersonalNoteFilter", - fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ( - "identifier", - models.CharField( - max_length=30, - validators=[aleksis.apps.alsijil.models.isidentifier], - verbose_name="Identifier", - ), - ), - ( - "description", - models.CharField( - blank=True, max_length=60, verbose_name="Description" - ), - ), - ( - "regex", - models.CharField(max_length=100, verbose_name="Match expression"), - ), - ( - "school", - models.ForeignKey( - default=1, - on_delete=django.db.models.deletion.CASCADE, - to="core.School", - ), - ), - ], - options={ - "ordering": ["identifier"], - "unique_together": { - ("school", "regex"), - ("school", "description"), - ("school", "identifier"), - }, - }, - ), - ] diff --git a/aleksis/apps/alsijil/model_extensions.py b/aleksis/apps/alsijil/model_extensions.py index bfa3dd7def75c3891f4cf32b0369ad7f6d7f0ee4..b503bbf8f552e31977e56e64709a019cff265cfc 100644 --- a/aleksis/apps/alsijil/model_extensions.py +++ b/aleksis/apps/alsijil/model_extensions.py @@ -1,8 +1,9 @@ from datetime import date -from calendarweek import CalendarWeek from django.db.models import Exists, F, OuterRef +from calendarweek import CalendarWeek + from aleksis.apps.chronos.models import LessonPeriod from aleksis.core.models import Group, Person diff --git a/aleksis/apps/alsijil/models.py b/aleksis/apps/alsijil/models.py index 8874719bf414a5b8dbafa603608f0cccd836241a..3867f6c5a449977f39135e1690bb7954623a247f 100644 --- a/aleksis/apps/alsijil/models.py +++ b/aleksis/apps/alsijil/models.py @@ -1,14 +1,14 @@ from django.db import models from django.utils.translation import gettext_lazy as _ -from aleksis.core.mixins import CRUDMixin +from aleksis.core.mixins import ExtensibleModel def isidentifier(value: str) -> bool: return value.isidentifier() -class PersonalNote(models.Model): +class PersonalNote(ExtensibleModel): """ A personal note about a single person. Used in the class register to note absences, excuses and remarks about a student in a single lesson period. """ @@ -27,6 +27,8 @@ class PersonalNote(models.Model): remarks = models.CharField(max_length=200, blank=True) class Meta: + verbose_name = _("Personal note") + verbose_name_plural = _("Personal notes") unique_together = [["lesson_period", "week", "person"]] ordering = [ "lesson_period__lesson__date_start", @@ -38,7 +40,7 @@ class PersonalNote(models.Model): ] -class LessonDocumentation(CRUDMixin): +class LessonDocumentation(ExtensibleModel): """ A documentation on a single lesson period. Non-personal, includes the topic and homework of the lesson. """ @@ -52,6 +54,8 @@ class LessonDocumentation(CRUDMixin): homework = models.CharField(verbose_name=_("Homework"), max_length=200, blank=True) class Meta: + verbose_name = _("Lesson documentation") + verbose_name_plural = _("Lesson documentations") unique_together = [["lesson_period", "week"]] ordering = [ "lesson_period__lesson__date_start", @@ -61,7 +65,7 @@ class LessonDocumentation(CRUDMixin): ] -class PersonalNoteFilter(models.Model): +class PersonalNoteFilter(ExtensibleModel): """ A filter definition that can generate statistics on personal note texts. """ identifier = models.CharField( @@ -74,4 +78,6 @@ class PersonalNoteFilter(models.Model): regex = models.CharField(verbose_name=_("Match expression"), max_length=100, unique=True) class Meta: + verbose_name = _("Personal note filter") + verbose_name_plural = _("Personal note filters") ordering = ["identifier"] diff --git a/aleksis/apps/alsijil/static/css/alsijil/full_register.css b/aleksis/apps/alsijil/static/css/alsijil/full_register.css index fe26ef3b717889ca953bc5c052ecb817d487fcf7..e9ee93a5c93d948307ae15b9f4b146616e73279a 100644 --- a/aleksis/apps/alsijil/static/css/alsijil/full_register.css +++ b/aleksis/apps/alsijil/static/css/alsijil/full_register.css @@ -130,3 +130,14 @@ table.person-info td.person-img img { .sheet * { page-break-inside: avoid; } + +img.max-size-600 { + max-width: 600px; + max-height: 600px; +} + +img.center{ + position: relative; + left: 50%; + transform: translateX(-50%); +} diff --git a/aleksis/apps/alsijil/templates/alsijil/print/full_register.html b/aleksis/apps/alsijil/templates/alsijil/print/full_register.html index b8756f4bcc1572c7335e7a56194864d19c4ab475..32069660f212b015faacd13c73d5b4689382b2b2 100644 --- a/aleksis/apps/alsijil/templates/alsijil/print/full_register.html +++ b/aleksis/apps/alsijil/templates/alsijil/print/full_register.html @@ -14,7 +14,9 @@ <section class="sheet padding-10mm bigprint" id="titlepage"> <div> <h1>{% trans 'Class register' %}</h1> - <img src="{% cropped_thumbnail school 'logo_cropping' max_size='600x600' %}" id="school-logo" /> + {% static "img/aleksis-banner.svg" as aleksis_banner %} + <img src="{% firstof request.site.preferences.theme__logo.url aleksis_banner %}" + alt="{{ request.site.preferences.general__title }} – Logo" class="max-size-600 center"> <p id="group-desc"> {{ group.name }} </p> @@ -82,7 +84,7 @@ <td>{{ person.first_name }}</td> <td>{{ person.get_sex_display }}</td> <td>{{ person.date_of_birth }}</td> - <td>{{ person.absences}}</td> + <td>{{ person.absences_count}}</td> <td>{{ person.unexcused }}</td> <td>{{ person.tardiness }}</td> </tr> @@ -204,7 +206,7 @@ <tbody> <tr> - <td>{{ person.absences }}</td> + <td>{{ person.absences_count }}</td> <td>{{ person.unexcused }}</td> <td>{{ person.tardiness }}</td> </tr> diff --git a/aleksis/apps/alsijil/urls.py b/aleksis/apps/alsijil/urls.py index 20519ad4d60020a5ed717c00a54baf4c8e21abc9..fe40f18ee256cba2f90c48489024cdde211a9b90 100644 --- a/aleksis/apps/alsijil/urls.py +++ b/aleksis/apps/alsijil/urls.py @@ -11,6 +11,8 @@ urlpatterns = [ ), path("week", views.week_view, name="week_view"), path("week/<int:year>/<int:week>", views.week_view, name="week_view_by_week"), + path("week/<str:type_>/<int:id_>/", views.week_view, name="week_view"), + path("week/<int:year>/<int:week>/<str:type_>/<int:id_>/", views.week_view, name="week_view_by_week"), path("print/group/<int:id_>", views.full_register_group, name="full_register_group"), path("absence/new", views.register_absence, name="register_absence"), path("filters/list", views.list_personal_note_filters, name="list_personal_note_filters",), diff --git a/aleksis/apps/alsijil/views.py b/aleksis/apps/alsijil/views.py index 0378d727ef323684db44621f5ef2ee338315d2fd..84c12a8e2e6354ec1208537dc7a31a7f142f71b6 100644 --- a/aleksis/apps/alsijil/views.py +++ b/aleksis/apps/alsijil/views.py @@ -1,20 +1,20 @@ -from calendarweek import CalendarWeek from datetime import date, datetime, timedelta from typing import Optional -from django.contrib.auth.decorators import login_required +from aleksis.apps.chronos.managers import TimetableType +from aleksis.apps.chronos.util.chronos_helpers import get_el_by_pk from django.core.exceptions import PermissionDenied from django.db.models import Count, Exists, OuterRef, Q, Sum -from django.http import Http404, HttpRequest, HttpResponse +from django.http import Http404, HttpRequest, HttpResponse, HttpResponseNotFound from django.shortcuts import get_object_or_404, redirect, render from django.urls import reverse from django.utils.translation import ugettext as _ +from calendarweek import CalendarWeek from django_tables2 import RequestConfig from aleksis.apps.chronos.models import LessonPeriod -from aleksis.core.decorators import admin_required -from aleksis.core.models import Group, Person, School +from aleksis.core.models import Group, Person, SchoolTerm from aleksis.core.util import messages from .forms import ( @@ -28,7 +28,6 @@ from .models import LessonDocumentation, PersonalNoteFilter from .tables import PersonalNoteFilterTable -@login_required def lesson( request: HttpRequest, year: Optional[int] = None, @@ -77,7 +76,7 @@ def lesson( lesson_period=lesson_period, week=wanted_week.week ) lesson_documentation_form = LessonDocumentationForm( - request.POST or None, instance=lesson_documentation, prefix="leson_documentation", + request.POST or None, instance=lesson_documentation, prefix="lesson_documentation", ) # Create a formset that holds all personal notes for all persons in this lesson @@ -109,9 +108,8 @@ def lesson( return render(request, "alsijil/class_register/lesson.html", context) -@login_required def week_view( - request: HttpRequest, year: Optional[int] = None, week: Optional[int] = None + request: HttpRequest, year: Optional[int] = None, week: Optional[int] = None, type_: Optional[str] = None, id_: Optional[int] = None ) -> HttpResponse: context = {} @@ -128,57 +126,74 @@ def week_view( ) ).in_week(wanted_week) - group = None # FIXME workaround for #38 - if ( - request.GET.get("group", None) - or request.GET.get("teacher", None) - or request.GET.get("room", None) - ): - lesson_periods = lesson_periods.filter_from_query(request.GET) - if "group" in request.GET and request.GET["group"]: - group = Group.objects.get(pk=request.GET["group"]) - else: - group = None + group = None + if type_ and id_: + instance = get_el_by_pk(request, type_, id_) + + if isinstance(instance, HttpResponseNotFound): + return HttpResponseNotFound() + + type_ = TimetableType.from_string(type_) + + if type_ == TimetableType.GROUP: + group = instance + + lesson_periods = lesson_periods.filter_from_type(type_, instance) elif hasattr(request, "user") and hasattr(request.user, "person"): - group = request.user.person.owner_of.first() - if group: - lesson_periods = lesson_periods.filter_group(group) - elif request.user.person.lessons_as_teacher.exists(): + instance = request.user.person + if request.user.person.lessons_as_teacher.exists(): lesson_periods = lesson_periods.filter_teacher(request.user.person) + type_ = TimetableType.TEACHER else: lesson_periods = lesson_periods.filter_participant(request.user.person) else: lesson_periods = None + # Add a form to filter the view + if type_: + initial = {type_.value: instance} + else: + initial = {} + select_form = SelectForm(request.POST or None, initial=initial) + + if request.method == "POST": + if select_form.is_valid(): + if "type_" not in select_form.cleaned_data: + return redirect("week_view_by_week", wanted_week.year, wanted_week.week) + else: + return redirect("week_view_by_week", wanted_week.year, wanted_week.week, + select_form.cleaned_data["type_"].value, select_form.cleaned_data["instance"].pk) + if lesson_periods: # Aggregate all personal notes for this group and week + lesson_periods_pk = lesson_periods.values_list("pk", flat=True) persons = ( Person.objects.filter(is_active=True) - .filter(member_of__lessons__lesson_periods__in=lesson_periods) + .filter(member_of__lessons__lesson_periods__in=lesson_periods_pk) .distinct() .prefetch_related("personal_notes") .annotate( - absences=Count( + absences_count=Count( "personal_notes__absent", filter=Q( - personal_notes__lesson_period__in=lesson_periods, + personal_notes__lesson_period__in=lesson_periods_pk, personal_notes__week=wanted_week.week, personal_notes__absent=True, ), ), - unexcused=Count( + unexcused_count=Count( "personal_notes__absent", filter=Q( - personal_notes__lesson_period__in=lesson_periods, + personal_notes__lesson_period__in=lesson_periods_pk, personal_notes__week=wanted_week.week, personal_notes__absent=True, personal_notes__excused=False, ), ), - tardiness=Sum( + tardiness_sum=Sum( "personal_notes__late", filter=Q( - personal_notes__lesson_period__in=lesson_periods, + personal_notes__lesson_period__in=lesson_periods_pk, personal_notes__week=wanted_week.week, ), ), @@ -187,15 +202,15 @@ def week_view( else: persons = None - # Add a form to filter the view - select_form = SelectForm(request.GET or None) + # Resort lesson periods manually because an union queryset doesn't support order_by + lesson_periods = sorted(lesson_periods, key=lambda x: (x.period.weekday, x.period.period)) - context["current_head"] = str(wanted_week) context["week"] = wanted_week context["lesson_periods"] = lesson_periods context["persons"] = persons context["group"] = group context["select_form"] = select_form + context["instance"] = instance week_prev = wanted_week - 1 week_next = wanted_week + 1 @@ -211,7 +226,6 @@ def week_view( return render(request, "alsijil/class_register/week_view.html", context) -@login_required def full_register_group(request: HttpRequest, id_: int) -> HttpResponse: context = {} @@ -220,20 +234,26 @@ def full_register_group(request: HttpRequest, id_: int) -> HttpResponse: # Get all lesson periods for the selected group lesson_periods = ( LessonPeriod.objects.filter_group(group) - .distinct() - .prefetch_related("documentations", "personal_notes") + .distinct() + .prefetch_related("documentations", "personal_notes") ) + current_school_term = SchoolTerm.current + + if not current_school_term: + return HttpResponseNotFound(_("There is no current school term.")) + weeks = CalendarWeek.weeks_within( - School.objects.first().current_term.date_start, - School.objects.first().current_term.date_end, + current_school_term.date_start, + current_school_term.date_end, ) + periods_by_day = {} for lesson_period in lesson_periods: for week in weeks: day = week[lesson_period.period.weekday - 1] - if lesson_period.lesson.date_start <= day and lesson_period.lesson.date_end >= day: + if lesson_period.lesson.date_start <= day <= lesson_period.lesson.date_end: documentations = list( filter(lambda d: d.week == week.week, lesson_period.documentations.all(),) ) @@ -247,7 +267,7 @@ def full_register_group(request: HttpRequest, id_: int) -> HttpResponse: ) persons = group.members.annotate( - absences=Count("personal_notes__absent", filter=Q(personal_notes__absent=True)), + absences_count=Count("personal_notes__absent", filter=Q(personal_notes__absent=True)), unexcused=Count( "personal_notes__absent", filter=Q(personal_notes__absent=True, personal_notes__excused=False), @@ -274,12 +294,10 @@ def full_register_group(request: HttpRequest, id_: int) -> HttpResponse: context["weeks"] = weeks context["periods_by_day"] = periods_by_day context["today"] = date.today() - context["school"] = School.objects.first() return render(request, "alsijil/print/full_register.html", context) -@admin_required def register_absence(request: HttpRequest) -> HttpResponse: context = {} @@ -349,7 +367,6 @@ def edit_personal_note_filter(request: HttpRequest, id: Optional["int"] = None) return render(request, "alsijil/personal_note_filter/manage.html", context) -@admin_required def delete_personal_note_filter(request: HttpRequest, id_: int) -> HttpResponse: context = {} diff --git a/poetry.lock b/poetry.lock index b7cb16f0d2c584260a70a2a4a84752239738287e..a64701112e831ae0da22f016db69439f5a698da7 100644 --- a/poetry.lock +++ b/poetry.lock @@ -68,7 +68,7 @@ extras = ["phonenumbers"] version = ">=3.0,<4.0" [package.dependencies.django-two-factor-auth] -extras = ["sms", "call", "yubikey", "phonenumbers"] +extras = ["sms", "call", "phonenumbers", "yubikey"] version = ">=1.11.0,<2.0.0" [package.dependencies.dynaconf] @@ -2327,14 +2327,12 @@ jinja2 = [ {file = "Jinja2-2.11.2.tar.gz", hash = "sha256:89aab215427ef59c34ad58735269eb58b1a5808103067f7bb9d5836c651b3bb0"}, ] libsass = [ - {file = "libsass-0.20.0-cp27-cp27m-macosx_10_14_intel.whl", hash = "sha256:107c409524c6a4ed14410fa9dafa9ee59c6bd3ecae75d73af749ab2b75685726"}, {file = "libsass-0.20.0-cp27-cp27m-win32.whl", hash = "sha256:98f6dee9850b29e62977a963e3beb3cfeb98b128a267d59d2c3d675e298c8d57"}, {file = "libsass-0.20.0-cp27-cp27m-win_amd64.whl", hash = "sha256:b077261a04ba1c213e932943208471972c5230222acb7fa97373e55a40872cbb"}, {file = "libsass-0.20.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:e6a547c0aa731dcb4ed71f198e814bee0400ce04d553f3f12a53bc3a17f2a481"}, {file = "libsass-0.20.0-cp36-abi3-manylinux1_x86_64.whl", hash = "sha256:74f6fb8da58179b5d86586bc045c16d93d55074bc7bb48b6354a4da7ac9f9dfd"}, {file = "libsass-0.20.0-cp36-cp36m-win32.whl", hash = "sha256:a43f3830d83ad9a7f5013c05ce239ca71744d0780dad906587302ac5257bce60"}, {file = "libsass-0.20.0-cp36-cp36m-win_amd64.whl", hash = "sha256:fd19c8f73f70ffc6cbcca8139da08ea9a71fc48e7dfc4bb236ad88ab2d6558f1"}, - {file = "libsass-0.20.0-cp37-abi3-macosx_10_14_x86_64.whl", hash = "sha256:8cf72552b39e78a1852132e16b706406bc76029fe3001583284ece8d8752a60a"}, {file = "libsass-0.20.0-cp37-cp37m-win32.whl", hash = "sha256:7555d9b24e79943cfafac44dbb4ca7e62105c038de7c6b999838c9ff7b88645d"}, {file = "libsass-0.20.0-cp37-cp37m-win_amd64.whl", hash = "sha256:794f4f4661667263e7feafe5cc866e3746c7c8a9192b2aa9afffdadcbc91c687"}, {file = "libsass-0.20.0-cp38-cp38-win32.whl", hash = "sha256:3bc0d68778b30b5fa83199e18795314f64b26ca5871e026343e63934f616f7f7"},