def medliab(self, request, tl, one, two, module, extra, prog):
     """
     Landing page redirecting to med-liab form on Formstack.
     """
     t = Tag.getProgramTag("formstack_id", self.program)
     v = Tag.getProgramTag("formstack_viewkey", self.program)
     context = {"formstack_id": t, "formstack_viewkey": v}
     return render_to_response(self.baseDir()+'medliab.html',
                               request, context)
Example #2
0
 def medliab(self, request, tl, one, two, module, extra, prog):
     """
     Landing page redirecting to med-liab form on Formstack.
     """
     t = Tag.getProgramTag("formstack_id", self.program)
     v = Tag.getProgramTag("formstack_viewkey", self.program)
     context = {"formstack_id": t, "formstack_viewkey": v}
     return render_to_response(self.baseDir() + 'medliab.html', request,
                               context)
Example #3
0
def render_class_core_helper(cls, prog=None, scrmi=None, colorstring=None, collapse_full_classes=None):
    if not prog:
        prog = cls.parent_program

    #   Show e-mail codes?  We need to look in the settings.
    if not scrmi:
        scrmi = cls.parent_program.getModuleExtension('StudentClassRegModuleInfo')

    # Okay, chose a program? Good. Now fetch the color from its hiding place and format it...
    if not colorstring:
        colorstring = prog.getColor()
        if colorstring is not None:
            colorstring = ' background-color:#' + colorstring + ';'
    
    # HACK for Harvard HSSP -- show application counts with enrollment
    #if cls.studentappquestion_set.count():
    #    cls._sections = list(cls.get_sections())
    #    for sec in cls._sections:
    #        sec.num_apps = sec.num_students(verbs=['Applied'])

    # Allow tag configuration of whether class descriptions get collapsed
    # when the class is full (default: yes)
    if collapse_full_classes is None:
        collapse_full_classes = ('false' not in Tag.getProgramTag('collapse_full_classes', prog, 'True').lower())

    return {'class': cls,
            'collapse_full': collapse_full_classes,
            'colorstring': colorstring,
            'show_enrollment': scrmi.visible_enrollments,
            'show_emailcodes': scrmi.show_emailcodes,
            'show_meeting_times': scrmi.visible_meeting_times}           
Example #4
0
    def __init__(self, *args, **kwargs):
        #   Extract a program if one was provided
        if 'program' in kwargs:
            program = kwargs['program']
            del kwargs['program']
        else:
            program = None

        #   Run default init function
        super(SplashInfoForm, self).__init__(*args, **kwargs)

        #   Set choices from Tag data (try to get program-specific choices if they exist)
        tag_data = None
        if program:
            tag_data = Tag.getProgramTag('splashinfo_choices', program)
        if not tag_data: tag_data = Tag.getTag('splashinfo_choices')
        if tag_data:
            tag_struct = json.loads(tag_data)
            self.fields['lunchsat'].choices = tag_struct['lunchsat']
            self.fields['lunchsun'].choices = tag_struct['lunchsun']

        if not Tag.getBooleanTag('splashinfo_siblingdiscount', default=True):
            del self.fields['siblingdiscount']
            del self.fields['siblingname']

        if not Tag.getBooleanTag('splashinfo_lunchsat', default=True):
            del self.fields['lunchsat']

        if not Tag.getBooleanTag('splashinfo_lunchsun', default=True):
            del self.fields['lunchsun']
Example #5
0
    def get_forms(self, reg_data, form_class=TeacherClassRegForm):
        reg_form = form_class(self.crmi, reg_data)

        static_resource_requests = Tag.getProgramTag(
            'static_resource_requests',
            self.program,
        )

        try:
            resource_formset = ResourceRequestFormSet(
                reg_data,
                prefix='request',
                static_resource_requests=static_resource_requests,
            )
        except ValidationError:
            resource_formset = None

        try:
            restype_formset = ResourceTypeFormSet(reg_data, prefix='restype')
        except ValidationError:
            restype_formset = None

        if not reg_form.is_valid() or (
                resource_formset and not resource_formset.is_valid()) or (
                    restype_formset and not restype_formset.is_valid()):
            raise ClassCreationValidationError, (
                reg_form, resource_formset, restype_formset,
                "Invalid form data.  Please make sure you are using the official registration form, on esp.mit.edu.  If you are, please let us know how you got this error."
            )

        return reg_form, resource_formset, restype_formset
    def quiz(self, request, tl, one, two, module, extra, prog):

        custom_form_id = Tag.getProgramTag('quiz_form_id', prog, None)
        if custom_form_id:
            cf = Form.objects.get(id=int(custom_form_id))
        else:
            raise ESPError(
                'Cannot find an appropriate form for the quiz.  Please ask your administrator to create a form and set the quiz_form_id Tag.'
            )

        form_wizard = FormHandler(cf, request, request.user).get_wizard()
        form_wizard.curr_request = request

        if request.method == 'POST':
            form = form_wizard.get_form(0, request.POST, request.FILES)
            if form.is_valid():
                form_wizard.done([form])
                self.controller.markCompleted(request.user)
                return self.goToCore(tl)
        else:
            form = form_wizard.get_form(0)

        return render_to_response(self.baseDir() + 'quiz.html', request,
                                  (prog, tl), {
                                      'prog': prog,
                                      'form': form
                                  })
Example #7
0
    def extraform(self, request, tl, one, two, module, extra, prog):

        custom_form_id = Tag.getProgramTag('%s_extraform_id' % tl, prog, None)
        if custom_form_id:
            cf = Form.objects.get(id=int(custom_form_id))
        else:
            raise ESPError(
                False
            ), 'Cannot find an appropriate form for the quiz.  Please ask your administrator to create a form and set the %s_extraform_id Tag.' % tl

        form_wizard = FormHandler(cf, request, request.user).get_wizard()
        form_wizard.curr_request = request

        if request.method == 'POST':
            form = form_wizard.get_form(0, request.POST, request.FILES)
            if form.is_valid():
                #   Delete previous responses from this user
                dmh = DynamicModelHandler(cf)
                form_model = dmh.createDynModel()
                form_model.objects.filter(user=request.user).delete()
                form_wizard.done([form])
                bit, created = UserBit.valid_objects().get_or_create(
                    user=request.user,
                    qsc=self.program.anchor,
                    verb=self.reg_verb)
                return self.goToCore(tl)
        else:
            #   If the user already filled out the form, use their earlier response for the initial values
            if self.isCompleted():
                dmh = DynamicModelHandler(cf)
                form_model = dmh.createDynModel()
                prev_results = form_model.objects.filter(
                    user=request.user).order_by('-id')
                if prev_results.exists():
                    prev_result_data = {}
                    plain_form = form_wizard.get_form(0)
                    #   Load previous results, with a hack for multiple choice questions.
                    for field in plain_form.fields:
                        if isinstance(plain_form.fields[field],
                                      forms.MultipleChoiceField):
                            prev_result_data[field] = getattr(
                                prev_results[0], field).split(';')
                        else:
                            prev_result_data[field] = getattr(
                                prev_results[0], field)
                    form_wizard = FormHandler(
                        cf, request, request.user).get_wizard(
                            initial_data={0: prev_result_data})

            form = form_wizard.get_form(0)

        return render_to_response(self.baseDir() + 'custom_form.html', request,
                                  (prog, tl), {
                                      'prog': prog,
                                      'form': form,
                                      'tl': tl
                                  })
    def phasezero(self, request, tl, one, two, module, extra, prog):
        context = {}
        role = str(prog) + " Winners"
        context['role'] = role
        q_phasezero = Q(phasezerorecord__program=self.program)
        entrants = ESPUser.objects.filter(q_phasezero).distinct()
        context['entrants'] = entrants
        context['nentrants'] = len(entrants)

        grades = range(prog.grade_min, prog.grade_max + 1)
        stats = {}

        #Calculate grade counts
        for grade in grades:
            stats[grade] = {}
            stats[grade]['in_lottery'] = 0
        for entrant in entrants:
            stats[entrant.getGrade(prog)]['in_lottery'] += 1

        #Run lottery if requested
        if request.POST:
            if Tag.getBooleanTag('student_lottery_run', prog, default=False):
                context['error'] = "You've already run the student lottery!"
            else:
                if "confirm" in request.POST:
                    if Tag.getProgramTag('program_size_by_grade', prog):
                        role = request.POST['rolename']
                        context['role'] = role
                        self.lottery(prog, role)
                        context[
                            'success'] = "The student lottery has been run successfully."
                    else:
                        context[
                            'error'] = "You haven't set the grade caps tag yet."
                else:
                    context[
                        'error'] = "You did not confirm that you would like to run the lottery"

        #If lottery has been run, calculate acceptance stats
        if Tag.getBooleanTag('student_lottery_run', prog, default=False):
            for grade in grades:
                stats[grade]['num_accepted'] = stats[grade]['per_accepted'] = 0
            winners = ESPUser.objects.filter(groups__name=role).distinct()
            for winner in winners:
                stats[winner.getGrade(prog)]['num_accepted'] += 1
            for grade in grades:
                if stats[grade]['in_lottery'] == 0:
                    stats[grade]['per_accepted'] = "NA"
                else:
                    stats[grade]['per_accepted'] = round(
                        stats[grade]['num_accepted'],
                        1) / stats[grade]['in_lottery'] * 100
        context['stats'] = stats
        return render_to_response(
            'program/modules/studentregphasezero/status.html', request,
            context)
Example #9
0
    def apply_settings(self):
        #   Rather than using a model in module_ext.*, configure the module
        #   from a Tag (which can be per-program or global), combining the
        #   Tag's specifications with defaults in the code.
        DEFAULTS = {
            'donation_text': 'Donation to Learning Unlimited',
            'donation_options': [10, 20, 50],
        }

        tag_data = json.loads(Tag.getProgramTag('donation_settings', self.program, "{}"))
        self.settings = DEFAULTS.copy()
        self.settings.update(tag_data)
        return self.settings
