Exemple #1
0
def export_as_csv(modeladmin: admin.ModelAdmin, request: HttpRequest, queryset: QuerySet) -> HttpResponse:
    semester = Semester.get_current_semester()

    records = Record.objects.filter(
        student__in=queryset,
        group__course__semester=semester,
        status=1).select_related(
        'student',
        'student__user',
        'group',
        'group__course')

    opts = modeladmin.model._meta

    response = HttpResponse(content_type='text/csv')
    response['Content-Disposition'] = 'attachment; filename=%s.csv' % str(opts).replace('.', '_')

    writer = csv.writer(response)
    for record in records:
        writer.writerow([record.student.matricula,
                         record.student.user.first_name,
                         record.student.user.last_name,
                         record.group.course.name,
                         record.group.get_type_display(),
                         record.group.get_terms_as_string()])
    return response
Exemple #2
0
def prepare_data_for_create_poll(request, group_id=0):
    data = pop_template_from_session(request)

    if group_id > 0:
        group = Group.objects.get(pk=group_id)
        data['group'] = group.pk
        data['type'] = group.type
        data['course_id'] = group.course.pk
        data['semester'] = group.course.semester.pk
        data['groups'] = Group.objects.filter(
            type=group.type, course=group.course).order_by('teacher')

    if data['semester']:
        data['courses'] = get_courses_for_user(request, data['semester'])
    else:
        semester_id = Semester.get_current_semester()
        data['semester'] = semester_id
        data['courses'] = get_courses_for_user(request, semester_id)

    data['studies_types'] = Program.objects.all()
    data['semesters'] = Semester.objects.all()
    data['sections'] = Section.objects.filter(deleted=False)
    data['types'] = GROUP_TYPE_CHOICES

    return data
Exemple #3
0
 def get_polls_for_semester(semester=None):
     if not semester:
         semester = Semester.get_current_semester()
     return Poll.objects.filter(semester=semester,
                                deleted=False).select_related(
                                    'group', 'group__course',
                                    'group__teacher',
                                    'group__teacher__user')
Exemple #4
0
 def get_groups_without_poll():
     semester = Semester.get_current_semester()
     polls = Poll.objects.filter(semester=semester,
                                 group__isnull=False,
                                 deleted=False).order_by('pk')
     polls = [p.group_id for p in polls]
     groups = Group.objects.filter(course__semester=semester).order_by('pk')
     return [g for g in groups if g.pk not in polls]
Exemple #5
0
    def count_current_semester_polls_without_keys():
        from apps.grade.ticket_create.models.public_key import PublicKey

        semester = Semester.get_current_semester()
        polls_with_keys = PublicKey.objects.all().values('poll')
        return Poll.objects.filter(
            semester=semester,
            deleted=False).exclude(pk__in=polls_with_keys).count()
    def handle(self, *args, **kwargs):
        semester_id = kwargs["semester"]
        created, skipped = 0, 0

        if semester_id:
            semester = Semester.objects.get(id=semester_id)
        else:
            semester = Semester.get_current_semester()

        self.stdout.write(f"Selected semester: `{semester}` with id {semester.id}")

        # Check whether poll exists for a selected semester
        self.stdout.write(f"\n{HEADER}Semester polls")
        semester_poll = Poll.objects.filter(semester=semester).count() > 0
        if semester_poll:
            self.stdout.write(f"{MARGIN}Poll for a selected semester already exists")
            skipped += 1
        else:
            new_poll = Poll(group=None, course=None, semester=semester)
            new_poll.save()
            self.stdout.write(
                f"{CREATED}Poll for a selected semester does not exist, creating"
            )
            created += 1

        # Check whether poll exists for courses held in a selected semester
        self.stdout.write(f"\n{HEADER}Course/group polls")
        courses = CourseInstance.objects.filter(semester=semester)

        for course in courses:
            course_poll = Poll.objects.filter(course=course).count() > 0
            if not course.has_exam or course_poll:
                self.stdout.write(f"{MARGIN}{course}")
                skipped += 1
            else:
                new_poll = Poll(group=None, course=course, semester=None)
                new_poll.save()
                self.stdout.write(f"{CREATED}{course}")
                created += 1

            groups = Group.objects.filter(course=course)

            for group in groups:
                group_poll = Poll.objects.filter(group=group).count() > 0
                if group_poll:
                    self.stdout.write(f"{MARGIN}{MARGIN}{group}")
                    skipped += 1
                else:
                    new_poll = Poll(group=group, course=None, semester=None)
                    new_poll.save()
                    self.stdout.write(f"{CREATED}{MARGIN}{group}")
                    created += 1

        self.stdout.write(f"\n{HEADER}Summary")
        self.stdout.write(f"{MARGIN}Created: {created}, skipped: {skipped}")
