Exemple #1
0
    def prepare(self, context={}):
        from esp.program.controllers.studentclassregmodule import RegistrationTypeController as RTC
        verbs = RTC.getVisibleRegistrationTypeNames(prog=self.program)
        regProf = RegistrationProfile.getLastForProgram(
            get_current_request().user, self.program)
        timeslots = self.program.getTimeSlots(
            types=['Class Time Block', 'Compulsory'])
        classList = ClassSection.prefetch_catalog_data(
            regProf.preregistered_classes(verbs=verbs))

        prevTimeSlot = None
        blockCount = 0

        user = get_current_request().user
        is_onsite = user.isOnsite(self.program)
        scrmi = self.program.studentclassregmoduleinfo

        #   Filter out volunteer timeslots
        timeslots = [
            x for x in timeslots if x.event_type.description != 'Volunteer'
        ]

        schedule = []
        timeslot_dict = {}
        for sec in classList:
            #   Get the verbs all the time in order for the schedule to show
            #   the student's detailed enrollment status.  (Performance hit, I know.)
            #   - Michael P, 6/23/2009
            #   if scrmi.use_priority:
            sec.verbs = sec.getRegVerbs(user, allowed_verbs=verbs)
            sec.verb_names = [v.name for v in sec.verbs]
            sec.is_enrolled = True if "Enrolled" in sec.verb_names else False

            # While iterating through the meeting times for a section,
            # we use this variable to keep track of the first timeslot.
            # In the section_dict appended to timeslot_dict,
            # we save whether or not this is the first timeslot for this
            # section. If it isn't, the student schedule will indicate
            # this, and will not display the option to remove the
            # section. This is to prevent students from removing what
            # they have mistaken to be duplicated classes from their
            # schedules.
            first_meeting_time = True

            for mt in sec.get_meeting_times().order_by('start'):
                section_dict = {
                    'section': sec,
                    'first_meeting_time': first_meeting_time
                }
                first_meeting_time = False
                if mt.id in timeslot_dict:
                    timeslot_dict[mt.id].append(section_dict)
                else:
                    timeslot_dict[mt.id] = [section_dict]

        for i in range(len(timeslots)):
            timeslot = timeslots[i]
            daybreak = False
            if prevTimeSlot != None:
                if not Event.contiguous(prevTimeSlot, timeslot):
                    blockCount += 1
                    daybreak = True

            if timeslot.id in timeslot_dict:
                cls_list = timeslot_dict[timeslot.id]
                doesnt_have_enrollment = not any(sec['section'].is_enrolled
                                                 for sec in cls_list)
                schedule.append((timeslot, cls_list, blockCount + 1,
                                 doesnt_have_enrollment))
            else:
                schedule.append((timeslot, [], blockCount + 1, False))

            prevTimeSlot = timeslot

        context['num_classes'] = len(classList)
        context['timeslots'] = schedule
        context['use_priority'] = scrmi.use_priority
        context['allow_removal'] = self.deadline_met('/Removal')

        return context
