def mark_attendance(request, **kwargs): serializer = AttendanceMarkSerializer(data=request.data) serializer.is_valid(raise_exception=True) trainer = request.user # trainer.pk == trainer.user.pk try: training = Training.objects.select_related("group").only( "group__trainer", "start", "end").get(pk=serializer.validated_data["training_id"]) except Training.DoesNotExist: raise NotFound() is_training_group(training.group, trainer) now = timezone.now() if not training.start <= now <= training.start + \ settings.TRAINING_EDITABLE_INTERVAL: return Response( status=status.HTTP_400_BAD_REQUEST, data=error_detail(*AttendanceErrors.TRAINING_NOT_EDITABLE)) id_to_hours = dict([(item["student_id"], item["hours"]) for item in serializer.validated_data["students_hours"] ]) max_hours = training.academic_duration students = User.objects.filter(pk__in=id_to_hours.keys()).only("email") hours_to_mark = [] negative_mark = [] overflow_mark = [] for student in students: hours_put = id_to_hours[student.pk] if hours_put < 0: negative_mark.append( compose_bad_grade_report(student.email, hours_put)) elif hours_put > max_hours: overflow_mark.append( compose_bad_grade_report(student.email, hours_put)) else: hours_to_mark.append((student, hours_put)) if negative_mark or overflow_mark: return Response(status=status.HTTP_400_BAD_REQUEST, data={ **error_detail(*AttendanceErrors.OUTBOUND_GRADES), "negative_marks": negative_mark, "overflow_marks": overflow_mark, }) else: mark_data = [(x[0].pk, x[1]) for x in hours_to_mark] mark_hours(training, mark_data) return Response( list( map(lambda x: compose_bad_grade_report(x[0].email, x[1]), hours_to_mark)))
def mark_attendance( training: Training, student_hours: Iterable[Tuple[User, float]], ): mark_hours( training, [ (data[0].pk, data[1]) for data in student_hours ] )
def save_related(self, request, form, formsets, change): """ There are three ways of creating/changing attendance records (from highest priority to lowest): 1) Upload CSV file 2) Use ModelMultipleChoiceField in ModelAdmin for training 3) Change/add attendance records using inline """ super().save_related(request, form, formsets, change) training = form.instance similar_student_hours = [ (student.pk, form.cleaned_data['hours']) for student in form.cleaned_data['attended_students'] ] xlsx_student_hours = [ (student.pk, hours) for (student, hours) in form.cleaned_data['attendances'] ] mark_hours(training, OrderedDict(similar_student_hours + xlsx_student_hours).items()) return training
def save_related(self, request, form, formsets, change): """ There are three ways of creating/changing attendance records (from highest priority to lowest): 1) Upload CSV file 2) Use ModelMultipleChoiceField in ModelAdmin for training 3) Change/add attendance records using inline """ if 'add-extra-multi' in request.path: for attendance_form in formsets[0].forms: attendance_form.instance.student = form.cleaned_data['student'] start = datetime.datetime.combine( datetime.date.fromisoformat( attendance_form['date'].value()), datetime.time(0, 0, 0), tzinfo=timezone.localtime().tzinfo) end = datetime.datetime.combine( datetime.date.fromisoformat( attendance_form['date'].value()), datetime.time(23, 59, 59), tzinfo=timezone.localtime().tzinfo) training = Training.objects.create(group=form.instance.group, start=start, end=end) attendance_form.instance.training = training training.save(False) attendance_form.instance.save(False) return None super().save_related(request, form, formsets, change) training = form.instance similar_student_hours = [ (student.pk, form.cleaned_data['hours']) for student in form.cleaned_data['attended_students'] ] xlsx_student_hours = [(student.pk, hours) for (student, hours) in form.cleaned_data['attendances']] mark_hours( training, OrderedDict(similar_student_hours + xlsx_student_hours).items()) return training
def test_mark_hours(student_factory, sport_factory, semester_factory, group_factory, training_factory): student1 = student_factory("A").student student2 = student_factory("B").student sport = sport_factory(name="Sport") semester = semester_factory(name="S19", start=dummy_date, end=dummy_date) group = group_factory(name="G1", sport=sport, semester=semester, capacity=20) training = training_factory(group=group, start=timezone.now(), end=timezone.now()) mark_hours(training, [ (student1.pk, 1.5), (student2.pk, 2.5) ]) attendance1 = Attendance.objects.get(student=student1) attendance2 = Attendance.objects.get(student=student2) assert attendance1.hours == 1.5 assert attendance2.hours == 2.5 mark_hours(training, [ (student1.pk, 3.5), (student2.pk, 0) ]) attendance1 = Attendance.objects.get(student=student1) assert attendance1.hours == 3.5 assert Attendance.objects.count() == 1