Exemple #7
0
    def changelist_view(self, request, extra_context=None):

        if 'course__semester__id__exact' not in request.GET:

            q = request.GET.copy()
            semester = Semester.get_current_semester()
            q['course__semester__id__exact'] = semester.id
            request.GET = q
            request.META['QUERY_STRING'] = request.GET.urlencode()
        return super(GroupAdmin,
                     self).changelist_view(request,
                                           extra_context=extra_context)
Exemple #8
0
def make_template_variables(request):
    """
        parse POST for template datas
        @author mjablonski

        @param request
        @return dictionary - templates option
    """
    var = {}

    if request.POST.get('title', '') == '':
        raise NoTitleException

    type = int(request.POST.get('type', 0))
    if not type:
        type = None

    studies_type = int(request.POST.get('studies-type', -1))
    if studies_type > -1:
        studies_type = Program.objects.get(pk=studies_type)
    else:
        studies_type = None

    course = int(request.POST.get('course', 0))
    if course > 0:
        course = CourseInstance.objects.get(pk=course)
    elif not course:
        course = None
    else:
        course = -1

    group = int(request.POST.get('group', 0))
    if group > 0:
        group = Group.objects.filter(pk=group)
    else:
        group = None

    semester = int(request.POST.get('semester', 0))
    if semester > 0:
        semester = Semester.objects.get(pk=semester)
    else:
        semester = Semester.get_current_semester()
    var['type'] = type
    var['studies_type'] = studies_type
    var['course'] = course
    var['title'] = request.POST.get('title', '')
    var['semester'] = semester
    var['description'] = request.POST.get('description', '')
    var['groups_without'] = request.POST.get('poll-only-without', 'off')
    var['group'] = group
    var['exam'] = request.POST.get('exam', False)
    return var
Exemple #9
0
def session(request, semester=None):
    from apps.enrollment.courses.models.semester import Semester

    exams_filter = ExamFilter(request.GET, queryset=Term.get_exams())

    if semester:
        semester = Semester.get_by_id(semester)
    else:
        semester = Semester.get_current_semester()

    return TemplateResponse(request, 'schedule/session.html', {
        "semester": semester,
        "exams": exams_filter.qs
    })
Exemple #10
0
    def get_all_polls_for_student(student: Student,
                                  semester: Semester = None) -> List:
        """Checks the eligibility of a student and returns a list of polls.

        :param student: a valid instance of `Student` class.
        :param semester: if no semester is given, assumes a current one.
        :returns: a list of all available polls for a given student.
        """
        if not student.is_active:
            return []

        current_semester = semester
        if current_semester is None:
            current_semester = Semester.get_current_semester()
        if current_semester is None:
            return []
        if not current_semester.is_grade_active:
            return []

        polls = []
        poll_for_semester = Poll.objects.filter(
            semester=current_semester).first()
        if poll_for_semester:
            polls.append(poll_for_semester)

        # Retrieves only the groups that the Student is enrolled in
        records = records_models.Record.objects.filter(
            student=student,
            status=records_models.RecordStatus.ENROLLED,
            group__course__semester=current_semester,
        ).select_related('group')

        for record in records:
            group = record.group
            course = group.course
            semester = course.semester

            if semester == current_semester:
                if course.has_exam:
                    poll_for_course = Poll.objects.get(course=course)
                    if poll_for_course and poll_for_course not in polls:
                        polls.append(poll_for_course)

                poll_for_group = Poll.objects.get(group=group)
                if poll_for_group:
                    polls.append(poll_for_group)

        return polls