Exemple #2
0
    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 prepare(self, context={}):
        from esp.program.controllers.studentclassregmodule import RegistrationTypeController as RTC
        verbs = RTC.getVisibleRegistrationTypeNames(prog=self.program)
        regProf = RegistrationProfile.getLastForProgram(
            get_current_request().user, self.program)
        timeslots = self.program.getTimeSlotList(exclude_compulsory=False)
        classList = ClassSection.prefetch_catalog_data(
            regProf.preregistered_classes(verbs=verbs))

        prevTimeSlot = None
        blockCount = 0

        if not isinstance(get_current_request().user, ESPUser):
            user = ESPUser(get_current_request().user)
        else:
            user = get_current_request().user

        is_onsite = user.isOnsite(self.program)
        scrmi = self.program.getModuleExtension('StudentClassRegModuleInfo')
        # Hack, to hide the Saturday night timeslots from grades 7-8
        if not is_onsite and not user.getGrade() > 8:
            timeslots = [x for x in timeslots if x.start.hour < 19]

        #   Filter out volunteer timeslots
        timeslots = [
            x for x in timeslots if x.event_type.description != 'Volunteer'
        ]

        schedule = []
        timeslot_dict = {}
        for sec in classList:
            #   TODO: Fix this bit (it was broken, and may need additional queries
            #   or a parameter added to ClassRegModuleInfo).
            show_changeslot = False

            #   Get the verbs all the time in order for the schedule to show
            #   the student's detailed enrollment status.  (Performance hit, I know.)
            #   - Michael P, 6/23/2009
            #   if scrmi.use_priority:
            sec.verbs = sec.getRegVerbs(user, allowed_verbs=verbs)

            for mt in sec.get_meeting_times():
                section_dict = {'section': sec, 'changeable': show_changeslot}
                if mt.id in timeslot_dict:
                    timeslot_dict[mt.id].append(section_dict)
                else:
                    timeslot_dict[mt.id] = [section_dict]

        user_priority = user.getRegistrationPriorities(
            self.program, [t.id for t in timeslots])
        for i in range(len(timeslots)):
            timeslot = timeslots[i]
            daybreak = False
            if prevTimeSlot != None:
                if not Event.contiguous(prevTimeSlot, timeslot):
                    blockCount += 1
                    daybreak = True

            if timeslot.id in timeslot_dict:
                cls_list = timeslot_dict[timeslot.id]
                schedule.append(
                    (timeslot, cls_list, blockCount + 1, user_priority[i]))
            else:
                schedule.append(
                    (timeslot, [], blockCount + 1, user_priority[i]))

            prevTimeSlot = timeslot

        context['num_classes'] = len(classList)
        context['timeslots'] = schedule
        context['use_priority'] = scrmi.use_priority
        context['allow_removal'] = self.deadline_met('/Removal')

        return context
Exemple #4
0
    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", len(classes)))
        class_num_list.append(("Total # of Class Sections",
                               len(prog.sections().select_related())))
        class_num_list.append(
            ("Total # of Lunch Classes", len(classes.filter(status=10))))
        class_num_list.append(
            ("Total # of Classes <span style='color: #00C;'>Unreviewed</span>",
             len(classes.filter(status=0))))
        class_num_list.append(
            ("Total # of Classes <span style='color: #0C0;'>Accepted</span>",
             len(classes.filter(status=10))))
        class_num_list.append(
            ("Total # of Classes <span style='color: #C00;'>Rejected</span>",
             len(classes.filter(status=-10))))
        class_num_list.append(
            ("Total # of Classes <span style='color: #990;'>Cancelled</span>",
             len(classes.filter(status=-20))))
        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, 
                    len(teachers[key])))
            else:
                vitals['teachernum'].append((key, len(teachers[key])))

        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], len(students[key])))
            else:
                vitals['studentnum'].append((key, len(students[key])))

        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))

        ## 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.getModuleExtension('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')).order_by(
                    '-num_subjects').values('id', 'num_sections',
                                            'num_subjects',
                                            'category').distinct()
        dictOut["stats"].append({
            "id":
            "categories",
            "data":
            filter(lambda x: x['id'] in program_categories,
                   annotated_categories)
        })

        #   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
            })

        return dictOut
    def prepare(self, context={}):
        from esp.program.controllers.studentclassregmodule import RegistrationTypeController as RTC
        verbs = RTC.getVisibleRegistrationTypeNames(prog=self.program)
        regProf = RegistrationProfile.getLastForProgram(get_current_request().user, self.program)
        timeslots = self.program.getTimeSlots(types=['Class Time Block', 'Compulsory'])
        classList = ClassSection.prefetch_catalog_data(regProf.preregistered_classes(verbs=verbs))

        prevTimeSlot = None
        blockCount = 0

        user = get_current_request().user
        is_onsite = user.isOnsite(self.program)
        scrmi = self.program.studentclassregmoduleinfo

        #   Filter out volunteer timeslots
        timeslots = [x for x in timeslots if x.event_type.description != 'Volunteer']

        schedule = []
        timeslot_dict = {}
        for sec in classList:
            #   Get the verbs all the time in order for the schedule to show
            #   the student's detailed enrollment status.  (Performance hit, I know.)
            #   - Michael P, 6/23/2009
            #   if scrmi.use_priority:
            sec.verbs = sec.getRegVerbs(user, allowed_verbs=verbs)
            sec.verb_names = [v.name for v in sec.verbs]
            sec.is_enrolled = True if "Enrolled" in sec.verb_names else False

            # While iterating through the meeting times for a section,
            # we use this variable to keep track of the first timeslot.
            # In the section_dict appended to timeslot_dict,
            # we save whether or not this is the first timeslot for this
            # section. If it isn't, the student schedule will indicate
            # this, and will not display the option to remove the
            # section. This is to prevent students from removing what
            # they have mistaken to be duplicated classes from their
            # schedules.
            first_meeting_time = True

            for mt in sec.get_meeting_times().order_by('start'):
                section_dict = {'section': sec, 'first_meeting_time': first_meeting_time}
                first_meeting_time = False
                if mt.id in timeslot_dict:
                    timeslot_dict[mt.id].append(section_dict)
                else:
                    timeslot_dict[mt.id] = [section_dict]

        for i in range(len(timeslots)):
            timeslot = timeslots[i]
            daybreak = False
            if prevTimeSlot != None:
                if not Event.contiguous(prevTimeSlot, timeslot):
                    blockCount += 1
                    daybreak = True

            if timeslot.id in timeslot_dict:
                cls_list = timeslot_dict[timeslot.id]
                doesnt_have_enrollment = not any(sec['section'].is_enrolled
                                                 for sec in cls_list)
                schedule.append((timeslot, cls_list, blockCount + 1,
                                 doesnt_have_enrollment))
            else:
                schedule.append((timeslot, [], blockCount + 1, False))

            prevTimeSlot = timeslot

        context['num_classes'] = len(classList)
        context['timeslots'] = schedule
        context['use_priority'] = scrmi.use_priority
        context['allow_removal'] = self.deadline_met('/Removal')

        return context
