def get_variable_stats(request, variable_name): """Returns the most recent values of a variable. Parameters: variable_name - The variable for which to lookup the values. """ # Ensure the request uses the GET method. if not request.method == "GET": return HttpResponseNotAllowed(['GET']) variable = get_object_or_404(Variable, name=variable_name) # Retrieve current group context try: student = Student.objects.get( identification=request.session.get("authenticated_user")) except Student.DoesNotExist: return JsonResponse([], safe=False) groups = CourseGroup.get_groups_by_date(date.today(), course__url=request.session.get('authenticated_course'), members=student) if len(groups): group = groups[0] else: return HttpResponseBadRequest("User does not belong to a course group") from datetime import timedelta # Calculate course-relative time context course_datetime_now = group.calculate_course_datetime(timezone.now()) # Collect relevant value history value_history = ValueHistory.objects.filter(variable=variable, group=group, course_datetime__lte=course_datetime_now) if len(value_history) == 0: return JsonResponse([], safe=False) # Calculate variable statistics statistics = variable.calculate_statistics_from_values(value_history) # Placeholder for the viewer's statement, as well as the bin it falls in. student_statistic = None student_bin = 0 # Init reference to store the maximum and minimum value found in the # extracted values. These will determine if all statistic values fit # within the existing value bins for this variable. max_value = float('-Inf') min_value = float('Inf') for statistic in statistics: if statistic['student'] == student.identification: student_statistic = statistic # Update the max and min value found if needed. value = statistic['value'] max_value = value if max_value < value else max_value min_value = value if min_value > value else min_value # Calculate bins bin_size = (max_value-min_value)/float(variable.num_bins) bin_fn = lambda x: round(min_value + x * bin_size, 2) lower_points = map(bin_fn, range(variable.num_bins)) upper_points = map(bin_fn, range(1, variable.num_bins+1)) bin_stats = [] for index in range(variable.num_bins): if index == 0: assignment_fn = (lambda s: s['value'] <= upper_points[index] and s['value'] >= lower_points[index]) else: assignment_fn = (lambda s: s['value'] <= upper_points[index] and s['value'] > lower_points[index]) # Check if we found the viewer's bin if student_statistic is not None and assignment_fn(student_statistic): student_bin = index predictions = {} for output_variable in Variable.objects.exclude(type='IN').exclude( pk=variable.pk).filter(course=group.course).order_by('order'): comparing_groups = CourseGroup.objects.filter( valuehistory__variable=output_variable).distinct() comparing_students = get_students_by_variable_values( variable, lower_points[index], upper_points[index], index, group__in=comparing_groups) predictions[output_variable.name] = get_gauss_params( output_variable, student__in=comparing_students) predictions[output_variable.name]['chart'] = output_variable.output_chart predictions[output_variable.name]['label'] = output_variable.label predictions[output_variable.name]["axis"] = ( output_variable.axis_label or output_variable.label) bin_stats.append({ 'id': index, 'lower': lower_points[index], 'upper': upper_points[index], 'count': len(filter(assignment_fn, statistics)), 'predictions': predictions }); return JsonResponse({ "student_bin": student_bin, "bins": bin_stats, "label": variable.label, "axis": variable.axis_label or variable.label },safe=False)
def update_from_storage(self): """Updates the appropriate stats models based on storage date. Based on new storage.models.Activity instances new observed values for this variable are calculated. These values are added as ValueHistory instances. The calculation of variable values based on storage data is delegated to the `calculate_values_from_activities` function of subclasses. This calculation function returns the extracted values as well as the latest Activity instance that was used in the calculation. The storage data provided is any Activity instance that has been stored after the latest consumed Activity instances for this variable. """ # Retrieve new activity instances for this variable. from storage.models import Activity ignored_objects = IgnoredObject.objects.all().values_list( 'object_id', flat=True) activities = Activity.objects.exclude( activity__in=ignored_objects).filter( course=self.course.url, pk__gt=self.last_consumed_activity_pk) # Return new ValueHistory instances and the last consumed Activity # instance based on the new activities. value_history, last_consumed = self.calculate_values_from_activities( activities) # If no activity was consumed, stop. if last_consumed is None: return annotated_value_history = [] for value_history_item in value_history: # Determine the attached course groups. Technically someone could # be in multiple groups in one course. groups = CourseGroup.get_groups_by_date( value_history_item.datetime.date(), course=self.course) if len(groups) == 0: continue group = groups[0] # Determine the attached student student_id = value_history_item.student student, _created = Student.objects.get_or_create( identification=student_id, defaults={"label": student_id}) group.members.add(student) value_history_item.group = group # Set course timestamp relative to start value_history_item.course_datetime = ( value_history_item.datetime - timezone.make_aware( datetime.combine(group.start_date, datetime.min.time()))) annotated_value_history.append(value_history_item) # Update the database by adding the new ValueHistory instances ValueHistory.objects.bulk_create(annotated_value_history) # Set the last consumed activity self.last_consumed_activity_pk = last_consumed.pk self.save()