def teacherDesc(self): fts = ClassFlagType.get_flag_types(self.program) descs = {} for flag_type in fts: descs[ 'flag_%s' % flag_type. id] = """Teachers who have a class with the "%s" flag.""" % flag_type.name return descs
def teachers(self, QObject = False): fts = ClassFlagType.get_flag_types(self.program) t = {} for flag_type in fts: q = Q(classsubject__flags__flag_type=flag_type.id, classsubject__parent_program=self.program) if QObject: t['flag_%s' % flag_type.id] = q else: t['flag_%s' % flag_type.id] = ESPUser.objects.filter(q).distinct() return t
def teachers(self, QObject=False): fts = ClassFlagType.get_flag_types(self.program) t = {} for flag_type in fts: q = Q(classsubject__flags__flag_type=flag_type.id, classsubject__parent_program=self.program) if QObject: t['flag_%s' % flag_type.id] = q else: t['flag_%s' % flag_type.id] = ESPUser.objects.filter(q).distinct() return t
context['isopenclass']]['type'] context['otherclass'] = context['classes'][1 - context['isopenclass']] context['qsd_name'] = 'classedit_' + context['classtype'] context['manage'] = False if ((request.method == "POST" and request.POST.get('manage') == 'manage') or (request.method == "GET" and request.GET.get('manage') == 'manage') or (tl == 'manage' and 'class' in context)) and request.user.isAdministrator(): context['manage'] = True if self.program.program_modules.filter( handler='ClassFlagModule').exists(): context['show_flags'] = True context['flag_types'] = ClassFlagType.get_flag_types( self.program) return render_to_response(self.baseDir() + 'classedit.html', request, context) @aux_call @needs_teacher def teacherlookup(self, request, tl, one, two, module, extra, prog, newclass=None):
def stats(prog): # Create a dictionary to assemble the output dictOut = {"stats": []} classes = prog.classes().select_related() vitals = {'id': 'vitals'} class_num_list = [] class_num_list.append( ("Total # of Classes", classes.distinct().count())) class_num_list.append( ("Total # of Class Sections", prog.sections().select_related().distinct().count())) class_num_list.append( ("Total # of Lunch Classes", classes.filter(category__category="Lunch").filter( status=10).distinct().count())) class_num_list.append( ("Total # of Classes <span style='color: #00C;'>Unreviewed</span>", classes.filter(status=0).distinct().count())) class_num_list.append( ("Total # of Classes <span style='color: #0C0;'>Accepted</span>", classes.filter(status=10).distinct().count())) class_num_list.append( ("Total # of Classes <span style='color: #C00;'>Rejected</span>", classes.filter(status=-10).distinct().count())) class_num_list.append( ("Total # of Classes <span style='color: #990;'>Cancelled</span>", classes.filter(status=-20).distinct().count())) for ft in ClassFlagType.get_flag_types(prog): class_num_list.append( ('Total # of Classes with the "%s" flag' % ft.name, classes.filter(flags__flag_type=ft).distinct().count())) vitals['classnum'] = class_num_list # Display pretty labels for teacher and student numbers teacher_labels_dict = {} for module in prog.getModules(): teacher_labels_dict.update(module.teacherDesc()) vitals['teachernum'] = [] ## Ew, queries in a for loop... ## Not much to be done about it, though; ## the loop is iterating over a list of independent queries and running each. teachers = prog.teachers() for key in teachers.keys(): if key in teacher_labels_dict: vitals['teachernum'].append(( teacher_labels_dict[key], ## Unfortunately, teachers[key].filter(is_active=True).distinct().count())) else: vitals['teachernum'].append( (key, teachers[key].filter(is_active=True).distinct().count())) student_labels_dict = {} for module in prog.getModules(): student_labels_dict.update(module.studentDesc()) vitals['studentnum'] = [] ## Ew, another set of queries in a for loop... ## Same justification, though. students = prog.students() for key in students.keys(): if key in student_labels_dict: vitals['studentnum'].append( (student_labels_dict[key], students[key].filter(is_active=True).distinct().count())) else: vitals['studentnum'].append( (key, students[key].filter(is_active=True).distinct().count())) timeslots = prog.getTimeSlots() vitals['timeslots'] = [] shours = 0.0 chours = 0.0 crhours = 0.0 ## Write this as a 'for' loop because PostgreSQL can't do it in ## one go without a subquery or duplicated logic, and Django ## doesn't have enough power to expose either approach directly. ## At least there aren't any queries in the for loop... ## (In MySQL, this could I believe be done with a minimally-painful extra() clause.) ## Also, since we're iterating over a big data set, use .values() ## minimize the number of objects that we're creating. ## One dict and two Decimals per row, as opposed to ## an Object per field and all kinds of stuff... for cls in prog.classes().exclude(category__category='Lunch').annotate( num_sections=Count('sections'), subject_duration=Sum('sections__duration'), subject_students=Sum('sections__enrolled_students')).values( 'num_sections', 'subject_duration', 'subject_students', 'class_size_max'): if cls['subject_duration']: chours += float(cls['subject_duration']) shours += float(cls['subject_duration']) * (float( cls['class_size_max']) if cls['class_size_max'] else 0) crhours += float(cls['subject_duration']) * float( cls['subject_students']) / float(cls['num_sections']) vitals["hournum"] = [] vitals["hournum"].append(("Total # of Class-Hours", chours)) vitals["hournum"].append( ("Total # of Class-Student-Hours (capacity)", shours)) vitals["hournum"].append( ("Total # of Class-Student-Hours (registered)", crhours)) ## Prefetch enough data that get_meeting_times() and num_students() don't have to hit the db curclasses = ClassSection.prefetch_catalog_data( ClassSection.objects.filter( parent_class__parent_program=prog).select_related( 'parent_class', 'parent_class__category')) ## Is it really faster to do this logic in Python? ## It'd be even faster to just write a raw SQL query to do it. ## But this is probably good enough. timeslot_dict = defaultdict(list) timeslot_set = set(timeslots) for section in curclasses: for timeslot in set.intersection(timeslot_set, section.get_meeting_times()): timeslot_dict[timeslot].append(section) for timeslot in timeslots: curTimeslot = {'slotname': timeslot.short_description} curTimeslot['classcount'] = len(timeslot_dict[timeslot]) def student_count(clslist): lst = [0] + [ x.num_students() for x in clslist if x.category.category != 'Lunch' ] return reduce(operator.add, lst) def student_max_count(clslist): lst = [0] + [ x.capacity for x in clslist if x.category.category != 'Lunch' ] return reduce(operator.add, lst) curTimeslot['studentcount'] = { 'count': student_count(timeslot_dict[timeslot]), 'max_count': student_max_count(timeslot_dict[timeslot]) } vitals['timeslots'].append(curTimeslot) dictOut["stats"].append(vitals) shirt_data = { "id": "shirtnum" } adminvitals_shirt = prog.getShirtInfo() shirt_data["sizes"] = adminvitals_shirt['shirt_sizes'] shirt_data["types"] = adminvitals_shirt['shirt_types'] shirt_data["data"] = adminvitals_shirt['shirts'] dictOut["stats"].append(shirt_data) Q_categories = Q(program=prog) crmi = prog.classregmoduleinfo if crmi.open_class_registration: Q_categories |= Q(pk=prog.open_class_category.pk) # Introduce a separate query to get valid categories, since the single query seemed to introduce duplicates program_categories = ClassCategories.objects.filter( Q_categories).distinct().values_list('id', flat=True) annotated_categories = ClassCategories.objects.filter( cls__parent_program=prog, cls__status__gte=0).annotate( num_subjects=Count('cls', distinct=True), num_sections=Count('cls__sections'), num_class_hours=Sum('cls__sections__duration')).order_by( '-num_subjects').values('id', 'num_sections', 'num_subjects', 'num_class_hours', 'category').distinct() # Convert Decimal values to float for serialization for i in range(len(annotated_categories)): annotated_categories[i]['num_class_hours'] = float( annotated_categories[i]['num_class_hours']) dictOut["stats"].append({ "id": "categories", "data": filter(lambda x: x['id'] in program_categories, annotated_categories) }) ## Calculate the grade data: grades = [i for i in range(prog.grade_min, prog.grade_max + 1)] # We can't perfectly trust most_recent_profile, but it's good enough for stats students_grades = students['enrolled'].filter( registrationprofile__most_recent_profile=True) students_grades = students_grades.values_list( 'registrationprofile__student_info__graduation_year') students_grades = students_grades.annotate(Count('id', distinct=True)) grades_dict = {result[0]: result[1] for result in students_grades} grades_results = [] for g in grades: year = ESPUser.YOGFromGrade(g, ESPUser.program_schoolyear(prog)) grade_classes = classes.filter(status__gte=0, grade_min__lte=g, grade_max__gte=g) grade_sections = prog.sections().filter( status__gte=0, parent_class__in=grade_classes) grades_results.append({ 'grade': g, 'num_subjects': grade_classes.count(), 'num_sections': grade_sections.count(), 'num_students': grades_dict[year] if year in grades_dict else 0 }) dictOut["stats"].append({"id": "grades", "data": grades_results}) # Add SplashInfo statistics if our program has them splashinfo_data = {} splashinfo_modules = filter(lambda x: isinstance(x, SplashInfoModule), prog.getModules('learn')) if len(splashinfo_modules) > 0: splashinfo_module = splashinfo_modules[0] tag_data = Tag.getProgramTag('splashinfo_choices', prog) if tag_data: splashinfo_choices = json.loads(tag_data) else: splashinfo_choices = { 'lunchsat': SplashInfoForm.default_choices, 'lunchsun': SplashInfoForm.default_choices } for key in splashinfo_choices: counts = {} for item in splashinfo_choices[key]: filter_kwargs = {'program': prog} filter_kwargs[key] = item[0] counts[item[1]] = SplashInfo.objects.filter( **filter_kwargs).distinct().count() splashinfo_data[key] = counts splashinfo_data['siblings'] = { 'yes': SplashInfo.objects.filter( program=prog, siblingdiscount=True).distinct().count(), 'no': SplashInfo.objects.filter(program=prog).exclude( siblingdiscount=True).distinct().count() } dictOut["stats"].append({ "id": "splashinfo", "data": splashinfo_data }) # Add accounting stats pac = ProgramAccountingController(prog) (num_payments, total_payment) = pac.payments_summary() accounting_data = { 'num_payments': num_payments, # We need to convert to a float in order for json to serialize it. # Since we're not doing any computation client-side with these # numbers, this doesn't cause accuracy issues. If the # total_payment is None, just coerce it to zero for display # purposes. 'total_payments': float(total_payment or 0), } dictOut["stats"].append({"id": "accounting", "data": accounting_data}) return dictOut
def manageclass(self, request, tl, one, two, module, extra, prog): cls = self.getClass(request,extra) sections = cls.sections.all().order_by('id') context = {} if cls.isCancelled(): cls_cancel_form = None else: cls_cancel_form = ClassCancellationForm(subject=cls) sec_cancel_forms = [] for sec in sections: if sec.isCancelled(): sec_cancel_forms.append(None) else: sec_cancel_forms.append(SectionCancellationForm(section=sec, prefix='sec'+str(sec.index()))) action = request.GET.get('action', None) if request.method == 'POST': if action == 'cancel_cls': cls_cancel_form.data = request.POST cls_cancel_form.is_bound = True if cls_cancel_form.is_valid(): # Call the Class{Subject,Section}.cancel() method to e-mail and remove students, etc. cls_cancel_form.cleaned_data['target'].cancel(email_students=True, include_lottery_students=cls_cancel_form.cleaned_data['email_lottery_students'], text_students=cls_cancel_form.cleaned_data['text_students'], email_teachers = cls_cancel_form.cleaned_data['email_teachers'], explanation=cls_cancel_form.cleaned_data['explanation'], unschedule=cls_cancel_form.cleaned_data['unschedule']) cls_cancel_form = None else: j = 0 for i in [sec.index() for sec in sections]: if action == ('cancel_sec_%d' % i): sec_cancel_forms[j].data = request.POST sec_cancel_forms[j].is_bound = True if sec_cancel_forms[j].is_valid(): sec_cancel_forms[j].cleaned_data['target'].cancel(email_students=True, include_lottery_students=sec_cancel_forms[j].cleaned_data['email_lottery_students'], text_students=sec_cancel_forms[j].cleaned_data['text_students'], email_teachers = sec_cancel_forms[j].cleaned_data['email_teachers'], explanation=sec_cancel_forms[j].cleaned_data['explanation'], unschedule=sec_cancel_forms[j].cleaned_data['unschedule']) sec_cancel_forms[j] = None j += 1 cls_form = ClassManageForm(self, subject=cls) sec_forms = [SectionManageForm(self, section=sec, prefix='sec'+str(sec.index())) for sec in cls.sections.all().order_by('id')] if request.method == 'POST' and action == 'modify': cls_form.data = request.POST cls_form.is_bound = True valid = cls_form.is_valid() for sf in sec_forms: sf.data = request.POST sf.is_bound = True valid = (valid and sf.is_valid()) if valid: # Leave a loophole: You can set a class to "Unreviewed" (ie., status = 0), # then cancel it, and it won't kick all the students out cls_alter = ClassSubject.objects.get(id=cls_form.cleaned_data['clsid']) new_status = int(cls_form.cleaned_data['status']) should_cancel_sections = (int(cls_alter.status) > 0 and new_status < 0) for sf in sec_forms: sec_alter = ClassSection.objects.get(id=sf.cleaned_data['secid']) orig_sec_status = sec_alter.status sf.save_data(sec_alter) # If the parent class was canceled, cancel the sections if should_cancel_sections and int(sec_alter.status) > 0: sec_alter.status = new_status sec_alter.save() # Kick all the students out of a class if it was rejected if int(sec_alter.status) < 0 and int(orig_sec_status) > 0: for student in sec_alter.students(): sec_alter.unpreregister_student(student) # Save class info after section info so that cls_form.save_data() # can override section information if it's supposed to. # This is needed for accepting/rejecting the sections of a # class when the sections are unreviewed. cls_form.save_data(cls_alter) return HttpResponseRedirect(request.get_full_path()) if self.program.program_modules.filter(handler='ClassFlagModule').exists(): context['show_flags'] = True context['flag_types'] = ClassFlagType.get_flag_types(self.program) context['class'] = cls context['sections'] = sections context['cls_form'] = cls_form context['sec_forms'] = sec_forms context['cls_cancel_form'] = cls_cancel_form context['sec_cancel_forms'] = sec_cancel_forms context['module'] = self return render_to_response(self.baseDir()+'manageclass.html', request, context)
def manageclass(self, request, tl, one, two, module, extra, prog): cls = self.getClass(request,extra) sections = cls.sections.all().order_by('id') context = {} if cls.isCancelled(): cls_cancel_form = None else: cls_cancel_form = ClassCancellationForm(subject=cls) sec_cancel_forms = [] for sec in sections: if sec.isCancelled(): sec_cancel_forms.append(None) else: sec_cancel_forms.append(SectionCancellationForm(section=sec, prefix='sec'+str(sec.index()))) action = request.GET.get('action', None) if request.method == 'POST': if action == 'cancel_cls': cls_cancel_form.data = request.POST cls_cancel_form.is_bound = True if cls_cancel_form.is_valid(): # Call the Class{Subject,Section}.cancel() method to e-mail and remove students, etc. cls_cancel_form.cleaned_data['target'].cancel(email_students=True, include_lottery_students=cls_cancel_form.cleaned_data['email_lottery_students'], explanation=cls_cancel_form.cleaned_data['explanation'], unschedule=cls_cancel_form.cleaned_data['unschedule']) cls_cancel_form = None else: j = 0 for i in [sec.index() for sec in sections]: if action == ('cancel_sec_%d' % i): sec_cancel_forms[j].data = request.POST sec_cancel_forms[j].is_bound = True if sec_cancel_forms[j].is_valid(): sec_cancel_forms[j].cleaned_data['target'].cancel(email_students=True, include_lottery_students=sec_cancel_forms[j].cleaned_data['email_lottery_students'], explanation=sec_cancel_forms[j].cleaned_data['explanation'], unschedule=sec_cancel_forms[j].cleaned_data['unschedule']) sec_cancel_forms[j] = None j += 1 cls_form = ClassManageForm(self, subject=cls) sec_forms = [SectionManageForm(self, section=sec, prefix='sec'+str(sec.index())) for sec in cls.sections.all().order_by('id')] if request.method == 'POST' and action == 'modify': cls_form.data = request.POST cls_form.is_bound = True valid = cls_form.is_valid() for sf in sec_forms: sf.data = request.POST sf.is_bound = True valid = (valid and sf.is_valid()) if valid: # Leave a loophole: You can set a class to "Unreviewed" (ie., status = 0), # then cancel it, and it won't kick all the students out cls_alter = ClassSubject.objects.get(id=cls_form.cleaned_data['clsid']) new_status = int(cls_form.cleaned_data['status']) should_cancel_sections = (int(cls_alter.status) > 0 and new_status < 0) for sf in sec_forms: sec_alter = ClassSection.objects.get(id=sf.cleaned_data['secid']) orig_sec_status = sec_alter.status sf.save_data(sec_alter) # If the parent class was canceled, cancel the sections if should_cancel_sections and int(sec_alter.status) > 0: sec_alter.status = new_status sec_alter.save() # Kick all the students out of a class if it was rejected if int(sec_alter.status) < 0 and int(orig_sec_status) > 0: for student in sec_alter.students(): sec_alter.unpreregister_student(student) # Save class info after section info so that cls_form.save_data() # can override section information if it's supposed to. # This is needed for accepting/rejecting the sections of a # class when the sections are unreviewed. cls_form.save_data(cls_alter) return HttpResponseRedirect(request.get_full_path()) if self.program.program_modules.filter(handler='ClassFlagModule').exists(): context['show_flags'] = True context['flag_types'] = ClassFlagType.get_flag_types(self.program) context['class'] = cls context['sections'] = sections context['cls_form'] = cls_form context['sec_forms'] = sec_forms context['cls_cancel_form'] = cls_cancel_form context['sec_cancel_forms'] = sec_cancel_forms context['module'] = self return render_to_response(self.baseDir()+'manageclass.html', request, context)
def stats(prog): # Create a dictionary to assemble the output dictOut = { "stats": [] } classes = prog.classes().select_related() vitals = {'id': 'vitals'} class_num_list = [] class_num_list.append(("Total # of Classes", classes.distinct().count())) class_num_list.append(("Total # of Class Sections", prog.sections().select_related().distinct().count())) class_num_list.append(("Total # of Lunch Classes", classes.filter(category__category = "Lunch").filter(status=10).distinct().count())) class_num_list.append(("Total # of Classes <span style='color: #00C;'>Unreviewed</span>", classes.filter(status=0).distinct().count())) class_num_list.append(("Total # of Classes <span style='color: #0C0;'>Accepted</span>", classes.filter(status=10).distinct().count())) class_num_list.append(("Total # of Classes <span style='color: #C00;'>Rejected</span>", classes.filter(status=-10).distinct().count())) class_num_list.append(("Total # of Classes <span style='color: #990;'>Cancelled</span>", classes.filter(status=-20).distinct().count())) for ft in ClassFlagType.get_flag_types(prog): class_num_list.append(('Total # of Classes with the "%s" flag' % ft.name, classes.filter(flags__flag_type=ft).distinct().count())) vitals['classnum'] = class_num_list # Display pretty labels for teacher and student numbers teacher_labels_dict = {} for module in prog.getModules(): teacher_labels_dict.update(module.teacherDesc()) vitals['teachernum'] = [] ## Ew, queries in a for loop... ## Not much to be done about it, though; ## the loop is iterating over a list of independent queries and running each. teachers = prog.teachers() for key in teachers.keys(): if key in teacher_labels_dict: vitals['teachernum'].append((teacher_labels_dict[key], ## Unfortunately, teachers[key].filter(is_active = True).distinct().count())) else: vitals['teachernum'].append((key, teachers[key].filter(is_active = True).distinct().count())) student_labels_dict = {} for module in prog.getModules(): student_labels_dict.update(module.studentDesc()) vitals['studentnum'] = [] ## Ew, another set of queries in a for loop... ## Same justification, though. students = prog.students() for key in students.keys(): if key in student_labels_dict: vitals['studentnum'].append((student_labels_dict[key], students[key].filter(is_active = True).distinct().count())) else: vitals['studentnum'].append((key, students[key].filter(is_active = True).distinct().count())) timeslots = prog.getTimeSlots() vitals['timeslots'] = [] shours = 0.0 chours = 0.0 crhours = 0.0 ## Write this as a 'for' loop because PostgreSQL can't do it in ## one go without a subquery or duplicated logic, and Django ## doesn't have enough power to expose either approach directly. ## At least there aren't any queries in the for loop... ## (In MySQL, this could I believe be done with a minimally-painful extra() clause.) ## Also, since we're iterating over a big data set, use .values() ## minimize the number of objects that we're creating. ## One dict and two Decimals per row, as opposed to ## an Object per field and all kinds of stuff... for cls in prog.classes().exclude(category__category='Lunch').annotate(num_sections=Count('sections'), subject_duration=Sum('sections__duration'), subject_students=Sum('sections__enrolled_students')).values('num_sections', 'subject_duration', 'subject_students', 'class_size_max'): if cls['subject_duration']: chours += float(cls['subject_duration']) shours += float(cls['subject_duration']) * (float(cls['class_size_max']) if cls['class_size_max'] else 0) crhours += float(cls['subject_duration']) * float(cls['subject_students']) / float(cls['num_sections']) vitals["hournum"] = [] vitals["hournum"].append(("Total # of Class-Hours", chours)) vitals["hournum"].append(("Total # of Class-Student-Hours (capacity)", shours)) vitals["hournum"].append(("Total # of Class-Student-Hours (registered)", crhours)) ## Prefetch enough data that get_meeting_times() and num_students() don't have to hit the db curclasses = ClassSection.prefetch_catalog_data( ClassSection.objects .filter(parent_class__parent_program=prog) .select_related('parent_class', 'parent_class__category')) ## Is it really faster to do this logic in Python? ## It'd be even faster to just write a raw SQL query to do it. ## But this is probably good enough. timeslot_dict = defaultdict(list) timeslot_set = set(timeslots) for section in curclasses: for timeslot in set.intersection(timeslot_set, section.get_meeting_times()): timeslot_dict[timeslot].append(section) for timeslot in timeslots: curTimeslot = {'slotname': timeslot.short_description} curTimeslot['classcount'] = len(timeslot_dict[timeslot]) def student_count(clslist): lst = [0] + [x.num_students() for x in clslist if x.category.category != 'Lunch'] return reduce(operator.add, lst) def student_max_count(clslist): lst = [0] + [x.capacity for x in clslist if x.category.category != 'Lunch'] return reduce(operator.add, lst) curTimeslot['studentcount'] = { 'count': student_count(timeslot_dict[timeslot]), 'max_count': student_max_count(timeslot_dict[timeslot]) } vitals['timeslots'].append(curTimeslot) dictOut["stats"].append(vitals) shirt_data = {"id": "shirtnum"}; adminvitals_shirt = prog.getShirtInfo() shirt_data["sizes"] = adminvitals_shirt['shirt_sizes']; shirt_data["types"] = adminvitals_shirt['shirt_types']; shirt_data["data"] = adminvitals_shirt['shirts']; dictOut["stats"].append(shirt_data); Q_categories = Q(program=prog) crmi = prog.classregmoduleinfo if crmi.open_class_registration: Q_categories |= Q(pk=prog.open_class_category.pk) # Introduce a separate query to get valid categories, since the single query seemed to introduce duplicates program_categories = ClassCategories.objects.filter(Q_categories).distinct().values_list('id', flat=True) annotated_categories = ClassCategories.objects.filter(cls__parent_program=prog, cls__status__gte=0).annotate(num_subjects=Count('cls', distinct=True), num_sections=Count('cls__sections'), num_class_hours=Sum('cls__sections__duration')).order_by('-num_subjects').values('id', 'num_sections', 'num_subjects', 'num_class_hours', 'category').distinct() # Convert Decimal values to float for serialization for i in range(len(annotated_categories)): annotated_categories[i]['num_class_hours'] = float(annotated_categories[i]['num_class_hours']) dictOut["stats"].append({"id": "categories", "data": filter(lambda x: x['id'] in program_categories, annotated_categories)}) ## Calculate the grade data: grades = [i for i in range(prog.grade_min, prog.grade_max+1)] # We can't perfectly trust most_recent_profile, but it's good enough for stats students_grades = students['enrolled'].filter(registrationprofile__most_recent_profile=True) students_grades = students_grades.values_list('registrationprofile__student_info__graduation_year') students_grades = students_grades.annotate(Count('id', distinct=True)) grades_dict = {result[0]: result[1] for result in students_grades} grades_results = [] for g in grades: year = ESPUser.YOGFromGrade(g, ESPUser.program_schoolyear(prog)) grade_classes = classes.filter(status__gte=0, grade_min__lte=g, grade_max__gte=g) grade_sections = prog.sections().filter(status__gte=0, parent_class__in=grade_classes) grades_results.append({'grade': g, 'num_subjects': grade_classes.count(), 'num_sections': grade_sections.count(), 'num_students': grades_dict[year] if year in grades_dict else 0}) dictOut["stats"].append({"id": "grades", "data": grades_results}) # Add SplashInfo statistics if our program has them splashinfo_data = {} splashinfo_modules = filter(lambda x: isinstance(x, SplashInfoModule), prog.getModules('learn')) if len(splashinfo_modules) > 0: splashinfo_module = splashinfo_modules[0] tag_data = Tag.getProgramTag('splashinfo_choices', prog) if tag_data: splashinfo_choices = json.loads(tag_data) else: splashinfo_choices = {'lunchsat': SplashInfoForm.default_choices, 'lunchsun': SplashInfoForm.default_choices} for key in splashinfo_choices: counts = {} for item in splashinfo_choices[key]: filter_kwargs = {'program': prog} filter_kwargs[key] = item[0] counts[item[1]] = SplashInfo.objects.filter(**filter_kwargs).distinct().count() splashinfo_data[key] = counts splashinfo_data['siblings'] = { 'yes': SplashInfo.objects.filter(program=prog, siblingdiscount=True).distinct().count(), 'no': SplashInfo.objects.filter(program=prog).exclude(siblingdiscount=True).distinct().count() } dictOut["stats"].append({"id": "splashinfo", "data": splashinfo_data}) # Add accounting stats pac = ProgramAccountingController(prog) (num_payments, total_payment) = pac.payments_summary() accounting_data = { 'num_payments': num_payments, # We need to convert to a float in order for json to serialize it. # Since we're not doing any computation client-side with these # numbers, this doesn't cause accuracy issues. If the # total_payment is None, just coerce it to zero for display # purposes. 'total_payments': float(total_payment or 0), } dictOut["stats"].append({"id": "accounting", "data": accounting_data}) return dictOut
def teacherDesc(self): fts = ClassFlagType.get_flag_types(self.program) descs = {} for flag_type in fts: descs['flag_%s' % flag_type.id] = """Teachers who have a class with the "%s" flag.""" % flag_type.name return descs