Example #10
0
def submit_transaction(request):
    #   We might also need to forward post variables to http://shopmitprd.mit.edu/controller/index.php?action=log_transaction

    if request.POST.has_key(
            "decision") and request.POST["decision"] != "REJECT":

        #   Figure out which user and program the payment are for.
        post_identifier = request.POST.get('merchantDefinedData1', '')
        iac = IndividualAccountingController.from_identifier(post_identifier)
        post_amount = Decimal(request.POST.get('orderAmount', '0.0'))

        #   Warn for possible duplicate payments
        prev_payments = iac.get_transfers().filter(
            line_item=iac.default_payments_lineitemtype())
        if prev_payments.count() > 0 and iac.amount_due() <= 0:
            from django.conf import settings
            recipient_list = [contact[1] for contact in settings.ADMINS]
            recipient_list.append(settings.DEFAULT_EMAIL_ADDRESSES['treasury'])
            refs = 'Cybersource request ID: %s' % post_identifier

            subject = 'Possible Duplicate Postback/Payment'
            refs = 'User: %s (%d); Program: %s (%d)' % (iac.user.name(
            ), iac.user.id, iac.program.niceName(), iac.program.id)
            refs += '\n\nPrevious payments\' Transfer IDs: ' + (u', '.join(
                [str(x.id) for x in prev_payments]))

            # Send mail!
            send_mail('[ ESP CC ] ' + subject + ' by ' + iac.user.first_name + ' ' + iac.user.last_name, \
                  """%s Notification\n--------------------------------- \n\n%s\n\nUser: %s %s (%s)\n\nCardholder: %s, %s\n\nRequest: %s\n\n""" % \
                  (subject, refs, request.user.first_name, request.user.last_name, request.user.id, request.POST.get('billTo_lastName', '--'), request.POST.get('billTo_firstName', '--'), request) , \
                  settings.SERVER_EMAIL, recipient_list, True)

        #   Save the payment as a transfer in the database
        iac.submit_payment(post_amount)

        tl = 'learn'
        one, two = iac.program.url.split('/')
        destination = Tag.getProgramTag("cc_redirect",
                                        iac.program,
                                        default="confirmreg")

        if destination.startswith('/') or '//' in destination:
            pass
        else:
            # simple urls like 'confirmreg' are relative to the program
            destination = "/%s/%s/%s/%s" % (tl, one, two, destination)

        return HttpResponseRedirect(destination)

    return render_to_response('accounting_docs/credit_rejected.html', request,
                              {})
Example #11
0
 def render(self, context):
     key = self.key.resolve(context)
     if self.program:
         program = self.program.resolve(context)
     else:
         program = None
     if self.default:
         default = self.default.resolve(context)
     else:
         default = None
     if self.boolean:
         return str(Tag.getBooleanTag(key, program, default)).lower()
     else:
         return str(Tag.getProgramTag(key, program, default))
    def prepare(self, context={}):
        """ prepare returns the context for the main teacherreg page. This will just set the teacherclsmodule as this module,
            since everything else can be gotten from hooks. """

        context['can_edit'] = self.deadline_met('/Classes/Edit')
        context['can_create'] = self.deadline_met('/Classes/Create')
        context['teacherclsmodule'] = self  # ...
        context['clslist'] = self.clslist(get_current_request().user)
        context['friendly_times_with_date'] = (Tag.getProgramTag(
            key='friendly_times_with_date',
            program=self.program,
            default=False) == "True")
        context['allow_class_import'] = 'false' not in Tag.getTag(
            'allow_class_import', default='true').lower()
        return context
Example #13
0
def render_class_teacher_list_row(klass):
    return {
        'cls':
        klass,
        'program':
        klass.parent_program,
        'teacherclsmodule':
        klass.parent_program.getModuleExtension('ClassRegModuleInfo'),
        'friendly_times_with_date':
        (Tag.getProgramTag(key='friendly_times_with_date',
                           program=klass.parent_program,
                           default=False) == "True"),
        'email_host':
        settings.EMAIL_HOST
    }
 def apply_settings(self):
     #   Rather than using a model in module_ext.*, configure the module
     #   from a Tag (which can be per-program or global), combining the
     #   Tag's specifications with defaults in the code.
     DEFAULTS = {
         'offer_donation': True,
         'donation_text': 'Donation to Learning Unlimited',
         'donation_options': [10, 20, 50],
         'invoice_prefix': settings.INSTITUTION_NAME.lower(),
     }
     DEFAULTS.update(settings.STRIPE_CONFIG)
     tag_data = json.loads(Tag.getProgramTag('stripe_settings', self.program, "{}"))
     self.settings = DEFAULTS.copy()
     self.settings.update(tag_data)
     return self.settings
Example #15
0
    def __init__(self, program, **kwargs):
        """ Set constant parameters for a lottery assignment. """


        self.program = program

        if Tag.getProgramTag('program_size_by_grade', self.program):
            # TODO(benkraft): Consider implementing this.  Stanford's use case
            # as of Fall 2015 (for which program_size_by_grade was written)
            # doesn't need it, but we might want to implement it anyway, or
            # remove the program size logic from the lottery entirely.
            print ("WARNING: The lottery doesn't support the "
                   "program_size_by_grade Tag yet.  It will run without a "
                   "program cap, and allow all students who have marked "
                   "preferences to get classes.")
            self.program_size_max = 0
        else:
            self.program_size_max = self.program.program_size_max
        students = self.program.students()
        if 'twophase_star_students' in students:
            # We can't do the join in SQL, because the query generated takes at least half an hour.  So do it in python.
            stars = set(students['twophase_star_students'].values_list('id',flat=True))
            prioritys = set(students['twophase_priority_students'].values_list('id',flat=True))
            self.lotteried_students = list(stars|prioritys)

        elif 'lotteried_students' in students:
            self.lotteried_students = students['lotteried_students']
        else:
            raise Exception('Cannot retrieve lottery preferences for program, please ensure that it has the lottery module.')
        self.sections = self.program.sections().filter(status__gt=0, parent_class__status__gt=0, registration_status=0, meeting_times__isnull=False).order_by('id').select_related('parent_class','parent_class__parent_program').prefetch_related('meeting_times').distinct()
        self.timeslots = self.program.getTimeSlots()
        self.num_timeslots = len(self.timeslots)
        self.num_students = len(self.lotteried_students)
        self.num_sections = len(self.sections)
        self.real_priority_limit = self.program.priorityLimit() # For most purposes, you probably want to use self.effective_priority_limit instead.
        self.grade_range_exceptions = self.program.useGradeRangeExceptions()
        self.effective_priority_limit = self.real_priority_limit + 1 if self.grade_range_exceptions else self.real_priority_limit

        self.options = LotteryAssignmentController.default_options.copy()
        self.options.update(kwargs)

        self.now = datetime.now()
        numpy.random.seed(self.now.microsecond)

        self.initialize()

        if self.options['stats_display']:
            logger.info('Initialized lottery assignment for %d students, %d sections, %d timeslots', self.num_students, self.num_sections, self.num_timeslots)
Example #16
0
 def apply_settings(self):
     #   Rather than using a model in module_ext.*, configure the module
     #   from a Tag (which can be per-program or global), combining the
     #   Tag's specifications with defaults in the code.
     DEFAULTS = {
         'offer_donation': True,
         'donation_text': 'Donation to Learning Unlimited',
         'donation_options': [10, 20, 50],
         'invoice_prefix': settings.INSTITUTION_NAME.lower(),
     }
     DEFAULTS.update(settings.STRIPE_CONFIG)
     tag_data = json.loads(
         Tag.getProgramTag('stripe_settings', self.program, "{}"))
     self.settings = DEFAULTS.copy()
     self.settings.update(tag_data)
     return self.settings
Example #17
0
 def render(self, context):
     key = self.key.resolve(context)
     if self.program:
         program = self.program.resolve(context)
     else:
         program = None
     if self.default:
         default = self.default.resolve(context)
     else:
         default = None
     if self.boolean:
         result = Tag.getBooleanTag(key, program, default)
         if isinstance(result, bool):
             return str(result).lower()
         else:
             return 'false'
     else:
         return str(Tag.getProgramTag(key, program, default))
Example #18
0
 def getVisibleRegistrationTypeNames(cls, prog, for_VRT_form=False):
     if not (prog and isinstance(prog, (Program, int))):
         return set(cls.default_names)
     if isinstance(prog, int):
         try:
             prog = Program.objects.get(pk=prog)
         except Exception:
             return set(cls.default_names)
     display_names = Tag.getProgramTag(key=cls.key, program=prog)
     if display_names:
         display_names = simplejson.loads(display_names) + cls.default_names
     else:
         display_names = cls.default_names
     if "All" in display_names:
         display_names = list(RegistrationType.objects.all().values_list(
             'name', flat=True).distinct().order_by('name'))
         if for_VRT_form:
             display_names.append("All")
     return display_names
