def test_archiving_does_not_change_results(self): results = calculate_average_grades_and_deviation(self.course) self.semester.archive() self.refresh_course() cache.clear() self.assertEqual(calculate_average_grades_and_deviation(self.course), results)
def test_average_grades(self): contributor1 = mommy.make(UserProfile) contributor2 = mommy.make(UserProfile) course = mommy.make(Course) questionnaire = mommy.make(Questionnaire) question_grade = mommy.make(Question, questionnaire=questionnaire, type="G") question_likert = mommy.make(Question, questionnaire=questionnaire, type="L") general_contribution = mommy.make(Contribution, contributor=None, course=course, questionnaires=[questionnaire]) contribution1 = mommy.make(Contribution, contributor=contributor1, course=course, questionnaires=[questionnaire]) contribution2 = mommy.make(Contribution, contributor=contributor2, course=course, questionnaires=[questionnaire]) mommy.make(RatingAnswerCounter, question=question_grade, contribution=contribution1, answer=1, count=1) mommy.make(RatingAnswerCounter, question=question_grade, contribution=contribution2, answer=4, count=2) mommy.make(RatingAnswerCounter, question=question_likert, contribution=contribution1, answer=3, count=4) mommy.make(RatingAnswerCounter, question=question_likert, contribution=general_contribution, answer=5, count=3) total_likert = settings.CONTRIBUTION_PERCENTAGE * 3 + (1 - settings.CONTRIBUTION_PERCENTAGE) * 5 total_grade = 2.5 total = settings.GRADE_PERCENTAGE * total_grade + (1 - settings.GRADE_PERCENTAGE) * total_likert average, deviation = calculate_average_grades_and_deviation(course) self.assertAlmostEqual(average, total) self.assertAlmostEqual(deviation, 0)
def semester_detail(request, semester_id): semester = get_object_or_404(Semester, id=semester_id) if request.user.is_reviewer: courses = list(semester.course_set.filter(state__in=["in_evaluation", "evaluated", "reviewed", "published"]).prefetch_related("degrees")) else: courses = list(semester.course_set.filter(state="published").prefetch_related("degrees")) courses = [course for course in courses if course.can_user_see_course(request.user)] # Annotate each course object with its grades. for course in courses: course.avg_grade, course.avg_deviation = calculate_average_grades_and_deviation(course) CourseTuple = namedtuple('CourseTuple', ('courses', 'single_results')) courses_by_degree = OrderedDict() for degree in Degree.objects.all(): courses_by_degree[degree] = CourseTuple([], []) for course in courses: if course.is_single_result: for degree in course.degrees.all(): section = calculate_results(course)[0] result = section.results[0] courses_by_degree[degree].single_results.append((course, result)) else: for degree in course.degrees.all(): courses_by_degree[degree].courses.append(course) template_data = dict(semester=semester, courses_by_degree=courses_by_degree) return render(request, "results_semester_detail.html", template_data)
def index(request): user = request.user contributor_visible_states = ['prepared', 'editor_approved', 'approved', 'in_evaluation', 'evaluated', 'reviewed', 'published'] own_courses = Course.objects.filter(contributions__contributor=user, state__in=contributor_visible_states) represented_users = user.represented_users.all() delegated_courses = Course.objects.exclude(id__in=own_courses).filter(contributions__can_edit=True, contributions__contributor__in=represented_users, state__in=contributor_visible_states) all_courses = list(own_courses) + list(delegated_courses) all_courses.sort(key=lambda course: list(STATES_ORDERED.keys()).index(course.state)) for course in all_courses: if course.state == 'published': course.avg_grade, course.avg_deviation = calculate_average_grades_and_deviation(course) semesters = Semester.objects.all() semester_list = [dict( semester_name=semester.name, id=semester.id, is_active_semester=semester.is_active_semester, courses=[course for course in all_courses if course.semester_id == semester.id] ) for semester in semesters] template_data = dict(semester_list=semester_list, delegated_courses=delegated_courses) return render(request, "contributor_index.html", template_data)
def course_detail(request, semester_id, course_id): semester = get_object_or_404(Semester, id=semester_id) course = get_object_or_404(semester.course_set, id=course_id, semester=semester) if not course.can_user_see_results(request.user): raise PermissionDenied sections = calculate_results(course) if request.user.is_reviewer: public_view = request.GET.get( 'public_view' ) != 'false' # if parameter is not given, show public view. else: public_view = request.GET.get( 'public_view' ) == 'true' # if parameter is not given, show own view. # If grades are not published, there is no public view if not course.can_publish_grades: public_view = False represented_users = list(request.user.represented_users.all()) represented_users.append(request.user) show_grades = request.user.is_reviewer or course.can_publish_grades # filter text answers for section in sections: results = [] for result in section.results: if isinstance(result, TextResult): answers = [ answer for answer in result.answers if user_can_see_text_answer( request.user, represented_users, answer, public_view) ] if answers: results.append( TextResult(question=result.question, answers=answers)) else: results.append(result) section.results[:] = results # filter empty headings for section in sections: filtered_results = [] for index in range(len(section.results)): result = section.results[index] # filter out if there are no more questions or the next question is also a heading question if isinstance(result, HeadingResult): if index == len(section.results) - 1 or isinstance( section.results[index + 1], HeadingResult): continue filtered_results.append(result) section.results[:] = filtered_results # remove empty sections sections = [section for section in sections if section.results] # group by contributor course_sections_top = [] course_sections_bottom = [] contributor_sections = OrderedDict() for section in sections: if section.contributor is None: if section.questionnaire.is_below_contributors: course_sections_bottom.append(section) else: course_sections_top.append(section) else: contributor_sections.setdefault(section.contributor, { 'total_votes': 0, 'sections': [] })['sections'].append(section) for result in section.results: if isinstance(result, TextResult): contributor_sections[ section.contributor]['total_votes'] += 1 elif isinstance(result, RatingResult) or isinstance( result, YesNoResult): # Only count rating results if we show the grades. if show_grades: contributor_sections[section.contributor][ 'total_votes'] += result.total_count # Show a warning if course is still in evaluation (for reviewer preview). evaluation_warning = course.state != 'published' # Results for a course might not be visible because there are not enough answers # but it can still be "published" e.g. to show the comment results to contributors. # Users who can open the results page see a warning message in this case. sufficient_votes_warning = not course.can_publish_grades course.avg_grade, course.avg_deviation = calculate_average_grades_and_deviation( course) template_data = dict(course=course, course_sections_top=course_sections_top, course_sections_bottom=course_sections_bottom, contributor_sections=contributor_sections, evaluation_warning=evaluation_warning, sufficient_votes_warning=sufficient_votes_warning, show_grades=show_grades, reviewer=request.user.is_reviewer, contributor=course.is_user_contributor_or_delegate( request.user), can_download_grades=request.user.can_download_grades, public_view=public_view) return render(request, "results_course_detail.html", template_data)
def export(self, response, course_types_list, include_not_enough_answers=False, include_unpublished=False): self.workbook = xlwt.Workbook() self.init_styles(self.workbook) counter = 1 for course_types in course_types_list: self.sheet = self.workbook.add_sheet("Sheet " + str(counter)) counter += 1 self.row = 0 self.col = 0 courses_with_results = list() course_states = ['published'] if include_unpublished: course_states.extend(['evaluated', 'reviewed']) used_questionnaires = set() for course in self.semester.course_set.filter( state__in=course_states, type__in=course_types).all(): if course.is_single_result: continue if not course.can_publish_grades and not include_not_enough_answers: continue results = OrderedDict() for questionnaire, contributor, __, data, __ in calculate_results( course): if has_no_rating_answers(course, contributor, questionnaire): continue results.setdefault(questionnaire.id, []).extend(data) used_questionnaires.add(questionnaire) courses_with_results.append((course, results)) courses_with_results.sort(key=lambda cr: (cr[0].type, cr[0].name)) used_questionnaires = sorted(used_questionnaires) course_type_names = [ ct.name for ct in CourseType.objects.filter(pk__in=course_types) ] writec( self, _("Evaluation {0}\n\n{1}").format( self.semester.name, ", ".join(course_type_names)), "headline") for course, results in courses_with_results: writec(self, course.name, "course", cols=2) writen(self) for course, results in courses_with_results: writec(self, _("Avg."), "avg") writec(self, _("Std. dev."), "border_top_bottom_right") for questionnaire in used_questionnaires: writen(self, questionnaire.name, "bold") for course, results in courses_with_results: self.write_two_empty_cells_with_borders() filtered_questions = self.filter_text_and_heading_questions( questionnaire.question_set.all()) for question in filtered_questions: if question.is_heading_question: writen(self, question.text, "italic") else: writen(self, question.text) for course, results in courses_with_results: if questionnaire.id not in results or question.is_heading_question: self.write_two_empty_cells_with_borders() continue qn_results = results[questionnaire.id] values = [] deviations = [] total_count = 0 approval_count = 0 for grade_result in qn_results: if grade_result.question.id == question.id: if grade_result.average: values.append(grade_result.average * grade_result.total_count) deviations.append(grade_result.deviation * grade_result.total_count) total_count += grade_result.total_count if grade_result.question.is_yes_no_question: approval_count += grade_result.approval_count enough_answers = course.can_publish_grades if values and enough_answers: avg = sum(values) / total_count dev = sum(deviations) / total_count if question.is_yes_no_question: percent_approval = float( approval_count) / float( total_count) if total_count > 0 else 0 writec(self, "{:.0%}".format(percent_approval), self.grade_to_style(avg)) writec(self, None, "border_right") else: writec(self, avg, self.grade_to_style(avg)) writec(self, dev, self.deviation_to_style(dev)) else: self.write_two_empty_cells_with_borders() writen(self, None) for course, results in courses_with_results: self.write_two_empty_cells_with_borders() writen(self, _("Overall Average Grade"), "bold") for course, results in courses_with_results: avg, dev = calculate_average_grades_and_deviation(course) if avg: writec(self, avg, self.grade_to_style(avg, total=True), cols=2) else: self.write_two_empty_cells_with_borders() writen(self, _("Overall Average Standard Deviation"), "bold") for course, results in courses_with_results: avg, dev = calculate_average_grades_and_deviation(course) if dev is not None: writec(self, dev, self.deviation_to_style(dev, total=True), cols=2) else: self.write_two_empty_cells_with_borders() writen(self, _("Total voters/Total participants"), "bold") for course, results in courses_with_results: percent_participants = float(course.num_voters) / float( course.num_participants ) if course.num_participants > 0 else 0 writec(self, "{}/{} ({:.0%})".format(course.num_voters, course.num_participants, percent_participants), "total_voters", cols=2) self.workbook.save(response)
def course_detail(request, semester_id, course_id): semester = get_object_or_404(Semester, id=semester_id) course = get_object_or_404(semester.course_set, id=course_id, semester=semester) if not course.can_user_see_results(request.user): raise PermissionDenied sections = calculate_results(course) public_view = request.GET.get('public_view') == 'true' # if parameter is not given, show own view. represented_users = list(request.user.represented_users.all()) represented_users.append(request.user) # filter text answers for section in sections: results = [] for result in section.results: if isinstance(result, TextResult): answers = [answer for answer in result.answers if user_can_see_text_answer(request.user, represented_users, answer, public_view)] if answers: results.append(TextResult(question=result.question, answers=answers)) else: results.append(result) section.results[:] = results # remove empty sections sections = [section for section in sections if section.results] # group by contributor course_sections = [] contributor_sections = OrderedDict() for section in sections: if not section.results: continue if section.contributor is None: course_sections.append(section) else: contributor_sections.setdefault(section.contributor, {'total_votes': 0, 'sections': []})['sections'].append(section) # Sum up all Sections for this contributor. # If section is not a RatingResult: # Add 1 as we assume it is a TextResult or something similar that should be displayed. contributor_sections[section.contributor]['total_votes'] +=\ sum([s.total_count if isinstance(s, RatingResult) else 1 for s in section.results]) # Show a warning if course is still in evaluation (for reviewer preview). evaluation_warning = course.state != 'published' # Results for a course might not be visible because there are not enough answers # but it can still be "published" e.g. to show the comment results to contributors. # Users who can open the results page see a warning message in this case. sufficient_votes_warning = not course.can_publish_grades show_grades = request.user.is_reviewer or course.can_publish_grades course.avg_grade, course.avg_deviation = calculate_average_grades_and_deviation(course) template_data = dict( course=course, course_sections=course_sections, contributor_sections=contributor_sections, evaluation_warning=evaluation_warning, sufficient_votes_warning=sufficient_votes_warning, show_grades=show_grades, reviewer=request.user.is_reviewer, contributor=course.is_user_contributor_or_delegate(request.user), can_download_grades=request.user.can_download_grades, public_view=public_view) return render(request, "results_course_detail.html", template_data)
def export(self, response, course_types_list, include_not_enough_answers=False, include_unpublished=False): self.workbook = xlwt.Workbook() self.init_styles(self.workbook) counter = 1 for course_types in course_types_list: self.sheet = self.workbook.add_sheet("Sheet " + str(counter)) counter += 1 self.row = 0 self.col = 0 courses_with_results = list() course_states = ['published'] if include_unpublished: course_states.extend(['evaluated', 'reviewed']) used_questionnaires = set() for course in self.semester.course_set.filter(state__in=course_states, type__in=course_types).all(): if course.is_single_result: continue if not course.can_publish_grades and not include_not_enough_answers: continue results = OrderedDict() for questionnaire, contributor, __, data, __ in calculate_results(course): if has_no_rating_answers(course, contributor, questionnaire): continue results.setdefault(questionnaire.id, []).extend(data) used_questionnaires.add(questionnaire) courses_with_results.append((course, results)) courses_with_results.sort(key=lambda cr: (cr[0].type, cr[0].name)) used_questionnaires = sorted(used_questionnaires) course_type_names = [ct.name for ct in CourseType.objects.filter(pk__in=course_types)] writec(self, _("Evaluation {0}\n\n{1}").format(self.semester.name, ", ".join(course_type_names)), "headline") for course, results in courses_with_results: writec(self, course.name, "course", cols=2) writen(self) for course, results in courses_with_results: writec(self, _("Avg."), "avg") writec(self, _("Std. dev."), "border_top_bottom_right") for questionnaire in used_questionnaires: writen(self, questionnaire.name, "bold") for course, results in courses_with_results: self.write_two_empty_cells_with_borders() for question in questionnaire.question_set.all(): if question.is_text_question: continue writen(self, question.text) for course, results in courses_with_results: if questionnaire.id not in results: self.write_two_empty_cells_with_borders() continue qn_results = results[questionnaire.id] values = [] deviations = [] total_count = 0 approval_count = 0 for grade_result in qn_results: if grade_result.question.id == question.id: if grade_result.average: values.append(grade_result.average * grade_result.total_count) deviations.append(grade_result.deviation * grade_result.total_count) total_count += grade_result.total_count if grade_result.question.is_yes_no_question: approval_count += grade_result.approval_count enough_answers = course.can_publish_grades if values and enough_answers: avg = sum(values) / total_count dev = sum(deviations) / total_count if question.is_yes_no_question: percent_approval = float(approval_count) / float(total_count) if total_count > 0 else 0 writec(self, "{:.0%}".format(percent_approval), self.grade_to_style(avg)) writec(self, None, "border_right") else: writec(self, avg, self.grade_to_style(avg)) writec(self, dev, self.deviation_to_style(dev)) else: self.write_two_empty_cells_with_borders() writen(self, None) for course, results in courses_with_results: self.write_two_empty_cells_with_borders() writen(self, _("Overall Average Grade"), "bold") for course, results in courses_with_results: avg, dev = calculate_average_grades_and_deviation(course) if avg: writec(self, avg, self.grade_to_style(avg, total=True), cols=2) else: self.write_two_empty_cells_with_borders() writen(self, _("Overall Average Standard Deviation"), "bold") for course, results in courses_with_results: avg, dev = calculate_average_grades_and_deviation(course) if dev is not None: writec(self, dev, self.deviation_to_style(dev, total=True), cols=2) else: self.write_two_empty_cells_with_borders() writen(self, _("Total voters/Total participants"), "bold") for course, results in courses_with_results: percent_participants = float(course.num_voters) / float(course.num_participants) if course.num_participants > 0 else 0 writec(self, "{}/{} ({:.0%})".format(course.num_voters, course.num_participants, percent_participants), "total_voters", cols=2) self.workbook.save(response)