Exemple #6
0
    def prepare(self, context=None):
        import operator

        if context is None: context = {}
        
        classes = self.program.classes().select_related()
        vitals = {'classtotal': classes}

        vitals['classsections'] = self.program.sections().select_related()
        vitals['classapproved'] = classes.filter(status=10)
        vitals['classunreviewed'] = classes.filter(status=0)
        vitals['classrejected'] = classes.filter(status=-10)
        vitals['classcancelled'] = classes.filter(status=-20)

        proganchor = self.program_anchor_cached()
        
        #   Display pretty labels for teacher and student numbers
        teacher_labels_dict = {}
        for module in self.program.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 = self.program.teachers()
        for key in teachers.keys():
            if key in teacher_labels_dict:
                vitals['teachernum'].append((teacher_labels_dict[key],         ## Unfortunately, 
teachers[key]))
            else:
                vitals['teachernum'].append((key, teachers[key]))
                
        student_labels_dict = {}
        for module in self.program.getModules():
            student_labels_dict.update(module.studentDesc())      
        vitals['studentnum'] = []

        ## Ew, another set of queries in a for loop...
        ## Same justification, though.
        students = self.program.students()
        for key in students.keys():
            if key in student_labels_dict:
                vitals['studentnum'].append((student_labels_dict[key], students[key]))
            else:
                vitals['studentnum'].append((key, students[key]))
                
        timeslots = self.program.getTimeSlots()
        vitals['timeslots'] = []
        
        ## 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 = self.program))

        ## 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])

            class studentcounter:
                self.clslist = []

                def count(self):
                    lst = [0] + [x.num_students() for x in self.clslist if x.category.category != 'Lunch']
                    return reduce(operator.add, lst)

                def max_count(self):
                    lst = [0] + [x.capacity for x in self.clslist if x.category.category != 'Lunch']
                    return reduce(operator.add, lst)

                def __init__(self, newclslist):
                    self.clslist = newclslist

            curTimeslot['studentcount'] = studentcounter(timeslot_dict[timeslot])
            
            vitals['timeslots'].append(curTimeslot)

        context['vitals'] = vitals

        adminvitals_shirt = self.program.getShirtInfo()
        context['shirt_sizes'] = adminvitals_shirt['shirt_sizes']
        context['shirt_types'] = adminvitals_shirt['shirt_types']
        context['shirts'] = adminvitals_shirt['shirts']

        shours = Decimal('0.0')
        chours = Decimal('0.0')
        crhours = Decimal('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 self.program.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 += cls['subject_duration']
                shours += cls['subject_duration'] * (cls['class_size_max'] if cls['class_size_max'] else 0)
                crhours += cls['subject_duration'] * cls['subject_students'] / cls['num_sections']
           
        context['classhours'] = chours
        context['classpersonhours'] = shours
        context['classreghours'] = crhours
        Q_categories = Q(program=self.program)
        crmi = self.program.getModuleExtension('ClassRegModuleInfo')
        if crmi.open_class_registration:
            Q_categories |= Q(pk=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=self.program, cls__status__gte=0).annotate(num_subjects=Count('cls', distinct=True), num_sections=Count('cls__sections')).order_by('-num_subjects').values('id', 'num_sections', 'num_subjects', 'category').distinct()
        context['categories'] = filter(lambda x: x['id'] in program_categories, annotated_categories)

        return context
Exemple #7
0
        def setUp(self):
            """ Create a Program to work with """
            super(self, ProgramModuleTestCase).setUp()

            self._max_prog_modules_id = ProgramModule.objects.order_by(
                '-id')[0].id

            # We need all the Program Modules loaded up to do this properly
            from esp.program.modules.models import install as program_modules_install
            program_modules_install()

            self.module = moduleClass()
            self.programmodule = ProgramModule.objects.get(
                handler=self.module.module_properties_autopopulated()
                ['handler'])

            self.prog_anchor = GetNode("Q/Programs/TestProgram/%s" %
                                       Random().sample(string.letters, 12))
            self.prog_urlname = "%s/%s" % (self.prog_anchor.parent.name,
                                           self.prog_anchor.name)
            self.prog = Program()

            # Create a random anchor, so that we can keep creating new programs
            self.prog.anchor = self.prog_anchor
            self.prog.grade_min = 8
            self.prog.grade_max = 11
            self.prog.director_email = "root@localhost"
            self.prog.program_size_max = 1000

            self.prog.program_modules.add(self.programmodule)

            self.prog.save()

            # Now, we need some class categories to play with
            self.class_categories = [
                ClassCategories.objects.get_or_create(category='Academic',
                                                      symbol='A')[0],
                ClassCategories.objects.get_or_create(category='Comedic',
                                                      symbol='C')[0],
                ClassCategories.objects.get_or_create(
                    category='Extracurricular', symbol='E')[0]
            ]

            # Stick some classes in this program
            self.classes = [ClassSubject(), ClassSubject(), ClassSubject()]
            for klass in self.classes:
                klass.anchor = self.prog_anchor
                klass.parent_program = self.prog
                klass.category = self.class_categories[0]
                klass.grade_min = 7
                klass.grade_max = 12
                klass.class_size_min = 0
                klass.class_size_max = 100
                klass.duration = 2
                klass.save()

                # And do the sketchtastic thing to get the DataTree node ID
                klass.anchor = GetNode(
                    "%s/Classes/%s" %
                    (klass.anchor.get_uri(), klass.emailcode()))
                klass.anchor.friendly_name = "Sample Class"
                klass.anchor.save()
                klass.save()

                # And make a class section too, just for good measure.
                # After all, we're not much of a class if we don't have a section.
                section = ClassSection()
                section.anchor = GetNode("%s/Section1" %
                                         klass.anchor.get_uri())
                section.duration = 2
                section.parent_class = klass
                section.save()

            self.students = [ESPUser(), ESPUser(), ESPUser(), ESPUser()]
            for student in self.students:
                # Set up some students
                pass

            self.teachers = [ESPUser(), ESPUser(), ESPUser()]
            for teacher in self.teachers:
                # Set up some teachers
                pass
    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