Example #19
0
    def special_classroom_types(self):
        """
         Check special classrooms types (music, computer, kitchen).

         Configuration Tag: special_classroom_types, a dictionary mapping
         resource request type desired_value regexes to a list of classrooms (by
         resource ID). Any classroom whose name matches the regex will
         automatically be included.
         """
        DEFAULT_CONFIG = {
            r'^.*(computer|cluster).*$': [],
            r'^.*music.*$': [],
            r'^.*kitchen.*$': []
        }
        config = json.loads(
            Tag.getProgramTag('special_classroom_types',
                              program=self.p,
                              default='{}'))
        config = config if config else DEFAULT_CONFIG

        HEADINGS = ["Class Section", "Unfulfilled Request", "Current Room"]
        mismatches = []

        for type_regex, matching_rooms in DEFAULT_CONFIG.iteritems():
            resource_requests = ResourceRequest.objects.filter(
                res_type__program=self.p, desired_value__iregex=type_regex)

            for rr in resource_requests:
                if all(room.id in matching_rooms
                       or re.match(type_regex, room.name, re.IGNORECASE)
                       for room in rr.target.classrooms()):
                    continue

                mismatches.append({
                    HEADINGS[0]: rr.target,
                    HEADINGS[1]: rr.desired_value,
                    HEADINGS[2]: rr.target.classrooms()[0].name
                })

        return self.formatter.format_table(
            mismatches, {'headings': HEADINGS},
            help_text=self.special_classroom_types.__doc__)
    def extraform(self, request, tl, one, two, module, extra, prog):
        custom_form_id = Tag.getProgramTag('%s_extraform_id' % tl, prog, None)
        if custom_form_id:
            cf = Form.objects.get(id=int(custom_form_id))
        else:
            raise ESPError('Cannot find an appropriate form for the quiz.  Please ask your administrator to create a form and set the %s_extraform_id Tag.' % tl, log=False)

        form_wizard = FormHandler(cf, request, request.user).get_wizard()
        form_wizard.curr_request = request

        if request.method == 'POST':
            form = form_wizard.get_form(0, request.POST, request.FILES)
            if form.is_valid():
                #   Delete previous responses from this user
                dmh = DynamicModelHandler(cf)
                form_model = dmh.createDynModel()
                form_model.objects.filter(user=request.user).delete()
                form_wizard.done([form])
                bit, created = Record.objects.get_or_create(user=request.user, program=self.program, event=self.event)
                return self.goToCore(tl)
        else:
            #   If the user already filled out the form, use their earlier response for the initial values
            if self.isCompleted():
                dmh = DynamicModelHandler(cf)
                form_model = dmh.createDynModel()
                prev_results = form_model.objects.filter(user=request.user).order_by('-id')
                if prev_results.exists():
                    prev_result_data = {}
                    plain_form = form_wizard.get_form(0)
                    #   Load previous results, with a hack for multiple choice questions.
                    for field in plain_form.fields:
                        if isinstance(plain_form.fields[field], forms.MultipleChoiceField):
                            prev_result_data[field] = getattr(prev_results[0], field).split(';')
                        else:
                            prev_result_data[field] = getattr(prev_results[0], field)
                    form_wizard = FormHandler(cf, request, request.user).get_wizard(initial_data={0: prev_result_data})

            form = form_wizard.get_form(0)

        return render_to_response(self.baseDir()+'custom_form.html', request, {'prog':prog, 'form': form, 'qsd_name': tl+':customform_header'})
     def special_classroom_types(self):
         """
         Check special classrooms types (music, computer, kitchen).

         Configuration Tag: special_classroom_types, a dictionary mapping
         resource request type desired_value regexes to a list of classrooms (by
         resource ID). Any classroom whose name matches the regex will
         automatically be included.
         """
         DEFAULT_CONFIG = {r'^.*(computer|cluster).*$': [],
                           r'^.*music.*$': [],
                           r'^.*kitchen.*$': []}
         config = json.loads(Tag.getProgramTag('special_classroom_types',
                                               program=self.p,
                                               default='{}'))
         config = config if config else DEFAULT_CONFIG

         HEADINGS = ["Class Section", "Unfulfilled Request", "Current Room"]
         mismatches = []

         for type_regex, matching_rooms in DEFAULT_CONFIG.iteritems():
             resource_requests = ResourceRequest.objects.filter(
                 res_type__program=self.p, desired_value__iregex=type_regex)

             for rr in resource_requests:
                 if all(room.id in matching_rooms or
                        re.match(type_regex, room.name, re.IGNORECASE)
                        for room in rr.target.classrooms()):
                     continue

                 mismatches.append({
                         HEADINGS[0]: rr.target,
                         HEADINGS[1]: rr.desired_value,
                         HEADINGS[2]: rr.target.classrooms()[0].name
                         })

         return self.formatter.format_table(mismatches,
                                            {'headings': HEADINGS},
                                            help_text=self.special_classroom_types.__doc__)
Example #22
0
 def no_overlap_classes(self):
     '''Gets a list of classes from the tag no_overlap_classes, and checks that they don't overlap.  The tag should contain a dict of {'comment': [list,of,class,ids]}.'''
     classes = json.loads(
         Tag.getProgramTag('no_overlap_classes',
                           program=self.p,
                           default="{}"))
     classes_lookup = {
         x.id: x
         for x in ClassSubject.objects.filter(
             id__in=sum(classes.values(), []))
     }
     bad_classes = []
     for key, l in classes.iteritems():
         eventtuples = list(
             Event.objects.filter(
                 meeting_times__parent_class__in=l).values_list(
                     'description', 'meeting_times',
                     'meeting_times__parent_class'))
         overlaps = {}
         for event, sec, cls in eventtuples:
             if event in overlaps:
                 overlaps[event].append(classes_lookup[cls])
             else:
                 overlaps[event] = [classes_lookup[cls]]
         for event in overlaps:
             if len(overlaps[event]) > 1:
                 bad_classes.append({
                     'Comment': key,
                     'Timeblock': event,
                     'Classes': overlaps[event]
                 })
     return self.formatter.format_table(
         bad_classes, {'headings': ['Comment', 'Timeblock', 'Classes']},
         help_text=
         "Given a list of classes that should not overlap, compute which overlap.  This is to be used for example for classes using the same materials which are not tracked by the website, or to check that directors' classes don't overlap.  The classes should be put in the Tag no_overlap_classes, in the format of a dictionary with keys various comments (e.g. 'classes using the Quiz Bowl buzzers') and values as corresponding lists of class IDs."
     )
 def no_overlap_classes(self):
     '''Gets a list of classes from the tag no_overlap_classes, and checks that they don't overlap.  The tag should contain a dict of {'comment': [list,of,class,ids]}.'''
     classes = json.loads(Tag.getProgramTag('no_overlap_classes',program=self.p, default="{}"))
     classes_lookup = {x.id: x for x in ClassSubject.objects.filter(id__in=sum(classes.values(),[]))}
     bad_classes = []
     for key, l in classes.iteritems():
         eventtuples = list(Event.objects.filter(meeting_times__parent_class__in=l).values_list('description', 'meeting_times', 'meeting_times__parent_class'))
         overlaps = {}
         for event, sec, cls in eventtuples:
             if event in overlaps:
                 overlaps[event].append(classes_lookup[cls])
             else:
                 overlaps[event]=[classes_lookup[cls]]
         for event in overlaps:
             if len(overlaps[event])>1:
                 bad_classes.append({
                     'Comment': key,
                     'Timeblock': event,
                     'Classes': overlaps[event]
                     })
     return self.formatter.format_table(bad_classes,
             {'headings': ['Comment', 'Timeblock', 'Classes']},
             help_text="Given a list of classes that should not overlap, compute which overlap.  This is to be used for example for classes using the same materials which are not tracked by the website, or to check that directors' classes don't overlap.  The classes should be put in the Tag no_overlap_classes, in the format of a dictionary with keys various comments (e.g. 'classes using the Quiz Bowl buzzers') and values as corresponding lists of class IDs."
             )
Example #24
0
    def studentreg2phase(self, request, tl, one, two, module, extra, prog):
        """
        Serves the two-phase student reg page. This page includes instructions
        for registration, and links to the phase1/phase2 sub-pages.
        """

        context = {}
        timeslot_dict = {}
        # Populate the timeslot dictionary with the priority to class title
        # mappings for each timeslot.
        priority_regs = StudentRegistration.valid_objects().filter(
            user=request.user, relationship__name__startswith='Priority')
        priority_regs = priority_regs.select_related('relationship', 'section',
                                                     'section__parent_class')
        for student_reg in priority_regs:
            rel = student_reg.relationship
            title = student_reg.section.parent_class.title
            sec = student_reg.section
            times = sec.meeting_times.all().order_by('start')
            if times.count() == 0:
                continue
            timeslot = times[0].id
            if not timeslot in timeslot_dict:
                timeslot_dict[timeslot] = {rel: title}
            else:
                timeslot_dict[timeslot][rel] = title

        star_counts = {}
        interests = StudentSubjectInterest.valid_objects().filter(
            user=request.user, subject__parent_program=prog)
        interests = interests.select_related('subject').prefetch_related(
            'subject__sections__meeting_times')
        for interest in interests:
            cls = interest.subject
            for sec in cls.sections.all():
                times = sec.meeting_times.all()
                if len(times) == 0:
                    continue
                timeslot = min(times, key=lambda t: t.start).id
                if not timeslot in star_counts:
                    star_counts[timeslot] = 1
                else:
                    star_counts[timeslot] += 1

        # Iterate through timeslots and create a list of tuples of information
        prevTimeSlot = None
        blockCount = 0
        schedule = []
        timeslots = prog.getTimeSlots(types=['Class Time Block', 'Compulsory'])

        context['num_priority'] = prog.priorityLimit()
        context['num_star'] = Tag.getProgramTag("num_stars",
                                                program=prog,
                                                default=10)

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

            if timeslot.id in timeslot_dict:
                priority_dict = timeslot_dict[timeslot.id]
                # (relationship, class_title) -> relationship.name
                priority_list = sorted(priority_dict.items(),
                                       key=lambda item: item[0].name)
            else:
                priority_list = []
            temp_list = []
            for i in range(0, context['num_priority']):
                if i < len(priority_list):
                    temp_list.append(
                        ("Priority " + str(i + 1), priority_list[i][1]))
                else:
                    temp_list.append(("Priority " + str(i + 1), ""))
            priority_list = temp_list
            star_count = 0
            if timeslot.id in star_counts:
                star_count = star_counts[timeslot.id]
            schedule.append(
                (timeslot, priority_list, blockCount + 1, star_count,
                 float(star_count) / context['num_star'] * 100))

            prevTimeSlot = timeslot

        context['timeslots'] = schedule

        return render_to_response(self.baseDir() + 'studentregtwophase.html',
                                  request, context)
    def catalog_render(self,
                       request,
                       tl,
                       one,
                       two,
                       module,
                       extra,
                       prog,
                       timeslot=None):
        """ Return the program class catalog """
        # using .extra() to select all the category text simultaneously
        classes = ClassSubject.objects.catalog(self.program)

        categories = {}
        for cls in classes:
            categories[cls.category_id] = {
                'id':
                cls.category_id,
                'category':
                cls.category_txt
                if hasattr(cls, 'category_txt') else cls.category.category
            }

        # Allow tag configuration of whether class descriptions get collapsed
        # when the class is full (default: yes)
        collapse_full = ('false'
                         not in Tag.getProgramTag('collapse_full_classes',
                                                  prog, 'True').lower())
        hide_full = Tag.getBooleanTag('hide_full_classes', prog, False)
        context = {
            'classes': classes,
            'one': one,
            'two': two,
            'categories': categories.values(),
            'hide_full': hide_full,
            'collapse_full': collapse_full
        }

        scrmi = prog.getModuleExtension('StudentClassRegModuleInfo')
        context['register_from_catalog'] = scrmi.register_from_catalog

        prog_color = prog.getColor()
        collapse_full_classes = ('false' not in Tag.getProgramTag(
            'collapse_full_classes', prog, 'True').lower())
        class_blobs = []

        category_header_str = """<hr size="1"/>
    <a name="cat%d"></a>
      <p style="font-size: 1.2em;" class="category">
         %s
      </p>
      <p class="linktop">
         <a href="#top">[ Return to Category List ]</a>
      </p>
"""

        class_category_id = None
        for cls in classes:
            if cls.category.id != class_category_id:
                class_category_id = cls.category.id
                class_blobs.append(category_header_str %
                                   (class_category_id, cls.category.category))
            class_blobs.append(render_class_direct(cls))
            class_blobs.append('<br />')
        context['class_descs'] = ''.join(class_blobs)

        return render_to_response(self.baseDir() + 'catalog.html',
                                  request, (prog, tl),
                                  context,
                                  use_request_context=False)
