def store_in_database(self, vote_start_datetime, vote_end_date, semester): course_type = CourseType.objects.get(name_de=self.type_name) # This is safe because the user's email address is checked before in the importer (see #953) responsible_dbobj = UserProfile.objects.get( email=self.responsible_email) course = Course( name_de=self.name_de, name_en=self.name_en, type=course_type, is_graded=self.is_graded, semester=semester, ) course.save() course.responsibles.set([responsible_dbobj]) for degree_name in self.degree_names: course.degrees.add(Degree.objects.get(name_de=degree_name)) evaluation = Evaluation( vote_start_datetime=vote_start_datetime, vote_end_date=vote_end_date, course=course, ) evaluation.save() evaluation.contributions.create( contributor=responsible_dbobj, evaluation=evaluation, can_edit=True, textanswer_visibility=Contribution.TextAnswerVisibility. GENERAL_TEXTANSWERS)
def save(self, commit=True): new_course: Course = super().save() # we need to create copies of evaluations and their participation as well for old_evaluation in self.old_course.evaluations.exclude( is_single_result=True): new_evaluation = Evaluation( **{ field: getattr(old_evaluation, field) for field in self.EVALUATION_COPIED_FIELDS }, can_publish_text_results=False, course=new_course, vote_start_datetime=self.cleaned_data["vote_start_datetime"], vote_end_date=self.cleaned_data["vote_end_date"], ) new_evaluation.save() new_evaluation.contributions.all().delete( ) # delete default general contribution for old_contribution in old_evaluation.contributions.all(): new_contribution = Contribution( **{ field: getattr(old_contribution, field) for field in self.CONTRIBUTION_COPIED_FIELDS }, evaluation=new_evaluation, ) new_contribution.save() new_contribution.questionnaires.set( old_contribution.questionnaires.all()) return new_course
def store_in_database(self, vote_start_datetime, vote_end_date, semester): assert not self.errors # This is safe because the user's email address is checked before in the importer (see #953) responsible_dbobj = UserProfile.objects.get( email=self.responsible_email) course = Course( name_de=self.name_de, name_en=self.name_en, type=self.course_type, semester=semester, ) course.save() course.responsibles.set([responsible_dbobj]) course.degrees.set(self.degrees) evaluation = Evaluation( vote_start_datetime=vote_start_datetime, vote_end_date=vote_end_date, course=course, wait_for_grade_upload_before_publishing=self.is_graded, ) evaluation.save() evaluation.contributions.create( evaluation=evaluation, contributor=responsible_dbobj, role=Contribution.Role.EDITOR, textanswer_visibility=Contribution.TextAnswerVisibility. GENERAL_TEXTANSWERS, )
def test_evaluation_ended(self): # Evaluation is out of evaluation period. course_1 = baker.make(Course) course_2 = baker.make(Course) baker.make( Evaluation, course=course_1, state=Evaluation.State.IN_EVALUATION, vote_start_datetime=datetime.now() - timedelta(days=2), vote_end_date=date.today() - timedelta(days=1), wait_for_grade_upload_before_publishing=False, ) # This evaluation is not. baker.make( Evaluation, course=course_2, state=Evaluation.State.IN_EVALUATION, vote_start_datetime=datetime.now() - timedelta(days=2), vote_end_date=date.today(), wait_for_grade_upload_before_publishing=False, ) with patch("evap.evaluation.models.Evaluation.end_evaluation") as mock: Evaluation.update_evaluations() self.assertEqual(mock.call_count, 1)
def test_in_evaluation_to_reviewed(self): # Evaluation is "fully reviewed" as no open text answers are present by default. evaluation = mommy.make(Evaluation, state='in_evaluation', vote_start_datetime=datetime.now() - timedelta(days=2), vote_end_date=date.today() - timedelta(days=1)) Evaluation.update_evaluations() evaluation = Evaluation.objects.get(pk=evaluation.pk) self.assertEqual(evaluation.state, 'reviewed')
def test_in_evaluation_to_evaluated(self): evaluation = mommy.make(Evaluation, state='in_evaluation', vote_start_datetime=datetime.now() - timedelta(days=2), vote_end_date=date.today() - timedelta(days=1)) with patch('evap.evaluation.models.Evaluation.is_fully_reviewed') as mock: mock.__get__ = Mock(return_value=False) Evaluation.update_evaluations() evaluation = Evaluation.objects.get(pk=evaluation.pk) self.assertEqual(evaluation.state, 'evaluated')
def test_approved_to_in_evaluation_sends_emails(self): """ Regression test for #945 """ participant = mommy.make(UserProfile, email='*****@*****.**') evaluation = mommy.make(Evaluation, state='approved', vote_start_datetime=datetime.now(), participants=[participant]) Evaluation.update_evaluations() evaluation = Evaluation.objects.get(pk=evaluation.pk) self.assertEqual(len(mail.outbox), 1) self.assertEqual(evaluation.state, 'in_evaluation')
def test_approved_to_in_evaluation(self): evaluation = mommy.make(Evaluation, state='approved', vote_start_datetime=datetime.now()) with patch('evap.evaluation.models.EmailTemplate.send_to_users_in_evaluations') as mock: Evaluation.update_evaluations() template = EmailTemplate.objects.get(name=EmailTemplate.EVALUATION_STARTED) mock.assert_called_once_with(template, [evaluation], [EmailTemplate.ALL_PARTICIPANTS], use_cc=False, request=None) evaluation = Evaluation.objects.get(pk=evaluation.pk) self.assertEqual(evaluation.state, 'in_evaluation')
def test_approved_to_in_evaluation_sends_emails(self): """Regression test for #945""" participant = baker.make(UserProfile, email="*****@*****.**") evaluation = baker.make( Evaluation, state=Evaluation.State.APPROVED, vote_start_datetime=datetime.now(), participants=[participant] ) Evaluation.update_evaluations() evaluation = Evaluation.objects.get(pk=evaluation.pk) self.assertEqual(len(mail.outbox), 1) self.assertEqual(evaluation.state, Evaluation.State.IN_EVALUATION)
def test_in_evaluation_to_reviewed(self): # Evaluation is "fully reviewed" as no open text answers are present by default. evaluation = baker.make( Evaluation, state=Evaluation.State.IN_EVALUATION, vote_start_datetime=datetime.now() - timedelta(days=2), vote_end_date=date.today() - timedelta(days=1), ) Evaluation.update_evaluations() evaluation = Evaluation.objects.get(pk=evaluation.pk) self.assertEqual(evaluation.state, Evaluation.State.REVIEWED)
def test_in_evaluation_to_published(self): # Evaluation is "fully reviewed" and not graded, thus gets published immediately. course = mommy.make(Course, is_graded=False) evaluation = mommy.make(Evaluation, course=course, state='in_evaluation', vote_start_datetime=datetime.now() - timedelta(days=2), vote_end_date=date.today() - timedelta(days=1)) with patch('evap.evaluation.tools.send_publish_notifications') as mock: Evaluation.update_evaluations() mock.assert_called_once_with([evaluation]) evaluation = Evaluation.objects.get(pk=evaluation.pk) self.assertEqual(evaluation.state, 'published')
def test_in_evaluation_to_evaluated(self): evaluation = baker.make( Evaluation, state=Evaluation.State.IN_EVALUATION, vote_start_datetime=datetime.now() - timedelta(days=2), vote_end_date=date.today() - timedelta(days=1), ) with patch("evap.evaluation.models.Evaluation.is_fully_reviewed") as mock: mock.__get__ = Mock(return_value=False) Evaluation.update_evaluations() evaluation = Evaluation.objects.get(pk=evaluation.pk) self.assertEqual(evaluation.state, Evaluation.State.EVALUATED)
def test_evaluation_ended(self): # Evaluation is out of evaluation period. course_1 = mommy.make(Course, is_graded=False) course_2 = mommy.make(Course, is_graded=False) mommy.make(Evaluation, course=course_1, state='in_evaluation', vote_start_datetime=datetime.now() - timedelta(days=2), vote_end_date=date.today() - timedelta(days=1)) # This evaluation is not. mommy.make(Evaluation, course=course_2, state='in_evaluation', vote_start_datetime=datetime.now() - timedelta(days=2), vote_end_date=date.today()) with patch('evap.evaluation.models.Evaluation.evaluation_end') as mock: Evaluation.update_evaluations() self.assertEqual(mock.call_count, 1)
def test_single_result_form_saves_participant_and_voter_count(self): responsible = mommy.make(UserProfile) course_type = mommy.make(CourseType) evaluation = Evaluation(course=mommy.make(Course), is_single_result=True) form_data = { "name_de": "qwertz", "name_en": "qwertz", "type": course_type.pk, "degrees": [1], "event_date": "2014-01-01", "responsible": responsible.pk, "answer_1": 6, "answer_2": 0, "answer_3": 2, "answer_4": 0, "answer_5": 2, "semester": evaluation.course.semester.pk } form = SingleResultForm(form_data, instance=evaluation) self.assertTrue(form.is_valid()) form.save(user=mommy.make(UserProfile)) evaluation = Evaluation.objects.get() self.assertEqual(evaluation.num_participants, 10) self.assertEqual(evaluation.num_voters, 10)
def test_approved_to_in_evaluation(self): evaluation = baker.make(Evaluation, state=Evaluation.State.APPROVED, vote_start_datetime=datetime.now()) with patch("evap.evaluation.models.EmailTemplate") as mock: mock.EVALUATION_STARTED = EmailTemplate.EVALUATION_STARTED mock.Recipients.ALL_PARTICIPANTS = EmailTemplate.Recipients.ALL_PARTICIPANTS mock.objects.get.return_value = mock Evaluation.update_evaluations() self.assertEqual(mock.objects.get.call_args_list[0][1]["name"], EmailTemplate.EVALUATION_STARTED) mock.send_to_users_in_evaluations.assert_called_once_with( [evaluation], [EmailTemplate.Recipients.ALL_PARTICIPANTS], use_cc=False, request=None ) evaluation = Evaluation.objects.get(pk=evaluation.pk) self.assertEqual(evaluation.state, Evaluation.State.IN_EVALUATION)
def test_single_result_form_can_change_responsible(self): responsible = mommy.make(UserProfile) course_type = mommy.make(CourseType) evaluation = Evaluation(course=mommy.make(Course), is_single_result=True) form_data = { "name_de": "qwertz", "name_en": "qwertz", "type": course_type.pk, "degrees": [1], "event_date": "2014-01-01", "responsible": responsible.pk, "answer_1": 6, "answer_2": 0, "answer_3": 2, "answer_4": 0, "answer_5": 2, "semester": evaluation.course.semester.pk } form = SingleResultForm(form_data, instance=evaluation) self.assertTrue(form.is_valid()) form.save(user=mommy.make(UserProfile)) self.assertEqual(evaluation.course.responsibles.first(), responsible) new_responsible = mommy.make(UserProfile) form_data["responsible"] = new_responsible.pk form = SingleResultForm(form_data, instance=evaluation) self.assertTrue(form.is_valid()) form.save(user=mommy.make(UserProfile)) self.assertEqual(evaluation.course.responsibles.first(), new_responsible)
def test_single_result_form_saves_participant_and_voter_count(self): course = mommy.make(Course) evaluation = Evaluation(course=course, is_single_result=True) form_data = { "name_de": "qwertz", "name_en": "qwertz", "weight": 1, "event_date": "2014-01-01", "answer_1": 6, "answer_2": 0, "answer_3": 2, "answer_4": 0, "answer_5": 2, "course": course.pk } form = SingleResultForm(form_data, instance=evaluation, semester=evaluation.course.semester) self.assertTrue(form.is_valid()) form.save(user=mommy.make(UserProfile)) evaluation = Evaluation.objects.get() self.assertEqual(evaluation.num_participants, 10) self.assertEqual(evaluation.num_voters, 10)
def test_in_evaluation_to_published(self): # Evaluation is "fully reviewed" and not graded, thus gets published immediately. course = baker.make(Course, is_graded=False) evaluation = baker.make(Evaluation, course=course, state='in_evaluation', vote_start_datetime=datetime.now() - timedelta(days=2), vote_end_date=date.today() - timedelta(days=1)) with patch('evap.evaluation.models.EmailTemplate.send_participant_publish_notifications') as participant_mock,\ patch('evap.evaluation.models.EmailTemplate.send_contributor_publish_notifications') as contributor_mock: Evaluation.update_evaluations() participant_mock.assert_called_once_with([evaluation]) contributor_mock.assert_called_once_with([evaluation]) evaluation = Evaluation.objects.get(pk=evaluation.pk) self.assertEqual(evaluation.state, 'published')
def test_initial_from_original(self): evaluation = Evaluation() form = ContributionCopyForm(None, instance=self.contribution, evaluation=evaluation) self.assertEqual(form['evaluation'].initial, None) self.assertEqual(form['contributor'].initial, self.contributor.pk) self.assertCountEqual(form['questionnaires'].initial, self.questionnaires) self.assertEqual(form['order'].initial, 2) self.assertEqual(form['role'].initial, Contribution.Role.EDITOR) self.assertEqual(form['textanswer_visibility'].initial, Contribution.TextAnswerVisibility.GENERAL_TEXTANSWERS) self.assertEqual(form['label'].initial, 'Teacher') self.assertEqual(form.evaluation, evaluation)
def get_evaluations_with_prefetched_data(evaluations): if isinstance(evaluations, QuerySet): evaluations = evaluations.select_related("course__type").prefetch_related( "course__degrees", "course__semester", "course__responsibles", ) evaluations = Evaluation.annotate_with_participant_and_voter_counts(evaluations) annotate_distributions_and_grades(evaluations) return evaluations
def store_in_database(self, vote_start_datetime, vote_end_date, semester): course_type = CourseType.objects.get(name_de=self.type_name) # This is safe because the user's email address is checked before in the importer (see #953) responsible_dbobj = UserProfile.objects.get(email=self.responsible_email) course = Course( name_de=self.name_de, name_en=self.name_en, type=course_type, is_graded=self.is_graded, semester=semester, ) course.save() course.responsibles.set([responsible_dbobj]) for degree_name in self.degree_names: course.degrees.add(Degree.objects.get(name_de=degree_name)) evaluation = Evaluation( vote_start_datetime=vote_start_datetime, vote_end_date=vote_end_date, course=course, ) evaluation.save() evaluation.contributions.create(contributor=responsible_dbobj, evaluation=evaluation, can_edit=True, textanswer_visibility=Contribution.GENERAL_TEXTANSWERS)
def test_initial_from_original(self): evaluation = Evaluation() form = ContributionCopyForm(None, instance=self.contribution, evaluation=evaluation) self.assertEqual(form["evaluation"].initial, None) self.assertEqual(form["contributor"].initial, self.contributor.pk) self.assertCountEqual(form["questionnaires"].initial, self.questionnaires) self.assertEqual(form["order"].initial, 2) self.assertEqual(form["role"].initial, Contribution.Role.EDITOR) self.assertEqual(form["textanswer_visibility"].initial, Contribution.TextAnswerVisibility.GENERAL_TEXTANSWERS) self.assertEqual(form["label"].initial, "Teacher") self.assertEqual(form.evaluation, evaluation)
def test_in_evaluation_to_published(self): # Evaluation is "fully reviewed" and not graded, thus gets published immediately. course = baker.make(Course) evaluation = baker.make( Evaluation, course=course, state=Evaluation.State.IN_EVALUATION, vote_start_datetime=datetime.now() - timedelta(days=2), vote_end_date=date.today() - timedelta(days=1), wait_for_grade_upload_before_publishing=False, ) with patch( "evap.evaluation.models.EmailTemplate.send_participant_publish_notifications" ) as participant_mock, patch( "evap.evaluation.models.EmailTemplate.send_contributor_publish_notifications" ) as contributor_mock: Evaluation.update_evaluations() participant_mock.assert_called_once_with([evaluation]) contributor_mock.assert_called_once_with([evaluation]) evaluation = Evaluation.objects.get(pk=evaluation.pk) self.assertEqual(evaluation.state, Evaluation.State.PUBLISHED)
def get_evaluations_with_prefetched_data(evaluations): if isinstance(evaluations, QuerySet): evaluations = (evaluations .select_related("course__type") .prefetch_related( "course__degrees", "course__semester", "course__responsibles", ) ) evaluations = Evaluation.annotate_with_participant_and_voter_counts(evaluations) for evaluation in evaluations: if not evaluation.is_single_result: evaluation.distribution = calculate_average_distribution(evaluation) else: evaluation.single_result_rating_result = get_single_result_rating_result(evaluation) evaluation.distribution = normalized_distribution(evaluation.single_result_rating_result.counts) evaluation.avg_grade = distribution_to_grade(evaluation.distribution) return evaluations
def index(request): query = (Evaluation.objects.annotate(participates_in=Exists( Evaluation.objects.filter(id=OuterRef("id"), participants=request.user) )).annotate(voted_for=Exists( Evaluation.objects.filter(id=OuterRef( "id"), voters=request.user))).filter( ~Q(state=Evaluation.State.NEW), course__evaluations__participants=request.user).exclude( state=Evaluation.State.NEW).prefetch_related( "course", "course__semester", "course__grade_documents", "course__type", "course__evaluations", "course__responsibles", "course__degrees", ).distinct()) query = Evaluation.annotate_with_participant_and_voter_counts(query) evaluations = [ evaluation for evaluation in query if evaluation.can_be_seen_by(request.user) ] inner_evaluation_ids = [ inner_evaluation.id for evaluation in evaluations for inner_evaluation in evaluation.course.evaluations.all() ] inner_evaluation_query = Evaluation.objects.filter( pk__in=inner_evaluation_ids) inner_evaluation_query = Evaluation.annotate_with_participant_and_voter_counts( inner_evaluation_query) evaluations_by_id = { evaluation["id"]: evaluation for evaluation in inner_evaluation_query.values() } for evaluation in evaluations: for inner_evaluation in evaluation.course.evaluations.all(): inner_evaluation.num_voters = evaluations_by_id[ inner_evaluation.id]["num_voters"] inner_evaluation.num_participants = evaluations_by_id[ inner_evaluation.id]["num_participants"] annotate_distributions_and_grades(e for e in evaluations if e.state == Evaluation.State.PUBLISHED) evaluations = get_evaluations_with_course_result_attributes(evaluations) # evaluations must be sorted for regrouping them in the template evaluations.sort( key=lambda evaluation: (evaluation.course.name, evaluation.name)) semesters = Semester.objects.all() semester_list = [ dict( semester_name=semester.name, id=semester.id, results_are_archived=semester.results_are_archived, grade_documents_are_deleted=semester.grade_documents_are_deleted, evaluations=[ evaluation for evaluation in evaluations if evaluation.course.semester_id == semester.id ], ) for semester in semesters ] unfinished_evaluations_query = (Evaluation.objects.filter( participants=request.user, state__in=[ Evaluation.State.PREPARED, Evaluation.State.EDITOR_APPROVED, Evaluation.State.APPROVED, Evaluation.State.IN_EVALUATION, ], ).exclude(voters=request.user).prefetch_related("course__responsibles", "course__type", "course__semester")) unfinished_evaluations_query = Evaluation.annotate_with_participant_and_voter_counts( unfinished_evaluations_query) unfinished_evaluations = list(unfinished_evaluations_query) # available evaluations come first, ordered by time left for evaluation and the name # evaluations in other (visible) states follow by name def sorter(evaluation): return ( evaluation.state != Evaluation.State.IN_EVALUATION, evaluation.vote_end_date if evaluation.state == Evaluation.State.IN_EVALUATION else None, evaluation.full_name, ) unfinished_evaluations.sort(key=sorter) template_data = dict( semester_list=semester_list, can_download_grades=request.user.can_download_grades, unfinished_evaluations=unfinished_evaluations, evaluation_end_warning_period=settings.EVALUATION_END_WARNING_PERIOD, ) return render(request, "student_index.html", template_data)
def handle(self, *args, **options): Evaluation.update_evaluations()
def index(request): query = (Evaluation.objects .annotate(participates_in=Exists(Evaluation.objects.filter(id=OuterRef('id'), participants=request.user))) .annotate(voted_for=Exists(Evaluation.objects.filter(id=OuterRef('id'), voters=request.user))) .filter(~Q(state="new"), course__evaluations__participants=request.user) .exclude(state="new") .prefetch_related( 'course', 'course__semester', 'course__grade_documents', 'course__type', 'course__evaluations', 'course__responsibles', 'course__degrees', ) .distinct() ) query = Evaluation.annotate_with_participant_and_voter_counts(query) evaluations = [evaluation for evaluation in query if evaluation.can_be_seen_by(request.user)] inner_evaluation_ids = [inner_evaluation.id for evaluation in evaluations for inner_evaluation in evaluation.course.evaluations.all()] inner_evaluation_query = Evaluation.objects.filter(pk__in=inner_evaluation_ids) inner_evaluation_query = Evaluation.annotate_with_participant_and_voter_counts(inner_evaluation_query) evaluations_by_id = {evaluation['id']: evaluation for evaluation in inner_evaluation_query.values()} for evaluation in evaluations: for inner_evaluation in evaluation.course.evaluations.all(): inner_evaluation.num_voters = evaluations_by_id[inner_evaluation.id]['num_voters'] inner_evaluation.num_participants = evaluations_by_id[inner_evaluation.id]['num_participants'] for evaluation in evaluations: if evaluation.state == "published": if not evaluation.is_single_result: evaluation.distribution = calculate_average_distribution(evaluation) else: evaluation.single_result_rating_result = get_single_result_rating_result(evaluation) evaluation.distribution = normalized_distribution(evaluation.single_result_rating_result.counts) evaluation.avg_grade = distribution_to_grade(evaluation.distribution) evaluations = get_evaluations_with_course_result_attributes(evaluations) # evaluations must be sorted for regrouping them in the template evaluations.sort(key=lambda evaluation: (evaluation.course.name, evaluation.name)) semesters = Semester.objects.all() semester_list = [dict( semester_name=semester.name, id=semester.id, results_are_archived=semester.results_are_archived, grade_documents_are_deleted=semester.grade_documents_are_deleted, evaluations=[evaluation for evaluation in evaluations if evaluation.course.semester_id == semester.id] ) for semester in semesters] unfinished_evaluations_query = ( Evaluation.objects .filter(participants=request.user, state__in=['prepared', 'editor_approved', 'approved', 'in_evaluation']) .exclude(voters=request.user) .prefetch_related('course__responsibles', 'course__type', 'course__semester') ) unfinished_evaluations_query = Evaluation.annotate_with_participant_and_voter_counts(unfinished_evaluations_query) unfinished_evaluations = list(unfinished_evaluations_query) # available evaluations come first, ordered by time left for evaluation and the name # evaluations in other (visible) states follow by name def sorter(evaluation): return ( evaluation.state != 'in_evaluation', evaluation.vote_end_date if evaluation.state == 'in_evaluation' else None, evaluation.full_name ) unfinished_evaluations.sort(key=sorter) template_data = dict( semester_list=semester_list, can_download_grades=request.user.can_download_grades, unfinished_evaluations=unfinished_evaluations, evaluation_end_warning_period=settings.EVALUATION_END_WARNING_PERIOD, ) return render(request, "student_index.html", template_data)
def test_no_original_given(self): new_evaluation = Evaluation() form = ContributionCopyForm(None, instance=None, evaluation=new_evaluation) self.assertEqual(form.evaluation, new_evaluation)