Exemple #11
0
def tickets_generate(request):
    """Renders tickets_generate page and lists Polls student is entitled to."""
    semester = Semester.get_current_semester()
    is_grade_active = semester.is_grade_active
    if not is_grade_active:
        messages.error(
            request,
            "Ocena zajęć jest w tej chwili zamknięta; nie można pobrać biletów"
        )
        return redirect('grade-main')
    polls = get_grouped_polls(request.user.student)
    data = {
        'polls': polls,
        'is_grade_active': is_grade_active,
    }
    return render(request, 'ticket_create/tickets_generate.html', data)
Exemple #12
0
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        classrooms = Classroom.objects.filter(can_reserve=True)
        by_floor = defaultdict(list)
        floor_names = dict(floors)
        for r in classrooms:
            by_floor[floor_names[r.floor]].append((r.pk, r.number))
        self.fields['rooms'].choices = by_floor.items()

        semester = Semester.get_current_semester()
        next_sem = Semester.objects.get_next()
        weeks = [(week[0], f"{week[0]} - {week[1]}") for week in semester.get_all_weeks()]
        if semester != next_sem:
            weeks.insert(0, ('nextsem', f"Generuj z planu zajęć dla semestru '{next_sem}'"))
        weeks.insert(0, ('currsem', f"Generuj z planu zajęć dla semestru '{semester}'"))
        self.fields['week'].widget.choices = weeks
Exemple #13
0
def generate_keys_for_polls(semester=None):
    from apps.enrollment.courses.models.semester import Semester
    if not semester:
        semester = Semester.get_current_semester()
    poll_list = Poll.get_polls_without_keys(semester)
    pub_list = []
    priv_list = []
    i = 1
    for el in poll_list:
        (pub, priv) = generate_rsa_key()
        pub_list.append(pub)
        priv_list.append(priv)
        i = i + 1
    save_public_keys(list(zip(poll_list, pub_list)))
    save_private_keys(list(zip(poll_list, priv_list)))
    print(i - 1)
    return
Exemple #14
0
    def __init__(self, user, data=None, **kwargs):

        if data:
            data = deepcopy(data)
            if 'type' not in data:
                data['type'] = Event.TYPE_GENERIC

        super(EventForm, self).__init__(data, **kwargs)
        if not self.instance.pk:
            self.instance.author = user
        if BaseUser.is_employee(user):
            self.fields['type'].choices = Event.TYPES_FOR_TEACHER
        else:
            self.fields['type'].choices = Event.TYPES_FOR_STUDENT

        if not BaseUser.is_employee(user):
            self.fields['course'].queryset = CourseInstance.objects.none()
        else:
            semester = Semester.get_current_semester()

            previous_semester = Semester.get_semester(datetime.now().date() -
                                                      timedelta(days=30))

            queryset = CourseInstance.objects.filter(semester__in=[semester, previous_semester]). \
                select_related('semester'). \
                order_by('semester')

            if not user.has_perm('schedule.manage_events'):
                queryset = CourseInstance.objects.filter(
                    groups__type='1',
                    groups__teacher=user.employee,
                    semester__in=[semester, previous_semester])

            self.fields['course'].queryset = queryset

        self.fields['title'].widget.attrs.update({'class': 'form-control'})
        self.fields['type'].widget.attrs.update({'class': 'form-control'})
        self.fields['course'].widget.attrs.update({'class': 'form-control'})
        self.fields['description'].widget.attrs.update(
            {'class': 'form-control'})
        self.fields['visible'].widget.attrs.update({
            'checked':
            '',
            'class':
            'custom-control-input'
        })