Example #26
0
    def __init__(self, module, *args, **kwargs):
        from esp.program.controllers.classreg import get_custom_fields

        def hide_field(field, default=None):
            field.widget = forms.HiddenInput()
            if default is not None:
                field.initial = default

        def hide_choice_if_useless(field):
            """ Hide a choice field if there's only one choice """
            if len(field.choices) == 1:
                hide_field(field, default=field.choices[0][0])

        super(TeacherClassRegForm, self).__init__(*args, **kwargs)

        prog = module.get_program()

        section_numbers = module.allowed_sections_actual
        section_numbers = zip(section_numbers, section_numbers)

        class_sizes = module.getClassSizes()
        class_sizes = zip(class_sizes, class_sizes)

        class_grades = module.getClassGrades()
        class_grades = zip(class_grades, class_grades)

        class_ranges = ClassSizeRange.get_ranges_for_program(prog)
        class_ranges = [(range.id, range.range_str())
                        for range in class_ranges]

        # num_sections: section_list; hide if useless
        self.fields['num_sections'].choices = section_numbers
        hide_choice_if_useless(self.fields['num_sections'])
        # category: program.class_categories.all()
        self.fields['category'].choices = [
            (x.id, x.category) for x in prog.class_categories.all()
        ]
        # grade_min, grade_max: module.getClassGrades
        self.fields['grade_min'].choices = class_grades
        self.fields['grade_max'].choices = class_grades
        if module.use_class_size_max:
            # class_size_max: module.getClassSizes
            self.fields['class_size_max'].choices = class_sizes
        else:
            del self.fields['class_size_max']

        if Tag.getTag('use_class_size_optimal', default=False):
            if not module.use_class_size_optimal:
                del self.fields['class_size_optimal']

            if module.use_optimal_class_size_range:
                self.fields['optimal_class_size_range'].choices = class_ranges
            else:
                del self.fields['optimal_class_size_range']

            if module.use_allowable_class_size_ranges:
                self.fields[
                    'allowable_class_size_ranges'].choices = class_ranges
            else:
                del self.fields['allowable_class_size_ranges']
        else:
            del self.fields['class_size_optimal']
            del self.fields['optimal_class_size_range']
            del self.fields['allowable_class_size_ranges']

        # global_resources: module.getResourceTypes(is_global=True)
        self.fields['global_resources'].choices = module.getResourceTypes(
            is_global=True)
        # resources: module.getResourceTypes(is_global=False)
        resource_choices = module.getResourceTypes(is_global=False)

        # decide whether to display certain fields
        # resources
        if len(resource_choices) > 0:
            self.fields['resources'].choices = resource_choices
        else:
            self.fields['resources'].widget = forms.HiddenInput()

        # prereqs
        if not module.set_prereqs:
            self.fields['prereqs'].widget = forms.HiddenInput()

        # allow_lateness
        if not module.allow_lateness:
            self.fields['allow_lateness'].widget = forms.HiddenInput()
            self.fields['allow_lateness'].initial = 'False'

        self.fields['duration'].choices = sorted(module.getDurations())
        hide_choice_if_useless(self.fields['duration'])

        # session_count
        if module.session_counts:
            session_count_choices = module.session_counts_ints
            session_count_choices = zip(session_count_choices,
                                        session_count_choices)
            self.fields['session_count'].choices = session_count_choices
        hide_choice_if_useless(self.fields['session_count'])

        # requested_room
        if not module.ask_for_room:
            hide_field(self.fields['requested_room'])

        #   Hide resource fields since separate forms are now being used. - Michael P
        resource_fields = [
            'has_own_space', 'global_resources', 'resources',
            'requested_special_resources'
        ]
        for field in resource_fields:
            self.fields[field].widget = forms.HiddenInput()

        #   Add program-custom form components (for inlining additional questions without
        #   introducing a separate program module)
        custom_fields = get_custom_fields()
        for field_name in custom_fields:
            self.fields[field_name] = custom_fields[field_name]

        #   Modify help text on these fields if necessary.
        custom_helptext_fields = [
            'duration', 'class_size_max', 'num_sections', 'requested_room',
            'message_for_directors', 'purchase_requests', 'class_info'
        ] + custom_fields.keys()
        for field in custom_helptext_fields:
            tag_data = Tag.getTag('teacherreg_label_%s' % field)
            if tag_data:
                self.fields[field].label = tag_data
            tag_data = Tag.getTag('teacherreg_help_text_%s' % field)
            if tag_data:
                self.fields[field].help_text = tag_data

        #   Hide fields as desired.
        tag_data = Tag.getProgramTag('teacherreg_hide_fields', prog)
        if tag_data:
            for field_name in tag_data.split(','):
                hide_field(self.fields[field_name])

        tag_data = Tag.getProgramTag('teacherreg_default_min_grade', prog)
        if tag_data:
            print tag_data
            self.fields['grade_min'].initial = tag_data

        tag_data = Tag.getProgramTag('teacherreg_default_max_grade', prog)
        if tag_data:
            print tag_data
            self.fields['grade_max'].initial = tag_data

        tag_data = Tag.getProgramTag('teacherreg_default_class_size_max', prog)
        if tag_data:
            print tag_data
            self.fields['class_size_max'].initial = tag_data

        #   Rewrite difficulty label/choices if desired:
        if Tag.getTag('teacherreg_difficulty_choices'):
            self.fields['hardness_rating'].choices = json.loads(
                Tag.getTag('teacherreg_difficulty_choices'))
        if Tag.getTag('teacherreg_difficulty_label'):
            self.fields['hardness_rating'].label = Tag.getTag(
                'teacherreg_difficulty_label')
    def makeaclass_logic(self,
                         request,
                         tl,
                         one,
                         two,
                         module,
                         extra,
                         prog,
                         newclass=None,
                         action='create',
                         populateonly=False):
        """
        The logic for the teacher class registration form.

        A brief description of some of the key arguments:
        - newclass -- The class object from which to fill in the data
        - action -- What action is the form performing? Options are 'create',
              'createopenclass', 'edit', 'editopenclass'
        - populateonly -- If True and newclass is specified, the form will only
              populate the fields, rather than keeping track of which class they
              came from and saving edits back to that. This is used for the class
              copying logic.
        """

        context = {'module': self}

        static_resource_requests = Tag.getProgramTag(
            'static_resource_requests',
            self.program,
        )

        if request.method == 'POST' and request.POST.has_key('class_reg_page'):
            if not self.deadline_met():
                return self.goToCore(tl)

            ccc = ClassCreationController(self.program)

            try:
                if action == 'create':
                    newclass = ccc.makeaclass(request.user, request.POST)
                elif action == 'createopenclass':
                    newclass = ccc.makeaclass(
                        request.user,
                        request.POST,
                        form_class=TeacherOpenClassRegForm)
                elif action == 'edit':
                    newclass = ccc.editclass(request.user, request.POST, extra)
                elif action == 'editopenclass':
                    newclass = ccc.editclass(
                        request.user,
                        request.POST,
                        extra,
                        form_class=TeacherOpenClassRegForm)

                do_question = bool(
                    ProgramModule.objects.filter(handler="TeacherReviewApps",
                                                 program=self.program))

                if do_question:
                    return HttpResponseRedirect(
                        newclass.parent_program.get_teach_url() +
                        "app_questions")
                if request.POST.has_key(
                        'manage') and request.POST['manage'] == 'manage':
                    if request.POST['manage_submit'] == 'reload':
                        return HttpResponseRedirect(request.get_full_path() +
                                                    '?manage=manage')
                    elif request.POST['manage_submit'] == 'manageclass':
                        return HttpResponseRedirect(
                            '/manage/%s/manageclass/%s' %
                            (self.program.getUrlBase(), extra))
                    elif request.POST['manage_submit'] == 'dashboard':
                        return HttpResponseRedirect('/manage/%s/dashboard' %
                                                    self.program.getUrlBase())
                    elif request.POST['manage_submit'] == 'main':
                        return HttpResponseRedirect('/manage/%s/main' %
                                                    self.program.getUrlBase())
                return self.goToCore(tl)

            except ClassCreationValidationError, e:
                reg_form = e.reg_form
                resource_formset = e.resource_formset
                restype_formset = e.restype_formset
    def __init__(self, crmi, *args, **kwargs):
        from esp.program.controllers.classreg import get_custom_fields

        def hide_field(field, default=None):
            field.widget = forms.HiddenInput()
            if default is not None:
                field.initial = default
        def hide_choice_if_useless(field):
            """ Hide a choice field if there's only one choice """
            if len(field.choices) == 1:
                hide_field(field, default=field.choices[0][0])

        super(TeacherClassRegForm, self).__init__(*args, **kwargs)

        prog = crmi.program

        section_numbers = crmi.allowed_sections_actual
        section_numbers = zip(section_numbers, section_numbers)

        class_sizes = crmi.getClassSizes()
        class_sizes = zip(class_sizes, class_sizes)

        class_grades = crmi.getClassGrades()
        class_grades = zip(class_grades, class_grades)

        class_ranges = ClassSizeRange.get_ranges_for_program(prog)
        class_ranges = [(range.id, range.range_str()) for range in class_ranges]

        # num_sections: section_list; hide if useless
        self.fields['num_sections'].choices = section_numbers
        hide_choice_if_useless( self.fields['num_sections'] )
        # category: program.class_categories.all()
        self.fields['category'].choices = [ (x.id, x.category) for x in prog.class_categories.all() ]
        # grade_min, grade_max: crmi.getClassGrades
        self.fields['grade_min'].choices = class_grades
        self.fields['grade_max'].choices = class_grades
        if crmi.use_class_size_max:
            # class_size_max: crmi.getClassSizes
            self.fields['class_size_max'].choices = class_sizes
        else:
            del self.fields['class_size_max']

        if Tag.getBooleanTag('use_class_size_optimal', default=False):
            if not crmi.use_class_size_optimal:
                del self.fields['class_size_optimal']

            if crmi.use_optimal_class_size_range:
                self.fields['optimal_class_size_range'].choices = class_ranges
            else:
                del self.fields['optimal_class_size_range']

            if crmi.use_allowable_class_size_ranges:
                self.fields['allowable_class_size_ranges'].choices = class_ranges
            else:
                del self.fields['allowable_class_size_ranges']
        else:
            del self.fields['class_size_optimal']
            del self.fields['optimal_class_size_range']
            del self.fields['allowable_class_size_ranges']

        # decide whether to display certain fields

        # prereqs
        if not crmi.set_prereqs:
            self.fields['prereqs'].widget = forms.HiddenInput()

        # allow_lateness
        if not crmi.allow_lateness:
            self.fields['allow_lateness'].widget = forms.HiddenInput()
            self.fields['allow_lateness'].initial = 'False'

        self.fields['duration'].choices = sorted(crmi.getDurations())
        hide_choice_if_useless( self.fields['duration'] )

        # session_count
        if crmi.session_counts:
            session_count_choices = crmi.session_counts_ints
            session_count_choices = zip(session_count_choices, session_count_choices)
            self.fields['session_count'].choices = session_count_choices
        hide_choice_if_useless( self.fields['session_count'] )

        # requested_room
        if not crmi.ask_for_room:
            hide_field( self.fields['requested_room'] )

        #   Hide resource fields since separate forms are now being used. - Michael P
        #   Most have now been removed, but this one gets un-hidden by open classes.
        self.fields['requested_special_resources'].widget = forms.HiddenInput()

        #   Add program-custom form components (for inlining additional questions without
        #   introducing a separate program module)
        custom_fields = get_custom_fields()
        for field_name in custom_fields:
            self.fields[field_name] = custom_fields[field_name]

        #   Modify help text on these fields if necessary.
        custom_helptext_fields = ['duration', 'class_size_max', 'num_sections', 'requested_room', 'message_for_directors', 'purchase_requests', 'class_info'] + custom_fields.keys()
        for field in custom_helptext_fields:
            tag_data = Tag.getProgramTag('teacherreg_label_%s' % field, prog)
            if tag_data:
                self.fields[field].label = tag_data
            tag_data = Tag.getProgramTag('teacherreg_help_text_%s' % field, prog)
            if tag_data:
                self.fields[field].help_text = tag_data

        #   Hide fields as desired.
        tag_data = Tag.getProgramTag('teacherreg_hide_fields', prog)
        if tag_data:
            for field_name in tag_data.split(','):
                hide_field(self.fields[field_name])

        tag_data = Tag.getProgramTag('teacherreg_default_min_grade', prog)
        if tag_data:
            self.fields['grade_min'].initial = tag_data

        tag_data = Tag.getProgramTag('teacherreg_default_max_grade', prog)
        if tag_data:
            self.fields['grade_max'].initial = tag_data

        tag_data = Tag.getProgramTag('teacherreg_default_class_size_max', prog)
        if tag_data:
            self.fields['class_size_max'].initial = tag_data

        #   Rewrite difficulty label/choices if desired:
        if Tag.getTag('teacherreg_difficulty_choices'):
            self.fields['hardness_rating'].choices = json.loads(Tag.getTag('teacherreg_difficulty_choices'))
