def _get_results_impl(evaluation: Evaluation, *, refetch_related_objects: bool = True): if refetch_related_objects: discard_cached_related_objects(evaluation) prefetch_related_objects([evaluation], *GET_RESULTS_PREFETCH_LOOKUPS) tas_per_contribution_question: Dict[Tuple[ int, int], List[TextAnswer]] = unordered_groupby( ((textanswer.contribution_id, textanswer.question_id), textanswer) for contribution in evaluation.contributions.all() for textanswer in contribution.textanswer_set.all() if textanswer.state in [TextAnswer.State.PRIVATE, TextAnswer.State.PUBLISHED]) racs_per_contribution_question: Dict[Tuple[ int, int], List[RatingAnswerCounter]] = unordered_groupby( ((counter.contribution_id, counter.question_id), counter) for contribution in evaluation.contributions.all() for counter in contribution.ratinganswercounter_set.all()) contributor_contribution_results = [] for contribution in evaluation.contributions.all(): questionnaire_results = [] for questionnaire in contribution.questionnaires.all(): results: List[Union[HeadingResult, TextResult, RatingResult]] = [] for question in questionnaire.questions.all(): if question.is_heading_question: results.append(HeadingResult(question=question)) continue text_result = None if question.can_have_textanswers and evaluation.can_publish_text_results: answers = tas_per_contribution_question.get( (contribution.id, question.id), []) text_result = TextResult( question=question, answers=answers, answers_visible_to=textanswers_visible_to( contribution)) if question.is_rating_question: if evaluation.can_publish_rating_results: answer_counters = racs_per_contribution_question.get( (contribution.id, question.id), []) else: answer_counters = None results.append( RatingResult(question, answer_counters, additional_text_result=text_result)) elif question.is_text_question and evaluation.can_publish_text_results: assert text_result is not None results.append(text_result) questionnaire_results.append( QuestionnaireResult(questionnaire, results)) contributor_contribution_results.append( ContributionResult(contribution.contributor, contribution.label, questionnaire_results)) return EvaluationResult(contributor_contribution_results)
def check_enrollment_data_sanity(self): enrollments_per_user = unordered_groupby( (enrollment[1].email, enrollment) for enrollment in self.enrollments) for email, enrollments in enrollments_per_user.items(): if len(enrollments) > settings.IMPORTER_MAX_ENROLLMENTS: self.warnings[ImporterWarning.MANY].append( _("Warning: User {} has {} enrollments, which is a lot."). format(email, len(enrollments)))
def index(request): semesters = Semester.get_all_with_published_unarchived_results() evaluations = Evaluation.objects.filter(course__semester__in=semesters, state=Evaluation.State.PUBLISHED) evaluations = evaluations.select_related("course", "course__semester") evaluations = [ evaluation for evaluation in evaluations if evaluation.can_be_seen_by(request.user) ] if request.user.is_reviewer: additional_evaluations = get_evaluations_with_prefetched_data( Evaluation.objects.filter( course__semester__in=semesters, state__in=[ Evaluation.State.IN_EVALUATION, Evaluation.State.EVALUATED, Evaluation.State.REVIEWED ], )) additional_evaluations = get_evaluations_with_course_result_attributes( additional_evaluations) evaluations += additional_evaluations # put evaluations into a dict that maps from course to a list of evaluations. # this dict is sorted by course.pk (important for the zip below) # (this relies on python 3.7's guarantee that the insertion order of the dict is preserved) evaluations.sort(key=lambda evaluation: evaluation.course.pk) courses_and_evaluations = unordered_groupby( (evaluation.course, evaluation) for evaluation in evaluations) course_pks = [course.pk for course in courses_and_evaluations.keys()] # annotate each course in courses with num_evaluations annotated_courses = (Course.objects.filter(pk__in=course_pks).annotate( num_evaluations=Count("evaluations")).order_by("pk").defer()) for course, annotated_course in zip(courses_and_evaluations.keys(), annotated_courses): course.num_evaluations = annotated_course.num_evaluations degrees = Degree.objects.filter(courses__pk__in=course_pks).distinct() course_types = CourseType.objects.filter( courses__pk__in=course_pks).distinct() template_data = dict( courses_and_evaluations=courses_and_evaluations.items(), degrees=degrees, course_types=course_types, semesters=semesters, ) return render(request, "results_index.html", template_data)
def anonymize_answers(self, lorem_ipsum): # This method is very mathematical and has a lot of "one new variable per line" code, but we think it's okay. # pylint: disable=too-many-locals self.stdout.write("Replacing text answers with fake ones...") for text_answer in TextAnswer.objects.all(): text_answer.answer = self.lorem(text_answer.answer, lorem_ipsum) if text_answer.original_answer: text_answer.original_answer = self.lorem( text_answer.original_answer, lorem_ipsum) text_answer.save() self.stdout.write("Shuffling rating answer counter counts...") contributions = Contribution.objects.all().prefetch_related( "ratinganswercounter_set__question") try: self.stdout.ending = "" progress_bar = ProgressBar(self.stdout, contributions.count()) for contribution_counter, contribution in enumerate(contributions): progress_bar.update(contribution_counter + 1) counters_per_question = unordered_groupby( (counter.question, counter) for counter in contribution.ratinganswercounter_set.all()) for question, counters in counters_per_question.items(): original_sum = sum(counter.count for counter in counters) missing_values = set( CHOICES[question.type].values).difference( set(c.answer for c in counters)) missing_values.discard( NO_ANSWER ) # don't add NO_ANSWER counter if it didn't exist before for value in missing_values: counters.append( RatingAnswerCounter(question=question, contribution=contribution, answer=value, count=0)) generated_counts = [random.random() for c in counters] # nosec generated_sum = sum(generated_counts) generated_counts = [ floor(count / generated_sum * original_sum) for count in generated_counts ] to_add = original_sum - sum(generated_counts) index = random.randint(0, len(generated_counts) - 1) # nosec generated_counts[index] += to_add for counter, generated_count in zip( counters, generated_counts): assert generated_count >= 0 counter.count = generated_count if counter.count: counter.save() elif counter.id: counter.delete() assert original_sum == sum(counter.count for counter in counters) finally: self.stdout.ending = "\n"