Exemple #15
0
    def get_all_polls_for_semester(user,
                                   semester: Semester = None) -> List['Poll']:
        """Returns all polls that user may see the submissions for.

        The polls will be annotated with submission counts.
        """
        current_semester = semester
        if current_semester is None:
            current_semester = Semester.get_current_semester()

        is_superuser = user.is_superuser
        is_employee = user.employee

        if not is_superuser and not is_employee:
            return Poll.objects.none()
        poll_for_semester = Poll.objects.filter(semester=current_semester)

        if is_superuser:
            polls_for_courses = Poll.objects.filter(
                course__semester=current_semester)
            polls_for_groups = Poll.objects.filter(
                group__course__semester=current_semester)
        elif is_employee:
            polls_for_courses = Poll.objects.filter(
                course__semester=current_semester,
                course__owner=user.employee,
            ) | Poll.objects.filter(group__course__semester=current_semester,
                                    group__course__owner=user.employee)
            polls_for_groups = Poll.objects.filter(
                group__course__semester=current_semester,
                group__teacher=user.employee,
            )

        qs = poll_for_semester | polls_for_courses | polls_for_groups

        sub_count_ann = models.Count(
            'submission', filter=models.Q(submission__submitted=True))
        return list(qs.annotate(number_of_submissions=sub_count_ann))
 def this_semester(self):
     return self.filter(semester=Semester.get_current_semester())
Exemple #17
0
def sign_tickets(request):
    """Signs the tickets sent by a student and returns list of signatures.

    The student must request to sign all the tickets he is entitled to. The
    signing may only be performed once in a semester.

    Request must be a dict of objects:
    {
        "signing_requests": [
            # An object for every poll.
            {
                # Id of the poll.
                "id": 123,
                # A blinded ticket.
                "ticket": 12345...,
            },
        ]
    }

    Returns:
        A list of signed tickets, each being a dict like below.
        {
            # Id of the poll.
            'id': 123,
            # The ticket from the request signed using a private key
            # corresponding to the poll.
            'signature': 1234...
        }

    Errors:
        400 BadRequest: When JSON request could not be parsed.
        403 Forbidden: When the student tries to sign a ticket for a poll he is
        not entitled to, fails to request a ticket he is entitled to, or has
        already been signing this semester.
    """
    try:
        signing_requests_dict = json.loads(request.body.decode('utf-8'))
    except json.decoder.JSONDecodeError:
        return HttpResponseBadRequest("Couldn't parse JSON")

    signing_requests = signing_requests_dict['signing_requests']
    semester = Semester.get_current_semester()
    student_polls = {
        poll.pk
        for poll in Poll.get_all_polls_for_student(request.user.student,
                                                   semester)
    }
    request_polls = {int(req['id']) for req in signing_requests}

    if request_polls - student_polls:
        return HttpResponseForbidden(
            f"Student nie jest upoważniony do tych ankiet: {request_polls - student_polls}."
        )
    if student_polls - request_polls:
        return HttpResponseForbidden(
            f"Student powinien również wygenerować bilety dla tych "
            "ankiet: {student_polls - request_polls}.")
    if len(request_polls) != len(signing_requests):
        return HttpResponseForbidden(
            f"Próbowano podpisać wiele biletów do jednej ankiety.")
    # Now we made sure that student_polls == request_polls

    # We obtain a lock on RSA keys.
    keys = RSAKeys.objects.filter(poll__in=request_polls).select_for_update()
    keys_by_poll = {key.poll_id: key for key in keys}

    _, created = StudentGraded.objects.get_or_create(
        student=request.user.student, semester=semester)
    if not created:
        return HttpResponseForbidden(
            f"Student już podpisywał bilety w tym semestrze.")

    response = []
    for signing_request in signing_requests:
        key = keys_by_poll[int(signing_request['id'])]
        signed_ticket = key.sign_ticket(signing_request['ticket'])
        signing_response = {
            'id': signing_request['id'],
            'signature': str(signed_ticket),
        }
        response.append(signing_response)
    return JsonResponse(response, safe=False)