Example #29
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
Example #30
0
def newprogram(request):
    template_prog = None
    template_prog_id = None
    if 'template_prog' in request.GET and (
            int(request.GET["template_prog"])
    ) != 0:  # if user selects None which value is 0,so we need to check for 0.
        #try:
        template_prog_id = int(request.GET["template_prog"])
        tprogram = Program.objects.get(id=template_prog_id)
        template_prog = {}
        template_prog.update(tprogram.__dict__)
        del template_prog["id"]

        template_prog["program_modules"] = tprogram.program_modules.all(
        ).values_list("id", flat=True)
        template_prog["class_categories"] = tprogram.class_categories.all(
        ).values_list("id", flat=True)
        '''
        As Program Name should be new for each new program created then it is better to not to show old program names in input box .
        template_prog["term"] = tprogram.anchor.name
        template_prog["term_friendly"] = tprogram.anchor.friendly_name
        '''
        template_prog["anchor"] = tprogram.anchor.parent.id

        # aseering 5/18/2008 -- List everyone who was granted V/Administer on the specified program
        template_prog["admins"] = ESPUser.objects.filter(
            userbit__verb=GetNode("V/Administer"),
            userbit__qsc=tprogram.anchor).values_list("id", flat=True)

        # aseering 5/18/2008 -- More aggressively list everyone who was an Admin
        #template_prog["admins"] = [ x.id for x in UserBit.objects.bits_get_users(verb=GetNode("V/Administer"), qsc=tprogram.anchor, user_objs=True) ]

        program_visible_bits = list(
            UserBit.objects.bits_get_users(
                verb=GetNode("V/Flags/Public"), qsc=tprogram.anchor).filter(
                    user__isnull=True).order_by("-startdate"))
        if len(program_visible_bits) > 0:
            newest_bit = program_visible_bits[0]
            oldest_bit = program_visible_bits[-1]

            template_prog["publish_start"] = oldest_bit.startdate
            template_prog["publish_end"] = newest_bit.enddate

        student_reg_bits = list(
            UserBit.objects.bits_get_users(
                verb=GetNode("V/Deadline/Registration/Student"),
                qsc=tprogram.anchor).filter(
                    user__isnull=True).order_by("-startdate"))
        if len(student_reg_bits) > 0:
            newest_bit = student_reg_bits[0]
            oldest_bit = student_reg_bits[-1]

            template_prog["student_reg_start"] = oldest_bit.startdate
            template_prog["student_reg_end"] = newest_bit.enddate

        teacher_reg_bits = list(
            UserBit.objects.bits_get_users(
                verb=GetNode("V/Deadline/Registration/Teacher"),
                qsc=tprogram.anchor).filter(
                    user__isnull=True).order_by("-startdate"))
        if len(teacher_reg_bits) > 0:
            newest_bit = teacher_reg_bits[0]
            oldest_bit = teacher_reg_bits[-1]

            template_prog["teacher_reg_start"] = oldest_bit.startdate
            template_prog["teacher_reg_end"] = newest_bit.enddate

        line_items = LineItemType.objects.filter(
            anchor__name="Required",
            anchor__parent__parent=tprogram.anchor).values(
                "amount", "finaid_amount")

        template_prog["base_cost"] = int(
            -sum([x["amount"] for x in line_items]))
        template_prog["finaid_cost"] = int(
            -sum([x["finaid_amount"] for x in line_items]))

    if 'checked' in request.GET:
        # Our form's anchor is wrong, because the form asks for the parent of the anchor that we really want.
        # Don't bother trying to fix the form; just re-set the anchor when we're done.
        context = pickle.loads(request.session['context_str'])
        pcf = ProgramCreationForm(context['prog_form_raw'])
        if pcf.is_valid():
            # Fix the anchor friendly name right away, otherwise in-memory caches cause (mild) issues later on
            anchor = GetNode(pcf.cleaned_data['anchor'].get_uri() + "/" +
                             pcf.cleaned_data["term"])
            anchor.friendly_name = pcf.cleaned_data['term_friendly']
            anchor.save()

            new_prog = pcf.save(
                commit=False)  # don't save, we need to fix it up:
            new_prog.anchor = anchor
            new_prog.save()
            pcf.save_m2m()

            commit_program(new_prog, context['datatrees'], context['userbits'],
                           context['modules'], context['costs'])

            # Create the default resource types now
            default_restypes = Tag.getProgramTag('default_restypes',
                                                 program=new_prog)
            if default_restypes:
                resource_type_labels = json.loads(default_restypes)
                resource_types = [
                    ResourceType.get_or_create(x, new_prog)
                    for x in resource_type_labels
                ]

            #   Force all ProgramModuleObjs and their extensions to be created now
            new_prog.getModules()

            manage_url = '/manage/' + new_prog.url() + '/resources'

            if settings.USE_MAILMAN and 'mailman_moderator' in settings.DEFAULT_EMAIL_ADDRESSES.keys(
            ):
                # While we're at it, create the program's mailing list
                mailing_list_name = "%s_%s" % (new_prog.anchor.parent.name,
                                               new_prog.anchor.name)
                teachers_list_name = "%s-%s" % (mailing_list_name, "teachers")
                students_list_name = "%s-%s" % (mailing_list_name, "students")

                create_list(
                    students_list_name,
                    settings.DEFAULT_EMAIL_ADDRESSES['mailman_moderator'])
                create_list(
                    teachers_list_name,
                    settings.DEFAULT_EMAIL_ADDRESSES['mailman_moderator'])

                load_list_settings(teachers_list_name,
                                   "lists/program_mailman.config")
                load_list_settings(students_list_name,
                                   "lists/program_mailman.config")

                apply_list_settings(
                    teachers_list_name, {
                        'owner': [
                            settings.
                            DEFAULT_EMAIL_ADDRESSES['mailman_moderator'],
                            new_prog.director_email
                        ]
                    })
                apply_list_settings(
                    students_list_name, {
                        'owner': [
                            settings.
                            DEFAULT_EMAIL_ADDRESSES['mailman_moderator'],
                            new_prog.director_email
                        ]
                    })

                if 'archive' in settings.DEFAULT_EMAIL_ADDRESSES.keys():
                    add_list_member(students_list_name, [
                        new_prog.director_email,
                        settings.DEFAULT_EMAIL_ADDRESSES['archive']
                    ])
                    add_list_member(teachers_list_name, [
                        new_prog.director_email,
                        settings.DEFAULT_EMAIL_ADDRESSES['archive']
                    ])

            return HttpResponseRedirect(manage_url)
        else:
            raise ESPError(False), "Improper form data submitted."

    #   If the form has been submitted, process it.
    if request.method == 'POST':
        form = ProgramCreationForm(request.POST)

        if form.is_valid():
            temp_prog = form.save(commit=False)
            datatrees, userbits, modules = prepare_program(
                temp_prog, form.cleaned_data)
            #   Save the form's raw data instead of the form itself, or its clean data.
            #   Unpacking of the data happens at the next step.

            context_pickled = pickle.dumps({
                'prog_form_raw':
                form.data,
                'datatrees':
                datatrees,
                'userbits':
                userbits,
                'modules':
                modules,
                'costs': (form.cleaned_data['base_cost'],
                          form.cleaned_data['finaid_cost'])
            })
            request.session['context_str'] = context_pickled

            return render_to_response(
                'program/newprogram_review.html', request,
                GetNode('Q/Programs/'), {
                    'prog': temp_prog,
                    'datatrees': datatrees,
                    'userbits': userbits,
                    'modules': modules
                })

    else:
        #   Otherwise, the default view is a blank form.
        if template_prog:
            form = ProgramCreationForm(template_prog)
        else:
            form = ProgramCreationForm()

    return render_to_response(
        'program/newprogram.html', request, GetNode('Q/Programs/'), {
            'form': form,
            'programs': Program.objects.all().order_by('-id'),
            'template_prog_id': template_prog_id
        })
