Skip to content
Snippets Groups Projects
views.py 56.4 KiB
Newer Older
Julian's avatar
Julian committed
                .aggregate(absences_count=Count("absent"))
Julian's avatar
Julian committed
                personal_notes.filter(absent=True, excused=True)
                .exclude(excuse_type__count_as_absent=False)
                .aggregate(excused=Count("absent"))
Julian's avatar
Julian committed
                personal_notes.filter(absent=True, excused=True, excuse_type__isnull=True)
                .exclude(excuse_type__count_as_absent=False)
                .aggregate(excused_no_excuse_type=Count("absent"))
            )
            stat.update(
                personal_notes.filter(absent=True, excused=False).aggregate(
                    unexcused=Count("absent")
            stat.update(personal_notes.aggregate(tardiness=Sum("late")))
            stat.update(personal_notes.filter(~Q(late=0)).aggregate(tardiness_count=Count("late")))
                stat.update(
                    personal_notes.filter(extra_marks=extra_mark).aggregate(
                        **{extra_mark.count_label: Count("pk")}
                    )
                )

                    personal_notes.filter(absent=True, excuse_type=excuse_type).aggregate(
                        **{excuse_type.count_label: Count("absent")}
                    )
            stats.append((school_term, stat))
        context["stats"] = stats
    # Build filter with own form and logic as django-filter can't work with different models
    if request.user.person.preferences["alsijil__default_lesson_documentation_filter"]:
        default_documentation = False
    else:
        default_documentation = None

Lloyd Meins's avatar
Lloyd Meins committed
    filter_form = FilterRegisterObjectForm(
        request, request.GET or None, for_person=True, default_documentation=default_documentation
Lloyd Meins's avatar
Lloyd Meins committed
    )
    filter_dict = (
        filter_form.cleaned_data
        if filter_form.is_valid()
        else {"has_documentation": default_documentation}
Lloyd Meins's avatar
Lloyd Meins committed
    )
    filter_dict["person"] = person
    context["filter_form"] = filter_form
        register_objects = generate_list_of_all_register_objects(filter_dict)
        table = RegisterObjectTable(register_objects)
        items_per_page = request.user.person.preferences[
            "alsijil__register_objects_table_items_per_page"
        ]
        RequestConfig(request, paginate={"per_page": items_per_page}).configure(table)
        context["register_object_table"] = table
Jonathan Weth's avatar
Jonathan Weth committed
    return render(request, "alsijil/class_register/person.html", context)


Hangzhi Yu's avatar
Hangzhi Yu committed
@never_cache
Hangzhi Yu's avatar
Hangzhi Yu committed
@permission_required("alsijil.register_absence_rule", fn=objectgetter_optional(Person))
def register_absence(request: HttpRequest, id_: int) -> HttpResponse:
Tom Teichler's avatar
Tom Teichler committed
    context = {}

    person = get_object_or_404(Person, pk=id_)

    register_absence_form = RegisterAbsenceForm(request.POST or None)
    if request.method == "POST" and register_absence_form.is_valid():
        confirmed = request.POST.get("confirmed", "0") == "1"

        # Get data from form
        # person = register_absence_form.cleaned_data["person"]
        start_date = register_absence_form.cleaned_data["date_start"]
        end_date = register_absence_form.cleaned_data["date_end"]
        from_period = register_absence_form.cleaned_data["from_period"]
        to_period = register_absence_form.cleaned_data["to_period"]
        absent = register_absence_form.cleaned_data["absent"]
        excused = register_absence_form.cleaned_data["excused"]
        excuse_type = register_absence_form.cleaned_data["excuse_type"]
        remarks = register_absence_form.cleaned_data["remarks"]

        # Mark person as absent
        delta = end_date - start_date
        for i in range(delta.days + 1):
            from_period_on_day = from_period if i == 0 else TimePeriod.period_min
            to_period_on_day = to_period if i == delta.days else TimePeriod.period_max
            day = start_date + timedelta(days=i)

            # Skip holidays if activated
            if not get_site_preferences()["alsijil__allow_entries_in_holidays"]:
                holiday = Holiday.on_day(day)
                if holiday:
                    continue

            with reversion.create_revision() if confirmed else nullcontext():
                affected_count += person.mark_absent(
                    day,
                    from_period_on_day,
                    absent,
                    excused,
                    excuse_type,
                    remarks,
                    to_period_on_day,
                    dry_run=not confirmed,
                )
        if not confirmed:
            # Show confirmation page
            context = {}
            context["affected_lessons"] = affected_count
            context["person"] = person
            context["form_data"] = register_absence_form.cleaned_data
            context["form"] = register_absence_form
            return render(request, "alsijil/absences/register_confirm.html", context)
        else:
            messages.success(request, _("The absence has been saved."))
            return redirect("overview_person", person.pk)
    context["register_absence_form"] = register_absence_form
    return render(request, "alsijil/absences/register.html", context)
Hangzhi Yu's avatar
Hangzhi Yu committed
@method_decorator(never_cache, name="dispatch")
class DeletePersonalNoteView(PermissionRequiredMixin, DetailView):
    model = PersonalNote
    template_name = "core/pages/delete.html"
Hangzhi Yu's avatar
Hangzhi Yu committed
    permission_required = "alsijil.edit_personalnote_rule"
    def post(self, request, *args, **kwargs):
        note = self.get_object()
        with reversion.create_revision():
            reversion.set_user(request.user)
            note.save()
        messages.success(request, _("The personal note has been deleted."))
        return redirect("overview_person", note.person.pk)
class ExtraMarkListView(PermissionRequiredMixin, SingleTableView):
    """Table of all extra marks."""
    model = ExtraMark
    table_class = ExtraMarkTable
Hangzhi Yu's avatar
Hangzhi Yu committed
    permission_required = "alsijil.view_extramarks_rule"
    template_name = "alsijil/extra_mark/list.html"
Hangzhi Yu's avatar
Hangzhi Yu committed
@method_decorator(never_cache, name="dispatch")
class ExtraMarkCreateView(PermissionRequiredMixin, AdvancedCreateView):
    """Create view for extra marks."""
    model = ExtraMark
    form_class = ExtraMarkForm
Hangzhi Yu's avatar
Hangzhi Yu committed
    permission_required = "alsijil.add_extramark_rule"
    template_name = "alsijil/extra_mark/create.html"
    success_url = reverse_lazy("extra_marks")
    success_message = _("The extra mark has been created.")
Hangzhi Yu's avatar
Hangzhi Yu committed
@method_decorator(never_cache, name="dispatch")
class ExtraMarkEditView(PermissionRequiredMixin, AdvancedEditView):
    """Edit view for extra marks."""
    model = ExtraMark
    form_class = ExtraMarkForm
Hangzhi Yu's avatar
Hangzhi Yu committed
    permission_required = "alsijil.edit_extramark_rule"
    template_name = "alsijil/extra_mark/edit.html"
    success_url = reverse_lazy("extra_marks")
    success_message = _("The extra mark has been saved.")
Hangzhi Yu's avatar
Hangzhi Yu committed
@method_decorator(never_cache, name="dispatch")
class ExtraMarkDeleteView(PermissionRequiredMixin, RevisionMixin, AdvancedDeleteView):
    """Delete view for extra marks."""
    model = ExtraMark
Hangzhi Yu's avatar
Hangzhi Yu committed
    permission_required = "alsijil.delete_extramark_rule"
    template_name = "core/pages/delete.html"
    success_url = reverse_lazy("extra_marks")
    success_message = _("The extra mark has been deleted.")
class ExcuseTypeListView(PermissionRequiredMixin, SingleTableView):
    """Table of all excuse types."""

    model = ExcuseType
    table_class = ExcuseTypeTable
Hangzhi Yu's avatar
Hangzhi Yu committed
    permission_required = "alsijil.view_excusetypes_rule"
    template_name = "alsijil/excuse_type/list.html"


Hangzhi Yu's avatar
Hangzhi Yu committed
@method_decorator(never_cache, name="dispatch")
class ExcuseTypeCreateView(PermissionRequiredMixin, AdvancedCreateView):
    """Create view for excuse types."""

    model = ExcuseType
    form_class = ExcuseTypeForm
Hangzhi Yu's avatar
Hangzhi Yu committed
    permission_required = "alsijil.add_excusetype_rule"
    template_name = "alsijil/excuse_type/create.html"
    success_url = reverse_lazy("excuse_types")
    success_message = _("The excuse type has been created.")


Hangzhi Yu's avatar
Hangzhi Yu committed
@method_decorator(never_cache, name="dispatch")
class ExcuseTypeEditView(PermissionRequiredMixin, AdvancedEditView):
    """Edit view for excuse types."""

    model = ExcuseType
    form_class = ExcuseTypeForm
Hangzhi Yu's avatar
Hangzhi Yu committed
    permission_required = "alsijil.edit_excusetype_rule"
    template_name = "alsijil/excuse_type/edit.html"
    success_url = reverse_lazy("excuse_types")
    success_message = _("The excuse type has been saved.")


Hangzhi Yu's avatar
Hangzhi Yu committed
@method_decorator(never_cache, "dispatch")
class ExcuseTypeDeleteView(PermissionRequiredMixin, RevisionMixin, AdvancedDeleteView):
    """Delete view for excuse types."""

    model = ExcuseType
Hangzhi Yu's avatar
Hangzhi Yu committed
    permission_required = "alsijil.delete_excusetype_rule"
    template_name = "core/pages/delete.html"
    success_url = reverse_lazy("excuse_types")
    success_message = _("The excuse type has been deleted.")
class GroupRoleListView(PermissionRequiredMixin, SingleTableView):
    """Table of all group roles."""
    model = GroupRole
    table_class = GroupRoleTable
Hangzhi Yu's avatar
Hangzhi Yu committed
    permission_required = "alsijil.view_grouproles_rule"
    template_name = "alsijil/group_role/list.html"


@method_decorator(never_cache, name="dispatch")
class GroupRoleCreateView(PermissionRequiredMixin, AdvancedCreateView):
    """Create view for group roles."""
    model = GroupRole
    form_class = GroupRoleForm
Hangzhi Yu's avatar
Hangzhi Yu committed
    permission_required = "alsijil.add_grouprole_rule"
    template_name = "alsijil/group_role/create.html"
    success_url = reverse_lazy("group_roles")
    success_message = _("The group role has been created.")


@method_decorator(never_cache, name="dispatch")
class GroupRoleEditView(PermissionRequiredMixin, AdvancedEditView):
    """Edit view for group roles."""
    model = GroupRole
    form_class = GroupRoleForm
Hangzhi Yu's avatar
Hangzhi Yu committed
    permission_required = "alsijil.edit_grouprole_rule"
    template_name = "alsijil/group_role/edit.html"
    success_url = reverse_lazy("group_roles")
    success_message = _("The group role has been saved.")


@method_decorator(never_cache, "dispatch")
class GroupRoleDeleteView(PermissionRequiredMixin, RevisionMixin, AdvancedDeleteView):
    """Delete view for group roles."""
    model = GroupRole
Hangzhi Yu's avatar
Hangzhi Yu committed
    permission_required = "alsijil.delete_grouprole_rule"
    template_name = "core/pages/delete.html"
    success_url = reverse_lazy("group_roles")
    success_message = _("The group role has been deleted.")


class AssignedGroupRolesView(PermissionRequiredMixin, DetailView):
Hangzhi Yu's avatar
Hangzhi Yu committed
    permission_required = "alsijil.view_assigned_grouproles_rule"
    model = Group
    template_name = "alsijil/group_role/assigned_list.html"

    def get_context_data(self, **kwargs: Any) -> Dict[str, Any]:
        context = super().get_context_data()

        today = timezone.now().date()
        context["today"] = today

        self.roles = GroupRole.objects.with_assignments(today, [self.object])
        context["roles"] = self.roles
        assignments = (
            GroupRoleAssignment.objects.filter(
                Q(groups=self.object) | Q(groups__child_groups=self.object)
            )
            .distinct()
            .order_by("-date_start")
        )
        context["assignments"] = assignments
        return context


@method_decorator(never_cache, name="dispatch")
class AssignGroupRoleView(PermissionRequiredMixin, SuccessNextMixin, AdvancedCreateView):
    model = GroupRoleAssignment
    form_class = AssignGroupRoleForm
Hangzhi Yu's avatar
Hangzhi Yu committed
    permission_required = "alsijil.assign_grouprole_for_group_rule"
    template_name = "alsijil/group_role/assign.html"
    success_message = _("The group role has been assigned.")

    def get_success_url(self) -> str:
        return reverse("assigned_group_roles", args=[self.group.pk])

    def get_permission_object(self):
        self.group = get_object_or_404(Group, pk=self.kwargs.get("pk"))
        try:
            self.role = GroupRole.objects.get(pk=self.kwargs.get("role_pk"))
        except GroupRole.DoesNotExist:
            self.role = None
        return self.group

    def get_form_kwargs(self):
        kwargs = super().get_form_kwargs()
        kwargs["request"] = self.request
        kwargs["initial"] = {"role": self.role, "groups": [self.group]}
        return kwargs

    def get_context_data(self, **kwargs: Any) -> Dict[str, Any]:
        context = super().get_context_data(**kwargs)
        context["role"] = self.role
        context["group"] = self.group
        return context


@method_decorator(never_cache, name="dispatch")
class AssignGroupRoleMultipleView(PermissionRequiredMixin, SuccessNextMixin, AdvancedCreateView):
    model = GroupRoleAssignment
    form_class = AssignGroupRoleForm
Hangzhi Yu's avatar
Hangzhi Yu committed
    permission_required = "alsijil.assign_grouprole_for_multiple_rule"
    template_name = "alsijil/group_role/assign.html"
    success_message = _("The group role has been assigned.")

    def get_success_url(self) -> str:
        return reverse("assign_group_role_multiple")

    def get_form_kwargs(self):
        kwargs = super().get_form_kwargs()
        kwargs["request"] = self.request
        return kwargs


@method_decorator(never_cache, name="dispatch")
class GroupRoleAssignmentEditView(PermissionRequiredMixin, SuccessNextMixin, AdvancedEditView):
    """Edit view for group role assignments."""

    model = GroupRoleAssignment
    form_class = GroupRoleAssignmentEditForm
Hangzhi Yu's avatar
Hangzhi Yu committed
    permission_required = "alsijil.edit_grouproleassignment_rule"
    template_name = "alsijil/group_role/edit_assignment.html"
    success_message = _("The group role assignment has been saved.")

    def get_success_url(self) -> str:
        pk = self.object.groups.first().pk
        return reverse("assigned_group_roles", args=[pk])


@method_decorator(never_cache, "dispatch")
class GroupRoleAssignmentStopView(PermissionRequiredMixin, SuccessNextMixin, DetailView):
    model = GroupRoleAssignment
Hangzhi Yu's avatar
Hangzhi Yu committed
    permission_required = "alsijil.stop_grouproleassignment_rule"
    def get_success_url(self) -> str:
        pk = self.object.groups.first().pk
        return reverse("assigned_group_roles", args=[pk])

    def get(self, request, *args, **kwargs):
        self.object = self.get_object()
        if not self.object.date_end:
            self.object.date_end = timezone.now().date()
            self.object.save()
            messages.success(request, _("The group role assignment has been stopped."))
        return redirect(self.get_success_url())


@method_decorator(never_cache, "dispatch")
class GroupRoleAssignmentDeleteView(
    PermissionRequiredMixin, RevisionMixin, SuccessNextMixin, AdvancedDeleteView
):
    """Delete view for group role assignments."""

    model = GroupRoleAssignment
Hangzhi Yu's avatar
Hangzhi Yu committed
    permission_required = "alsijil.delete_grouproleassignment_rule"
    template_name = "core/pages/delete.html"
    success_message = _("The group role assignment has been deleted.")

    def get_success_url(self) -> str:
        pk = self.object.groups.first().pk
        return reverse("assigned_group_roles", args=[pk])


class AllRegisterObjectsView(PermissionRequiredMixin, View):
    """Provide overview of all register objects for coordinators."""

Hangzhi Yu's avatar
Hangzhi Yu committed
    permission_required = "alsijil.view_register_objects_list_rule"
    def get_context_data(self, request):
        context = {}
        # Filter selectable groups by permissions
        groups = Group.objects.all()
        if not check_global_permission(request.user, "alsijil.view_full_register"):
            allowed_groups = get_objects_for_user(
                self.request.user, "core.view_full_register_group", Group
            ).values_list("pk", flat=True)
            groups = groups.filter(Q(parent_groups__in=allowed_groups) | Q(pk__in=allowed_groups))

        # Build filter with own form and logic as django-filter can't work with different models
        filter_form = FilterRegisterObjectForm(
            request, request.GET or None, for_person=False, groups=groups
        )
        filter_dict = filter_form.cleaned_data if filter_form.is_valid() else {}
        filter_dict["groups"] = groups
        context["filter_form"] = filter_form

        register_objects = generate_list_of_all_register_objects(filter_dict)

        self.action_form = RegisterObjectActionForm(request, register_objects, request.POST or None)
        context["action_form"] = self.action_form

            self.table = RegisterObjectSelectTable(register_objects)
            items_per_page = request.user.person.preferences[
                "alsijil__register_objects_table_items_per_page"
            ]
            RequestConfig(request, paginate={"per_page": items_per_page}).configure(self.table)
            context["table"] = self.table
        return context

    def get(self, request: HttpRequest) -> HttpResponse:
        context = self.get_context_data(request)
        return render(request, "alsijil/class_register/all_objects.html", context)

    def post(self, request: HttpRequest):
        context = self.get_context_data(request)
        if self.action_form.is_valid():
            self.action_form.execute()
        return render(request, "alsijil/class_register/all_objects.html", context)
Julian's avatar
Julian committed


class CoursebookView(DetailView):
    model = Lesson
    template_name = "alsijil/class_register/coursebook.html"
    permission_required = ""  # FIXME

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        return context

Julian's avatar
Julian committed

class SelectCoursebookView(TemplateView):
    template_name = "alsijil/class_register/select_coursebook.html"
    permission_required = ""  # FIXME

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context["lessons"] = self.request.user.person.lessons_as_teacher.all()
        return context