Exemple #18
0
    def __init__(self, data=None, *args, **kwargs):
        if not data:
            semester = Semester.get_current_semester()
            data = {'event__course__semester': semester.id}

        super(ExamFilter, self).__init__(data, *args, **kwargs)
Exemple #19
0
def check_grade_status() -> bool:
    """Checks whether any of the semesters has grade enabled."""
    current_semester = Semester.get_current_semester()
    return current_semester.is_grade_active
Exemple #20
0
 def get_all_polls_for_group(group, semester=None):
     if not semester:
         semester = Semester.get_current_semester()
     return Poll.objects.filter(semester=semester,
                                group=group,
                                deleted=False)
    def handle(self, *args, **options):
        semester = Semester.get_current_semester()

        freedays = Freeday.objects.filter(Q(day__gte=semester.lectures_beginning),
                                          Q(day__lte=semester.lectures_ending))\
            .values_list('day', flat=True)
        changed = ChangedDay.objects.filter(
            Q(day__gte=semester.lectures_beginning),
            Q(day__lte=semester.lectures_ending)).values_list(
                'day', 'weekday')
        days = {0: [], 1: [], 2: [], 3: [], 4: [], 5: [], 6: []}

        day = semester.lectures_beginning

        s4 = Classroom.objects.get(number='4')
        s5 = Classroom.objects.get(number='5')
        s25 = Classroom.objects.get(number='25')
        s141 = Classroom.objects.get(number='141')
        s139 = Classroom.objects.get(number='139')
        s140 = Classroom.objects.get(number='140')
        s103 = Classroom.objects.get(number='103')
        s104 = Classroom.objects.get(number='104')
        s105 = Classroom.objects.get(number='105')
        s108 = Classroom.objects.get(number='108')
        s110 = Classroom.objects.get(number='110')
        s119 = Classroom.objects.get(number='119')
        s237 = Classroom.objects.get(number='237')
        s310 = Classroom.objects.get(number='310')

        while day <= semester.lectures_ending:

            if day in freedays:
                day = day + datetime.timedelta(days=1)
                continue

            weekday = day.weekday()

            for d in changed:
                if d[0] == day:
                    weekday = int(d[1]) - 1
                    break

            days[weekday].append(day)

            day = day + datetime.timedelta(days=1)

        def create_event(title, visible=True):
            ev = Event()
            ev.title = title
            ev.type = '4'
            ev.visible = visible
            ev.status = '1'
            ev.author_id = 1
            ev.save()

            return ev

        def create_term(event,
                        day,
                        start,
                        end,
                        room,
                        minutes_start=0,
                        minutes_end=0):
            newTerm = Term()
            newTerm.event = event
            newTerm.day = day
            newTerm.start = datetime.time(hour=start, minute=minutes_start)
            newTerm.end = datetime.time(hour=end, minute=minutes_end)
            newTerm.room = room
            newTerm.save()

        for d in days[4]:  # piatek
            ev = create_event('Kolokwia')
            create_term(ev, d, 14, 16, s25)

            ev = create_event('Jezyk Angielski')
            create_term(ev, d, 12, 15, s4)

            ev = create_event('INSTYTUT DZIENNIKARSTWA')
            create_term(ev, d, 15, 20, s141)
            create_term(ev, d, 15, 20, s140)
            create_term(ev, d, 15, 20, s139)
            create_term(ev, d, 15, 20, s103)
            create_term(ev, d, 15, 20, s104)
            create_term(ev, d, 15, 20, s4)
            create_term(ev, d, 15, 20, s5)

            ev = create_event('SEMINARIUM ZJP')
            create_term(ev, d, 13, 15, s105)

        for d in days[5]:  # sobota

            ev = create_event('INSTYTUT DZIENNIKARSTWA')
            create_term(ev, d, 9, 18, s141)
            create_term(ev, d, 9, 18, s140)
            create_term(ev, d, 9, 18, s139)
            create_term(ev, d, 9, 18, s103)
            create_term(ev, d, 9, 18, s104)
            create_term(ev, d, 9, 18, s5)
            create_term(ev, d, 9, 18, s4)

        for d in days[6]:  # niedziela

            ev = create_event('INSTYTUT DZIENNIKARSTWA')
            create_term(ev, d, 9, 18, s141)
            create_term(ev, d, 9, 18, s140)
            create_term(ev, d, 9, 18, s139)
            create_term(ev, d, 9, 18, s103)
            create_term(ev, d, 9, 18, s104)
            create_term(ev, d, 9, 18, s5)
            create_term(ev, d, 9, 18, s4)

        for d in days[0]:  # poniedzialke

            ev = create_event('Jezyk Angielski')
            create_term(ev, d, 8, 12, s4)

            ev = create_event('INSTYTUT MATEMATYCZNY')
            create_term(ev, d, 10, 12, s104)

        for d in days[1]:  # wtorek
            ev = create_event('JUG')
            create_term(ev, d, 18, 21, s25, minutes_start=30)

            ev = create_event('SEMINARIUM ZMN')
            create_term(ev, d, 14, 16, s104)
            create_term(ev, d, 14, 16, s237)

            ev = create_event('INSTYTUT DZIENNIKARSTWA')
            create_term(ev, d, 12, 14, s4)

            ev = create_event('JUG')
            create_term(ev, d, 18, 21, s25, 30)

            ev = create_event('Grupa .NET')
            create_term(ev, d, 18, 20, s119)

        for d in days[2]:  # sroda

            ev = create_event('SEMINARIUM PGK')
            create_term(ev, d, 12, 14, s105)

            ev = create_event('INSTYTUT DZIENNIKARSTWA')
            create_term(ev, d, 10, 12, s4)

        for d in days[3]:  # czwartek

            ev = create_event('SEMINARIUM PIO')
            create_term(ev, d, 16, 18, s237)

            ev = create_event('SEMINARIUM ZZOIA')
            create_term(ev, d, 12, 14, s119)

            ev = create_event('SEMINARIUM INSTYTUTOWE')
            create_term(ev, d, 14, 16, s119)

            ev = create_event('INSTYTUT DZIENNIKARSTWA')
            create_term(ev, d, 8, 10, s4)
            create_term(ev, d, 12, 14, s4)
            create_term(ev, d, 16, 20, s5)