Example #31
0
def newprogram(request):
    template_prog = None
    template_prog_id = None
    if 'template_prog' in request.GET and (int(request.GET["template_prog"])) != 0:       # if user selects None which value is 0,so we need to check for 0.
       #try:
        template_prog_id = int(request.GET["template_prog"])
        tprogram = Program.objects.get(id=template_prog_id)
        template_prog = {}
        template_prog.update(tprogram.__dict__)
        del template_prog["id"]
        template_prog["program_type"] = tprogram.program_type
        template_prog["program_modules"] = tprogram.program_modules.all().values_list("id", flat=True)
        template_prog["class_categories"] = tprogram.class_categories.all().values_list("id", flat=True)
        '''
        As Program Name should be new for each new program created then it is better to not to show old program names in input box .
        template_prog["term"] = tprogram.program_instance()
        template_prog["term_friendly"] = tprogram.niceName()
        '''

        student_reg_bits = list(Permission.objects.filter(permission_type__startswith='Student', program=template_prog_id).order_by('-start_date'))
        if len(student_reg_bits) > 0:
            newest_bit = student_reg_bits[0]
            oldest_bit = student_reg_bits[-1]

            template_prog["student_reg_start"] = oldest_bit.start_date
            template_prog["student_reg_end"] = newest_bit.end_date

        teacher_reg_bits = list(Permission.objects.filter(permission_type__startswith='Teacher', program=template_prog_id).order_by('-start_date'))
        if len(teacher_reg_bits) > 0:
            newest_bit = teacher_reg_bits[0]
            oldest_bit = teacher_reg_bits[-1]

            template_prog["teacher_reg_start"] = oldest_bit.start_date
            template_prog["teacher_reg_end"] = newest_bit.end_date

        pac = ProgramAccountingController(tprogram)
        line_items = pac.get_lineitemtypes(required_only=True).values('amount_dec')

        template_prog["base_cost"] = int(sum(x["amount_dec"] for x in line_items))
        template_prog["sibling_discount"] = tprogram.sibling_discount

    if 'checked' in request.GET:
        # Our form's anchor is wrong, because the form asks for the parent of the anchor that we really want.
        # Don't bother trying to fix the form; just re-set the anchor when we're done.
        context = pickle.loads(request.session['context_str'])
        pcf = ProgramCreationForm(context['prog_form_raw'])
        if pcf.is_valid():

            new_prog = pcf.save(commit = True)

            commit_program(new_prog, context['perms'], context['modules'], context['cost'], context['sibling_discount'])

            # Create the default resource types now
            default_restypes = Tag.getProgramTag('default_restypes', program=new_prog)
            if default_restypes:
                resource_type_labels = json.loads(default_restypes)
                resource_types = [ResourceType.get_or_create(x, new_prog) for x in resource_type_labels]

            #   Force all ProgramModuleObjs and their extensions to be created now
            new_prog.getModules()

            manage_url = '/manage/' + new_prog.url + '/resources'

            if settings.USE_MAILMAN and 'mailman_moderator' in settings.DEFAULT_EMAIL_ADDRESSES.keys():
                # While we're at it, create the program's mailing list
                mailing_list_name = "%s_%s" % (new_prog.program_type, new_prog.program_instance)
                teachers_list_name = "%s-%s" % (mailing_list_name, "teachers")
                students_list_name = "%s-%s" % (mailing_list_name, "students")

                create_list(students_list_name, settings.DEFAULT_EMAIL_ADDRESSES['mailman_moderator'])
                create_list(teachers_list_name, settings.DEFAULT_EMAIL_ADDRESSES['mailman_moderator'])

                load_list_settings(teachers_list_name, "lists/program_mailman.config")
                load_list_settings(students_list_name, "lists/program_mailman.config")

                apply_list_settings(teachers_list_name, {'owner': [settings.DEFAULT_EMAIL_ADDRESSES['mailman_moderator'], new_prog.director_email]})
                apply_list_settings(students_list_name, {'owner': [settings.DEFAULT_EMAIL_ADDRESSES['mailman_moderator'], new_prog.director_email]})

                if 'archive' in settings.DEFAULT_EMAIL_ADDRESSES.keys():
                    add_list_members(students_list_name, [new_prog.director_email, settings.DEFAULT_EMAIL_ADDRESSES['archive']])
                    add_list_members(teachers_list_name, [new_prog.director_email, settings.DEFAULT_EMAIL_ADDRESSES['archive']])


            return HttpResponseRedirect(manage_url)
        else:
            raise ESPError("Improper form data submitted.", log=False)


    #   If the form has been submitted, process it.
    if request.method == 'POST':
        form = ProgramCreationForm(request.POST)

        if form.is_valid():
            temp_prog = form.save(commit=False)
            perms, modules = prepare_program(temp_prog, form.cleaned_data)
            #   Save the form's raw data instead of the form itself, or its clean data.
            #   Unpacking of the data happens at the next step.

            context_pickled = pickle.dumps({'prog_form_raw': form.data, 'perms': perms, 'modules': modules, 'cost': form.cleaned_data['base_cost'], 'sibling_discount': form.cleaned_data['sibling_discount']})
            request.session['context_str'] = context_pickled

            return render_to_response('program/newprogram_review.html', request, {'prog': temp_prog, 'perms':perms, 'modules': modules})

    else:
        #   Otherwise, the default view is a blank form.
        if template_prog:
            form = ProgramCreationForm(template_prog)
        else:
            form = ProgramCreationForm()

    return render_to_response('program/newprogram.html', request, {'form': form, 'programs': Program.objects.all().order_by('-id'),'template_prog_id':template_prog_id})
Example #32
0
    def testProgramTag(self):
        '''Test the logic of getProgramTag in a bunch of different conditions.'''

        # Delete any existing tags that might interfere
        Tag.objects.filter(key="test").delete()
        # Dump any existing Tag cache
        Tag._getTag.delete_all()

        #Caching is hard, so what the hell, let's run every assertion twice.
        self.assertFalse(Tag.getProgramTag("test", program=self.program))
        self.assertFalse(Tag.getProgramTag("test", program=self.program))
        self.assertFalse(Tag.getProgramTag("test", program=None))
        self.assertFalse(Tag.getProgramTag("test", program=None))
        self.assertEqual(
            Tag.getProgramTag("test",
                              program=self.program,
                              default="the default"), "the default")
        self.assertEqual(
            Tag.getProgramTag("test",
                              program=self.program,
                              default="the default"), "the default")
        self.assertEqual(
            Tag.getProgramTag("test", program=None, default="the default"),
            "the default")
        self.assertEqual(
            Tag.getProgramTag("test", program=None, default="the default"),
            "the default")

        # Set the program-specific tag
        Tag.setTag("test", target=self.program, value="program tag value")

        self.assertFalse(Tag.getProgramTag("test", program=None))
        self.assertFalse(Tag.getProgramTag("test", program=None))
        self.assertEqual(
            Tag.getProgramTag("test", program=None, default="the default"),
            "the default")
        self.assertEqual(
            Tag.getProgramTag("test", program=None, default="the default"),
            "the default")
        self.assertEqual(Tag.getProgramTag("test", program=self.program),
                         "program tag value")
        self.assertEqual(Tag.getProgramTag("test", program=self.program),
                         "program tag value")
        self.assertEqual(
            Tag.getProgramTag("test",
                              program=self.program,
                              default="the default"), "program tag value")
        self.assertEqual(
            Tag.getProgramTag("test",
                              program=self.program,
                              default="the default"), "program tag value")

        # Now set the general tag
        Tag.setTag("test", target=None, value="general tag value")

        self.assertEqual(Tag.getProgramTag("test", program=None),
                         "general tag value")
        self.assertEqual(Tag.getProgramTag("test", program=None),
                         "general tag value")
        self.assertEqual(
            Tag.getProgramTag("test", program=None, default="the default"),
            "general tag value")
        self.assertEqual(
            Tag.getProgramTag("test", program=None, default="the default"),
            "general tag value")
        self.assertEqual(Tag.getProgramTag("test", program=self.program),
                         "program tag value")
        self.assertEqual(Tag.getProgramTag("test", program=self.program),
                         "program tag value")
        self.assertEqual(
            Tag.getProgramTag("test",
                              program=self.program,
                              default="the default"), "program tag value")
        self.assertEqual(
            Tag.getProgramTag("test",
                              program=self.program,
                              default="the default"), "program tag value")

        # Now unset the program-specific tag
        Tag.unSetTag("test", target=self.program)

        self.assertEqual(Tag.getProgramTag("test", program=None),
                         "general tag value")
        self.assertEqual(Tag.getProgramTag("test", program=None),
                         "general tag value")
        self.assertEqual(
            Tag.getProgramTag("test", program=None, default="the default"),
            "general tag value")
        self.assertEqual(
            Tag.getProgramTag("test", program=None, default="the default"),
            "general tag value")
        self.assertEqual(Tag.getProgramTag("test", program=self.program),
                         "general tag value")
        self.assertEqual(Tag.getProgramTag("test", program=self.program),
                         "general tag value")
        self.assertEqual(
            Tag.getProgramTag("test",
                              program=self.program,
                              default="the default"), "general tag value")
        self.assertEqual(
            Tag.getProgramTag("test",
                              program=self.program,
                              default="the default"), "general tag value")

        #just to clean up
        Tag.unSetTag("test", target=None)

        self.assertFalse(Tag.getProgramTag("test", program=self.program))
        self.assertFalse(Tag.getProgramTag("test", program=self.program))
        self.assertFalse(Tag.getProgramTag("test", program=None))
        self.assertFalse(Tag.getProgramTag("test", program=None))
        self.assertEqual(
            Tag.getProgramTag("test",
                              program=self.program,
                              default="the default"), "the default")
        self.assertEqual(
            Tag.getProgramTag("test",
                              program=self.program,
                              default="the default"), "the default")
        self.assertEqual(
            Tag.getProgramTag("test", program=None, default="the default"),
            "the default")
        self.assertEqual(
            Tag.getProgramTag("test", program=None, default="the default"),
            "the default")
Example #33
0
def newprogram(request):
    template_prog = None
    template_prog_id = None
    if 'template_prog' in request.GET and (
            int(request.GET["template_prog"])
    ) != 0:  # if user selects None which value is 0,so we need to check for 0.
        #try:
        template_prog_id = int(request.GET["template_prog"])
        tprogram = Program.objects.get(id=template_prog_id)
        template_prog = {}
        template_prog.update(tprogram.__dict__)
        del template_prog["id"]
        template_prog["program_type"] = tprogram.program_type
        template_prog["program_modules"] = tprogram.program_modules.all(
        ).values_list("id", flat=True)
        template_prog["class_categories"] = tprogram.class_categories.all(
        ).values_list("id", flat=True)
        '''
        As Program Name should be new for each new program created then it is better to not to show old program names in input box .
        template_prog["term"] = tprogram.anchor.name
        template_prog["term_friendly"] = tprogram.anchor.friendly_name
        '''

        template_prog["admins"] = ESPUser.objects.filter(
            permission__permission_type="Administer",
            permission__program=tprogram).values_list("id", flat=True)

        # aseering 5/18/2008 -- More aggressively list everyone who was an Admin
        #template_prog["admins"] = [ x.id for x in UserBit.objects.bits_get_users(verb=GetNode("V/Administer"), qsc=tprogram.anchor, user_objs=True) ]

        student_reg_bits = list(
            Permission.objects.filter(
                permission_type__startswith='Student',
                program=template_prog_id).order_by('-start_date'))
        if len(student_reg_bits) > 0:
            newest_bit = student_reg_bits[0]
            oldest_bit = student_reg_bits[-1]

            template_prog["student_reg_start"] = oldest_bit.start_date
            template_prog["student_reg_end"] = newest_bit.end_date

        teacher_reg_bits = list(
            Permission.objects.filter(
                permission_type__startswith='Teacher',
                program=template_prog_id).order_by('-start_date'))
        if len(teacher_reg_bits) > 0:
            newest_bit = teacher_reg_bits[0]
            oldest_bit = teacher_reg_bits[-1]

            template_prog["teacher_reg_start"] = oldest_bit.start_date
            template_prog["teacher_reg_end"] = newest_bit.end_date

        pac = ProgramAccountingController(tprogram)
        line_items = pac.get_lineitemtypes(
            required_only=True).values('amount_dec')

        template_prog["base_cost"] = int(
            sum(x["amount_dec"] for x in line_items))
        template_prog["sibling_discount"] = tprogram.sibling_discount

    if 'checked' in request.GET:
        # Our form's anchor is wrong, because the form asks for the parent of the anchor that we really want.
        # Don't bother trying to fix the form; just re-set the anchor when we're done.
        context = pickle.loads(request.session['context_str'])
        pcf = ProgramCreationForm(context['prog_form_raw'])
        if pcf.is_valid():

            new_prog = pcf.save(commit=True)

            commit_program(new_prog, context['perms'], context['modules'],
                           context['cost'], context['sibling_discount'])

            # Create the default resource types now
            default_restypes = Tag.getProgramTag('default_restypes',
                                                 program=new_prog)
            if default_restypes:
                resource_type_labels = json.loads(default_restypes)
                resource_types = [
                    ResourceType.get_or_create(x, new_prog)
                    for x in resource_type_labels
                ]

            #   Force all ProgramModuleObjs and their extensions to be created now
            new_prog.getModules()

            manage_url = '/manage/' + new_prog.url + '/resources'

            if settings.USE_MAILMAN and 'mailman_moderator' in settings.DEFAULT_EMAIL_ADDRESSES.keys(
            ):
                # While we're at it, create the program's mailing list
                mailing_list_name = "%s_%s" % (new_prog.program_type,
                                               new_prog.program_instance)
                teachers_list_name = "%s-%s" % (mailing_list_name, "teachers")
                students_list_name = "%s-%s" % (mailing_list_name, "students")

                create_list(
                    students_list_name,
                    settings.DEFAULT_EMAIL_ADDRESSES['mailman_moderator'])
                create_list(
                    teachers_list_name,
                    settings.DEFAULT_EMAIL_ADDRESSES['mailman_moderator'])

                load_list_settings(teachers_list_name,
                                   "lists/program_mailman.config")
                load_list_settings(students_list_name,
                                   "lists/program_mailman.config")

                apply_list_settings(
                    teachers_list_name, {
                        'owner': [
                            settings.
                            DEFAULT_EMAIL_ADDRESSES['mailman_moderator'],
                            new_prog.director_email
                        ]
                    })
                apply_list_settings(
                    students_list_name, {
                        'owner': [
                            settings.
                            DEFAULT_EMAIL_ADDRESSES['mailman_moderator'],
                            new_prog.director_email
                        ]
                    })

                if 'archive' in settings.DEFAULT_EMAIL_ADDRESSES.keys():
                    add_list_member(students_list_name, [
                        new_prog.director_email,
                        settings.DEFAULT_EMAIL_ADDRESSES['archive']
                    ])
                    add_list_member(teachers_list_name, [
                        new_prog.director_email,
                        settings.DEFAULT_EMAIL_ADDRESSES['archive']
                    ])

            return HttpResponseRedirect(manage_url)
        else:
            raise ESPError(False), "Improper form data submitted."

    #   If the form has been submitted, process it.
    if request.method == 'POST':
        form = ProgramCreationForm(request.POST)

        if form.is_valid():
            temp_prog = form.save(commit=False)
            perms, modules = prepare_program(temp_prog, form.cleaned_data)
            #   Save the form's raw data instead of the form itself, or its clean data.
            #   Unpacking of the data happens at the next step.

            context_pickled = pickle.dumps({
                'prog_form_raw':
                form.data,
                'perms':
                perms,
                'modules':
                modules,
                'cost':
                form.cleaned_data['base_cost'],
                'sibling_discount':
                form.cleaned_data['sibling_discount']
            })
            request.session['context_str'] = context_pickled

            return render_to_response('program/newprogram_review.html',
                                      request, {
                                          'prog': temp_prog,
                                          'perms': perms,
                                          'modules': modules
                                      })

    else:
        #   Otherwise, the default view is a blank form.
        if template_prog:
            form = ProgramCreationForm(template_prog)
        else:
            form = ProgramCreationForm()

    return render_to_response(
        'program/newprogram.html', request, {
            'form': form,
            'programs': Program.objects.all().order_by('-id'),
            'template_prog_id': template_prog_id
        })
Example #34
0
    def __init__(self, crmi, *args, **kwargs):
        from esp.program.controllers.classreg import get_custom_fields

        def hide_field(field, default=None):
            field.widget = forms.HiddenInput()
            if default is not None:
                field.initial = default
        def hide_choice_if_useless(field):
            """ Hide a choice field if there's only one choice """
            if len(field.choices) == 1:
                hide_field(field, default=field.choices[0][0])

        super(TeacherClassRegForm, self).__init__(*args, **kwargs)

        prog = crmi.program

        section_numbers = crmi.allowed_sections_actual
        section_numbers = zip(section_numbers, section_numbers)

        class_sizes = crmi.getClassSizes()
        class_sizes = zip(class_sizes, class_sizes)

        class_grades = crmi.getClassGrades()
        class_grades = zip(class_grades, class_grades)

        class_ranges = ClassSizeRange.get_ranges_for_program(prog)
        class_ranges = [(range.id, range.range_str()) for range in class_ranges]

        # num_sections: section_list; hide if useless
        self.fields['num_sections'].choices = section_numbers
        hide_choice_if_useless( self.fields['num_sections'] )
        # category: program.class_categories.all()
        self.fields['category'].choices = [ (x.id, x.category) for x in prog.class_categories.all() ]
        # grade_min, grade_max: crmi.getClassGrades
        self.fields['grade_min'].choices = class_grades
        self.fields['grade_max'].choices = class_grades
        if Tag.getTag('grade_ranges'):
            grade_ranges = json.loads(Tag.getTag('grade_ranges'))
            self.fields['grade_range'].choices = [(range,str(range[0]) + " - " + str(range[1])) for range in grade_ranges]
            self.fields['grade_range'].required = True
            hide_field( self.fields['grade_min'] )
            self.fields['grade_min'].required = False
            hide_field( self.fields['grade_max'] )
            self.fields['grade_max'].required = False
        else:
            hide_field( self.fields['grade_range'] )
        if crmi.use_class_size_max:
            # class_size_max: crmi.getClassSizes
            self.fields['class_size_max'].choices = class_sizes
        else:
            del self.fields['class_size_max']

        if Tag.getBooleanTag('use_class_size_optimal', default=False):
            if not crmi.use_class_size_optimal:
                del self.fields['class_size_optimal']

            if crmi.use_optimal_class_size_range:
                self.fields['optimal_class_size_range'].choices = class_ranges
            else:
                del self.fields['optimal_class_size_range']

            if crmi.use_allowable_class_size_ranges:
                self.fields['allowable_class_size_ranges'].choices = class_ranges
            else:
                del self.fields['allowable_class_size_ranges']
        else:
            del self.fields['class_size_optimal']
            del self.fields['optimal_class_size_range']
            del self.fields['allowable_class_size_ranges']

        # decide whether to display certain fields

        # prereqs
        if not crmi.set_prereqs:
            self.fields['prereqs'].widget = forms.HiddenInput()

        # allow_lateness
        if not crmi.allow_lateness:
            self.fields['allow_lateness'].widget = forms.HiddenInput()
            self.fields['allow_lateness'].initial = 'False'

        self.fields['duration'].choices = sorted(crmi.getDurations())
        hide_choice_if_useless( self.fields['duration'] )

        # session_count
        if crmi.session_counts:
            session_count_choices = crmi.session_counts_ints
            session_count_choices = zip(session_count_choices, session_count_choices)
            self.fields['session_count'].choices = session_count_choices
        hide_choice_if_useless( self.fields['session_count'] )

        # requested_room
        if not crmi.ask_for_room:
            hide_field( self.fields['requested_room'] )

        #   Hide resource fields since separate forms are now being used. - Michael P
        #   Most have now been removed, but this one gets un-hidden by open classes.
        self.fields['requested_special_resources'].widget = forms.HiddenInput()

        #   Add program-custom form components (for inlining additional questions without
        #   introducing a separate program module)
        custom_fields = get_custom_fields()
        for field_name in custom_fields:
            self.fields[field_name] = custom_fields[field_name]

        #   Modify help text on these fields if necessary.
        #   TODO(benkraft): Is there a reason not to allow this on all fields?
        custom_helptext_fields = [
            'duration', 'class_size_max', 'num_sections', 'requested_room',
            'message_for_directors', 'purchase_requests', 'class_info',
            'grade_max', 'grade_min'] + custom_fields.keys()
        for field in custom_helptext_fields:
            tag_data = Tag.getProgramTag('teacherreg_label_%s' % field, prog)
            if tag_data:
                self.fields[field].label = tag_data
            tag_data = Tag.getProgramTag('teacherreg_help_text_%s' % field, prog)
            if tag_data:
                self.fields[field].help_text = tag_data

        #   Hide fields as desired.
        tag_data = Tag.getProgramTag('teacherreg_hide_fields', prog)
        if tag_data:
            for field_name in tag_data.split(','):
                hide_field(self.fields[field_name])

        tag_data = Tag.getProgramTag('teacherreg_default_min_grade', prog)
        if tag_data:
            self.fields['grade_min'].initial = tag_data

        tag_data = Tag.getProgramTag('teacherreg_default_max_grade', prog)
        if tag_data:
            self.fields['grade_max'].initial = tag_data

        tag_data = Tag.getProgramTag('teacherreg_default_class_size_max', prog)
        if tag_data:
            self.fields['class_size_max'].initial = tag_data

        #   Rewrite difficulty label/choices if desired:
        if Tag.getTag('teacherreg_difficulty_choices'):
            self.fields['hardness_rating'].choices = json.loads(Tag.getTag('teacherreg_difficulty_choices'))

        # Get class_style_choices from tag, otherwise hide the field
        if Tag.getTag('class_style_choices'):
            self.fields['class_style'].choices = json.loads(Tag.getTag('class_style_choices'))
            self.fields['class_style'].required = True
        else:
            hide_field(self.fields['class_style'])