Exemple #22
0
    def get(self, request, semester_id=None, poll_id=None, submission_id=None):
        """Controls the main logic of passing the data to the template
        responsible for presenting the results of the poll.

        :param semester_id: if given, fetches polls from requested semester.
        :param poll_id: if given, displays summary for a given poll.
        :param submission_id: if given, displays detailed submission view.
        """
        is_grade_active = check_grade_status()
        if semester_id is None:
            semester_id = Semester.get_current_semester().id
        current_semester = Semester.get_current_semester()
        selected_semester = Semester.objects.filter(pk=semester_id).get()

        available_polls = Poll.get_all_polls_for_semester(
            user=request.user, semester=selected_semester)
        current_poll = Poll.objects.filter(id=poll_id).first()
        if poll_id is not None:
            submissions = Submission.objects.filter(
                poll=poll_id, submitted=True).order_by('modified')
            if current_poll not in available_polls:
                # User does not have permission to view details about
                # the selected poll
                messages.error(
                    request, "Nie masz uprawnień do wyświetlenia tej ankiety.")
                return redirect('grade-poll-results', semester_id=semester_id)
        else:
            submissions = []

        semesters = Semester.objects.all()

        if request.user.is_superuser or request.user.employee:
            return render(
                request,
                self.template_name,
                {
                    'is_grade_active':
                    is_grade_active,
                    'polls':
                    group(entries=available_polls, sort=True),
                    'results':
                    self.__get_processed_results(submissions),
                    'results_iterator':
                    itertools.count(),
                    'semesters':
                    semesters,
                    'current_semester':
                    current_semester,
                    'current_poll_id':
                    poll_id,
                    'current_poll':
                    current_poll,
                    'selected_semester':
                    selected_semester,
                    'submissions_count':
                    self.__get_counter_for_categories(available_polls),
                    'iterator':
                    itertools.count(),
                },
            )

        messages.error(request,
                       "Nie masz uprawnień do wyświetlania wyników oceny.")
        return redirect('grade-main')