Example #35
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
                for field_name in get_custom_fields():
                    if field_name in newclass.custom_form_data:
                        current_data[field_name] = newclass.custom_form_data[
                            field_name]
                if newclass.optimal_class_size_range:
                    current_data[
                        'optimal_class_size_range'] = newclass.optimal_class_size_range.id
                if newclass.allowable_class_size_ranges.all():
                    current_data['allowable_class_size_ranges'] = list(
                        newclass.allowable_class_size_ranges.all().values_list(
                            'id', flat=True))

                # Makes importing a class from a previous program work
                # These are the only three fields that can currently be hidden
                # If another one is added later, this will need to be changed
                hidden_fields = Tag.getProgramTag('teacherreg_hide_fields',
                                                  prog)
                if hidden_fields:
                    if 'grade_min' in hidden_fields:
                        current_data['grade_min'] = Tag.getProgramTag(
                            'teacherreg_default_min_grade', prog)
                    if 'grade_max' in hidden_fields:
                        current_data['grade_max'] = Tag.getProgramTag(
                            'teacherreg_default_max_grade', prog)
                    if 'class_size_max' in hidden_fields:
                        current_data['class_size_max'] = Tag.getProgramTag(
                            'teacherreg_default_class_size_max', prog)

                if not populateonly:
                    context['class'] = newclass

                if action == 'edit':
    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
                    elif request.POST['manage_submit'] == 'main':
                        return HttpResponseRedirect('/manage/%s/main' %
                                                    self.program.getUrlBase())
                return self.goToCore(tl)

            except ClassCreationValidationError, e:
                reg_form = e.reg_form
                resource_formset = e.resource_formset
                restype_formset = e.restype_formset

        else:
            errors = {}

            resource_types = set([])
            default_restypes = Tag.getProgramTag(
                'default_restypes',
                program=self.program,
            )
            if default_restypes:
                resource_type_labels = json.loads(default_restypes)
                resource_types = resource_types.union(
                    set([
                        ResourceType.get_or_create(x, self.program)
                        for x in resource_type_labels
                    ]))

            if static_resource_requests:
                # With static resource requests, we need to display a form
                # each available type --- there's no way to add the types
                # that we didn't start out with
                # Thus, if default_restype isn't set, we display everything
                # potentially relevant
    def ajax_requests(self, request, tl, one, two, module, extra, prog):
        """ Handle a request for modifying a ResourceRequestFormSet. 
            This view is customized to handle the dynamic form
            (i.e. resource type is specified in advance and loaded
            into a hidden field). 
        """

        static_resource_requests = Tag.getProgramTag(
            'static_resource_requests',
            prog,
        )

        #   Construct a formset from post data
        resource_formset = ResourceRequestFormSet(
            request.POST,
            prefix='request',
            static_resource_requests=static_resource_requests,
        )
        validity = resource_formset.is_valid()
        form_dicts = [x.__dict__ for x in resource_formset.forms]

        #   Retrieve data from forms
        form_datas = [
            x.cleaned_data for x in resource_formset.forms
            if hasattr(x, 'cleaned_data')
        ]
        form_datas_filtered = [
            x for x in form_datas if x.has_key('resource_type')
        ]

        if request.POST.has_key('action'):
            #   Modify form if an action is specified
            if request.POST['action'] == 'add':
                #   Construct initial data including new form
                new_restype = ResourceType.objects.get(
                    id=request.POST['restype'])
                if len(new_restype.choices) > 0:
                    new_data = form_datas_filtered + [{
                        'desired_value':
                        new_restype.choices[0]
                    }]
                else:
                    new_data = form_datas_filtered + [{}]
                new_types = [x['resource_type']
                             for x in form_datas_filtered] + [new_restype]
            elif request.POST['action'] == 'remove':
                #   Construct initial data removing undesired form
                form_to_remove = int(request.POST['form'])
                new_data = form_datas_filtered[:
                                               form_to_remove] + form_datas_filtered[
                                                   (form_to_remove + 1):]
                new_types = [x['resource_type'] for x in new_data]

            #   Instantiate a new formset having the additional form
            new_formset = ResourceRequestFormSet(
                initial=new_data,
                resource_type=new_types,
                prefix='request',
                static_resource_requests=static_resource_requests,
            )
        else:
            #   Otherwise, just send back the original form
            new_formset = resource_formset

        #   Render an HTML fragment with the new formset
        context = {}
        context['formset'] = new_formset
        context['ajax_request'] = True
        formset_str = render_to_string(
            self.baseDir() + 'requests_form_fragment.html', context)
        formset_script = render_to_string(
            self.baseDir() + 'requests_form_fragment.js', context)
        return HttpResponse(
            json.dumps({
                'request_forms_html': formset_str,
                'script': formset_script
            }))
Example #40
0
    def testProgramTag(self):
        '''Test the logic of getProgramTag in a bunch of different conditions.'''

        # Delete any existing tags that might interfere
        Tag.objects.filter(key="test").delete()
        # Dump any existing Tag cache
        Tag._getTag.delete_all()

        #Caching is hard, so what the hell, let's run every assertion twice.
        self.assertFalse(Tag.getProgramTag("test",program=self.program))
        self.assertFalse(Tag.getProgramTag("test",program=self.program))
        self.assertFalse(Tag.getProgramTag("test",program=None))
        self.assertFalse(Tag.getProgramTag("test",program=None))
        self.assertEqual(Tag.getProgramTag("test",program=self.program,default="the default"),"the default")
        self.assertEqual(Tag.getProgramTag("test",program=self.program,default="the default"),"the default")
        self.assertEqual(Tag.getProgramTag("test",program=None,default="the default"),"the default")
        self.assertEqual(Tag.getProgramTag("test",program=None,default="the default"),"the default")


        # Set the program-specific tag
        Tag.setTag("test",target=self.program,value="program tag value")

        self.assertFalse(Tag.getProgramTag("test",program=None))
        self.assertFalse(Tag.getProgramTag("test",program=None))
        self.assertEqual(Tag.getProgramTag("test",program=None,default="the default"),"the default")
        self.assertEqual(Tag.getProgramTag("test",program=None,default="the default"),"the default")
        self.assertEqual(Tag.getProgramTag("test",program=self.program),"program tag value")
        self.assertEqual(Tag.getProgramTag("test",program=self.program),"program tag value")
        self.assertEqual(Tag.getProgramTag("test",program=self.program,default="the default"),"program tag value")
        self.assertEqual(Tag.getProgramTag("test",program=self.program,default="the default"),"program tag value")


        # Now set the general tag
        Tag.setTag("test",target=None,value="general tag value")

        self.assertEqual(Tag.getProgramTag("test",program=None),"general tag value")
        self.assertEqual(Tag.getProgramTag("test",program=None),"general tag value")
        self.assertEqual(Tag.getProgramTag("test",program=None,default="the default"),"general tag value")
        self.assertEqual(Tag.getProgramTag("test",program=None,default="the default"),"general tag value")
        self.assertEqual(Tag.getProgramTag("test",program=self.program),"program tag value")
        self.assertEqual(Tag.getProgramTag("test",program=self.program),"program tag value")
        self.assertEqual(Tag.getProgramTag("test",program=self.program,default="the default"),"program tag value")
        self.assertEqual(Tag.getProgramTag("test",program=self.program,default="the default"),"program tag value")


        # Now unset the program-specific tag
        Tag.unSetTag("test",target=self.program)

        self.assertEqual(Tag.getProgramTag("test",program=None),"general tag value")
        self.assertEqual(Tag.getProgramTag("test",program=None),"general tag value")
        self.assertEqual(Tag.getProgramTag("test",program=None,default="the default"),"general tag value")
        self.assertEqual(Tag.getProgramTag("test",program=None,default="the default"),"general tag value")
        self.assertEqual(Tag.getProgramTag("test",program=self.program),"general tag value")
        self.assertEqual(Tag.getProgramTag("test",program=self.program),"general tag value")
        self.assertEqual(Tag.getProgramTag("test",program=self.program,default="the default"),"general tag value")
        self.assertEqual(Tag.getProgramTag("test",program=self.program,default="the default"),"general tag value")

        #just to clean up
        Tag.unSetTag("test",target=None)

        self.assertFalse(Tag.getProgramTag("test",program=self.program))
        self.assertFalse(Tag.getProgramTag("test",program=self.program))
        self.assertFalse(Tag.getProgramTag("test",program=None))
        self.assertFalse(Tag.getProgramTag("test",program=None))
        self.assertEqual(Tag.getProgramTag("test",program=self.program,default="the default"),"the default")
        self.assertEqual(Tag.getProgramTag("test",program=self.program,default="the default"),"the default")
        self.assertEqual(Tag.getProgramTag("test",program=None,default="the default"),"the default")
        self.assertEqual(Tag.getProgramTag("test",program=None,default="the default"),"the default")