Exemple #23
0
def display_report(request, form, report_type: 'Literal["table", "doors"]'):
    class ListEvent(NamedTuple):
        date: Optional[datetime.datetime]
        weekday: int  # Monday is 1, Sunday is 7 like in
        # https://docs.python.org/3/library/datetime.html#datetime.date.isoweekday.
        begin: datetime.time
        end: datetime.time
        room: Classroom
        title: str
        type: str
        author: str

    rooms = set(Classroom.objects.filter(id__in=form.cleaned_data['rooms']))
    events: List[ListEvent] = []
    # Every event will regardless of its origin be translated into a ListEvent.
    beg_date = form.cleaned_data.get('beg_date', None)
    end_date = form.cleaned_data.get('end_date', None)
    semester = None
    if form.cleaned_data.get('week', None) == 'currsem':
        semester = Semester.get_current_semester()
    elif form.cleaned_data.get('week', None) == 'nextsem':
        semester = Semester.objects.get_next()
    if semester:
        terms = CourseTerm.objects.filter(
            group__course__semester=semester,
            classrooms__in=rooms).distinct().select_related(
                'group__course', 'group__teacher',
                'group__teacher__user').prefetch_related('classrooms')
        for term in terms:
            for r in set(term.classrooms.all()) & rooms:
                events.append(
                    ListEvent(date=None,
                              weekday=int(term.dayOfWeek),
                              begin=term.start_time,
                              end=term.end_time,
                              room=r,
                              title=term.group.course.name,
                              type=term.group.human_readable_type(),
                              author=term.group.teacher.get_full_name()))
        terms = SpecialReservation.objects.filter(
            semester=semester, classroom__in=rooms).select_related('classroom')
        for term in terms:
            events.append(
                ListEvent(date=None,
                          weekday=int(term.dayOfWeek),
                          begin=term.start_time,
                          end=term.end_time,
                          room=term.classroom,
                          title=term.title,
                          type="",
                          author=""))
    elif 'week' in form.cleaned_data:
        beg_date = datetime.datetime.strptime(form.cleaned_data['week'],
                                              "%Y-%m-%d")
        end_date = beg_date + datetime.timedelta(days=6)
    if beg_date and end_date:
        terms = Term.objects.filter(
            day__gte=beg_date,
            day__lte=end_date,
            room__in=rooms,
            event__status=Event.STATUS_ACCEPTED).select_related(
                'room', 'event', 'event__group', 'event__author')
        for term in terms:
            events.append(
                ListEvent(date=term.day,
                          weekday=term.day.isoweekday(),
                          begin=term.start,
                          end=term.end,
                          room=term.room,
                          title=term.event.title or str(term.event.course)
                          or "",
                          type=term.event.group.human_readable_type() if
                          term.event.group else term.event.get_type_display(),
                          author=term.event.author.get_full_name()))

    if report_type == 'table':
        events = sorted(events,
                        key=operator.attrgetter('room.id', 'date', 'begin'))
    else:
        events = sorted(events,
                        key=operator.attrgetter('room.id', 'weekday', 'begin'))
    terms_by_room = groupby(events, operator.attrgetter('room.number'))
    terms_by_room = sorted([(int(k), list(g)) for k, g in terms_by_room])

    return render(
        request, f'schedule/reports/report_{report_type}.html', {
            'events': terms_by_room,
            'semester': semester,
            'beg_date': beg_date,
            'end_date': end_date,
        })