Beispiel #1
0
    def forwards(self, orm):
        #   Set up accounts
        gac = GlobalAccountingController()
        gac.setup_accounts()

        for program in orm['program.Program'].objects.all().order_by('id'):
            pac = ProgramAccountingController(program)
            pac.setup_accounts()
    def students(self, QObject = False):
        #   This query represented students who have a payment transfer from the outside
        pac = ProgramAccountingController(self.program)
        QObj = Q(transfer__source__isnull=True, transfer__line_item=pac.default_payments_lineitemtype())

        if QObject:
            return {'creditcard': QObj}
        else:
            return {'creditcard':ESPUser.objects.filter(QObj).distinct()}
    def students(self, QObject=False):
        #   This query represented students who have a payment transfer from the outside
        pac = ProgramAccountingController(self.program)
        QObj = Q(transfer__source__isnull=True,
                 transfer__line_item=pac.default_payments_lineitemtype())

        if QObject:
            return {'creditcard': QObj}
        else:
            return {'creditcard': ESPUser.objects.filter(QObj).distinct()}
Beispiel #4
0
    def studentDesc(self):
        """ Return a description for each line item type that students can be filtered by. """
        student_desc = {}
        pac = ProgramAccountingController(self.program)
        for i in pac.get_lineitemtypes(optional_only=True):
            student_desc[
                'extracosts_%d' %
                i.id] = """Students who have opted for '%s'""" % i.text

        return student_desc
    def studentDesc(self):
        """ Return a description for each line item type that students can be filtered by. """
        student_desc = {}
        pac = ProgramAccountingController(self.program)
        for line_item_type in pac.get_lineitemtypes(optional_only=True):
            student_desc['extracosts_%d' % line_item_type.id] = """Students who have opted for '%s'""" % line_item_type.text
            for option in line_item_type.options:
                (option_id, option_amount, option_description) = option
                key = 'extracosts_%d_%d' % (line_item_type.id, option_id)
                student_desc[key] = """Students who have opted for '%s' for '%s' ($%s)""" % (option_description, line_item_type.text, option_amount or line_item_type.amount_dec)

        return student_desc
    def viewpay_cybersource(self, request, tl, one, two, module, extra, prog):
        pac = ProgramAccountingController(prog)
        student_list = list(pac.all_students())
        payment_table = []

        for student in student_list:
            iac = IndividualAccountingController(prog, student)
            payment_table.append((student, iac.get_transfers(),
                                  iac.amount_requested(), iac.amount_due()))

        context = {'program': prog, 'payment_table': payment_table}

        return render_to_response(self.baseDir() + 'viewpay_cybersource.html',
                                  request, context)
Beispiel #7
0
    def test_extracosts(self):
        """ Verify that the "Student Extra Costs" module behaves as specified. """

        program_cost = 25.0

        #   Set up some extra costs options: Item1 (max-qty 1) for $10, Item2 (max-qty 10) for $5, and selectable food
        pac = ProgramAccountingController(self.program)
        pac.clear_all_data()
        pac.setup_accounts()
        pac.setup_lineitemtypes(program_cost, [('Item1', 10, 1), ('Item2', 5, 10)], [('Food', [('Small', 3), ('Large', 7)])])

        #   Choose a random student and check that the extracosts page loads
        student = random.choice(self.students)
        iac = IndividualAccountingController(self.program, student)
        self.assertTrue( self.client.login( username=student.username, password='******' ), "Couldn't log in as student %s" % student.username )
        response = self.client.get('/learn/%s/extracosts' % self.program.url)
        self.assertEqual(response.status_code, 200)

        #   Check that they are being charged the program admission fee
        self.assertEqual(iac.amount_due(), program_cost)

        #   Check that selecting one of the "buy-one" extra items works
        lit = LineItemType.objects.get(program=self.program, text='Item1')
        response = self.client.post('/learn/%s/extracosts' % self.program.getUrlBase(), {'%d-cost' % lit.id: 'checked'})
        self.assertEqual(response.status_code, 302)
        self.assertIn('/learn/%s/studentreg' % self.program.url, response['Location'])
        self.assertEqual(iac.amount_due(), program_cost + 10)

        #   Check that selecting one or more than one of the "buy many" extra items works
        lit = LineItemType.objects.get(program=self.program, text='Item2')
        response = self.client.post('/learn/%s/extracosts' % self.program.getUrlBase(), {'%d-cost' % lit.id: 'checked', '%d-count' % lit.id: '1'})
        self.assertEqual(response.status_code, 302)
        self.assertIn('/learn/%s/studentreg' % self.program.url, response['Location'])
        self.assertEqual(iac.amount_due(), program_cost + 5)

        response = self.client.post('/learn/%s/extracosts' % self.program.getUrlBase(), {'%d-cost' % lit.id: 'checked', '%d-count' % lit.id: '3'})
        self.assertEqual(response.status_code, 302)
        self.assertIn('/learn/%s/studentreg' % self.program.url, response['Location'])
        self.assertEqual(iac.amount_due(), program_cost + 15)

        #   Check that selecting an option for a "multiple choice" extra item works
        lit = LineItemType.objects.get(program=self.program, text='Food')
        lio = filter(lambda x: x[2] == 'Large', lit.options)[0]
        response = self.client.post('/learn/%s/extracosts' % self.program.getUrlBase(), {'multi%d-option' % lit.id: str(lio[0])})
        self.assertEqual(response.status_code, 302)
        self.assertIn('/learn/%s/studentreg' % self.program.url, response['Location'])
        self.assertEqual(iac.amount_due(), program_cost + 7)

        #   Check that financial aid applies to the "full" cost including the extra items
        #   (e.g. we are not forcing financial aid students to pay for food)
        request = FinancialAidRequest.objects.create(user=student, program=self.program)
        (fg, created) = FinancialAidGrant.objects.get_or_create(request=request, percent=100)
        self.assertEqual(iac.amount_due(), 0.0)
        fg.delete()

        #   Check that removing items on the form removes their cost for the student
        response = self.client.post('/learn/%s/extracosts' % self.program.getUrlBase(), {})
        self.assertEqual(response.status_code, 302)
        self.assertIn('/learn/%s/studentreg' % self.program.url, response['Location'])
        self.assertEqual(iac.amount_due(), program_cost)
Beispiel #8
0
    def students(self, QObject=False):
        """ Return the useful lists of students for the Extra Costs module. """

        student_lists = {}
        pac = ProgramAccountingController(self.program)

        # Get all the line item types for this program.
        for i in pac.get_lineitemtypes(optional_only=True):
            if QObject:
                student_lists['extracosts_%d' % i.id] = self.getQForUser(
                    Q(transfer__line_item=i))
            else:
                student_lists['extracosts_%d' % i.id] = ESPUser.objects.filter(
                    transfer__line_item=i).distinct()

        return student_lists
Beispiel #9
0
    def studentDesc(self):
        """ Return a description for each line item type that students can be filtered by. """
        student_desc = {}
        pac = ProgramAccountingController(self.program)
        for line_item_type in pac.get_lineitemtypes(optional_only=True):
            student_desc[
                'extracosts_%d' % line_item_type.
                id] = """Students who have opted for '%s'""" % line_item_type.text
            for option in line_item_type.options:
                (option_id, option_amount, option_description) = option
                key = 'extracosts_%d_%d' % (line_item_type.id, option_id)
                student_desc[
                    key] = """Students who have opted for '%s' for '%s' ($%s)""" % (
                        option_description, line_item_type.text, option_amount
                        or line_item_type.amount_dec)

        return student_desc
    def test_extracosts(self):
        """ Verify that the "Student Extra Costs" module behaves as specified. """

        program_cost = 25.0

        #   Set up some extra costs options: Item1 (max-qty 1) for $10, Item2 (max-qty 10) for $5, and selectable food
        pac = ProgramAccountingController(self.program)
        pac.clear_all_data()
        pac.setup_accounts()
        pac.setup_lineitemtypes(program_cost, [('Item1', 10, 1), ('Item2', 5, 10)], [('Food', [('Small', 3), ('Large', 7)])])

        #   Choose a random student and check that the extracosts page loads
        student = random.choice(self.students)
        iac = IndividualAccountingController(self.program, student)
        self.assertTrue( self.client.login( username=student.username, password='******' ), "Couldn't log in as student %s" % student.username )
        response = self.client.get('/learn/%s/extracosts' % self.program.url)
        self.assertEqual(response.status_code, 200)

        #   Check that they are being charged the program admission fee
        self.assertEqual(iac.amount_due(), program_cost)

        #   Check that selecting one of the "buy-one" extra items works
        lit = LineItemType.objects.get(program=self.program, text='Item1')
        response = self.client.post('/learn/%s/extracosts' % self.program.getUrlBase(), {'%d-cost' % lit.id: 'checked'})
        self.assertEqual(response.status_code, 302)
        self.assertIn('/learn/%s/studentreg' % self.program.url, response['Location'])
        self.assertEqual(iac.amount_due(), program_cost + 10)

        #   Check that selecting one or more than one of the "buy many" extra items works
        lit = LineItemType.objects.get(program=self.program, text='Item2')
        response = self.client.post('/learn/%s/extracosts' % self.program.getUrlBase(), {'%d-cost' % lit.id: 'checked', '%d-count' % lit.id: '1'})
        self.assertEqual(response.status_code, 302)
        self.assertIn('/learn/%s/studentreg' % self.program.url, response['Location'])
        self.assertEqual(iac.amount_due(), program_cost + 5)

        response = self.client.post('/learn/%s/extracosts' % self.program.getUrlBase(), {'%d-cost' % lit.id: 'checked', '%d-count' % lit.id: '3'})
        self.assertEqual(response.status_code, 302)
        self.assertIn('/learn/%s/studentreg' % self.program.url, response['Location'])
        self.assertEqual(iac.amount_due(), program_cost + 15)

        #   Check that selecting an option for a "multiple choice" extra item works
        lit = LineItemType.objects.get(program=self.program, text='Food')
        lio = filter(lambda x: x[2] == 'Large', lit.options)[0]
        response = self.client.post('/learn/%s/extracosts' % self.program.getUrlBase(), {'multi%d-option' % lit.id: str(lio[0])})
        self.assertEqual(response.status_code, 302)
        self.assertIn('/learn/%s/studentreg' % self.program.url, response['Location'])
        self.assertEqual(iac.amount_due(), program_cost + 7)

        #   Check that financial aid applies to the "full" cost including the extra items
        #   (e.g. we are not forcing financial aid students to pay for food)
        request = FinancialAidRequest.objects.create(user=student, program=self.program)
        (fg, created) = FinancialAidGrant.objects.get_or_create(request=request, percent=100)
        self.assertEqual(iac.amount_due(), 0.0)
        fg.delete()

        #   Check that removing items on the form removes their cost for the student
        response = self.client.post('/learn/%s/extracosts' % self.program.getUrlBase(), {})
        self.assertEqual(response.status_code, 302)
        self.assertIn('/learn/%s/studentreg' % self.program.url, response['Location'])
        self.assertEqual(iac.amount_due(), program_cost)
Beispiel #11
0
    def viewpay_cybersource(self, request, tl, one, two, module, extra, prog):
        pac = ProgramAccountingController(prog)
        student_list = list(pac.all_students())
        payment_table = []

        #   Fetch detailed information for every student associated with the program
        for student in student_list:
            iac = IndividualAccountingController(prog, student)
            payment_table.append((student, iac.get_transfers(), iac.amount_requested(), iac.amount_due()))

        #   Also fetch summary information about the payments
        (num_payments, total_payment) = pac.payments_summary()

        context = {
            'program': prog,
            'payment_table': payment_table,
            'num_students': len(student_list),
            'num_payments': num_payments,
            'total_payment': total_payment,
        }

        return render_to_response(self.baseDir() + 'viewpay_cybersource.html', request, context)
Beispiel #12
0
def commit_program(prog, perms, modules, cost=0, sibling_discount=None):
    #   This function implements the changes suggested by prepare_program.

    def gen_perm(tup):
        new_perm=Permission(permission_type=tup[0], program=prog)

        if tup[2]:
            new_perm.start_date = tup[2]
        if tup[3]:
            new_perm.end_date = tup[3]

        if tup[1] is not None:
            new_perm.user=tup[1]
            new_perm.save()
            return
        elif tup[1] is None and tup[0].startswith("Student"):
            new_perm.role=Group.objects.get(name="Student")
            new_perm.save()
            return
        elif tup[1] is None and tup[0].startswith("Teacher"):
            new_perm.role=Group.objects.get(name="Teacher")
            new_perm.save()
            return

        #It's not for a specific user and not a teacher or student deadline
        for x in ESPUser.getTypes():
            newnew_perm=Permission(permission_type=new_perm.permission_type, role=Group.objects.get(name=x), start_date=new_perm.start_date, end_date=new_perm.end_date, program=prog)
            newnew_perm.save()

    for perm_tup in perms:
        gen_perm(perm_tup)

    pac = ProgramAccountingController(prog)
    pac.setup_accounts()
    pac.setup_lineitemtypes(cost)
    prog.sibling_discount = sibling_discount # property saves Tag, no explicit save needed

    return prog
    def students(self, QObject = False):
        """ Return the useful lists of students for the Extra Costs module. """

        student_lists = OrderedDict()
        pac = ProgramAccountingController(self.program)

        # Get all the line item types for this program.
        for i in pac.get_lineitemtypes(optional_only=True):
            if QObject:
                students = pac.all_students_Q(lineitemtype_id=i.id)
                student_lists['extracosts_%d' % i.id] = students
            else:
                students = pac.all_students(lineitemtype_id=i.id).distinct()
                student_lists['extracosts_%d' % i.id] = students
            for option in i.options:
                key = 'extracosts_%d_%d' % (i.id, option[0])
                filter_qobject = Q(transfer__option=option[0])
                if QObject:
                    student_lists[key] = students & filter_qobject
                else:
                    student_lists[key] = students.filter(filter_qobject).distinct()

        return student_lists
Beispiel #14
0
    def students(self, QObject=False):
        """ Return the useful lists of students for the Extra Costs module. """

        student_lists = OrderedDict()
        pac = ProgramAccountingController(self.program)

        # Get all the line item types for this program.
        for i in pac.get_lineitemtypes(optional_only=True):
            if QObject:
                students = pac.all_students_Q(lineitemtype_id=i.id)
                student_lists['extracosts_%d' % i.id] = students
            else:
                students = pac.all_students(lineitemtype_id=i.id).distinct()
                student_lists['extracosts_%d' % i.id] = students
            for option in i.options:
                key = 'extracosts_%d_%d' % (i.id, option[0])
                filter_qobject = Q(transfer__option=option[0])
                if QObject:
                    student_lists[key] = students & filter_qobject
                else:
                    student_lists[key] = students.filter(
                        filter_qobject).distinct()

        return student_lists
Beispiel #15
0
def commit_program(prog, perms, modules, cost=0, sibling_discount=None):
    #   This function implements the changes suggested by prepare_program.

    def gen_perm(tup):
        new_perm = Permission(permission_type=tup[0], program=prog)

        if tup[2]:
            new_perm.start_date = tup[2]
        if tup[3]:
            new_perm.end_date = tup[3]

        if tup[1] is not None:
            new_perm.user = tup[1]
            new_perm.save()
            return
        elif tup[1] is None and tup[0].startswith("Student"):
            new_perm.role = Group.objects.get(name="Student")
            new_perm.save()
            return
        elif tup[1] is None and tup[0].startswith("Teacher"):
            new_perm.role = Group.objects.get(name="Teacher")
            new_perm.save()
            return

        #It's not for a specific user and not a teacher or student deadline
        for x in ESPUser.getTypes():
            newnew_perm = Permission(permission_type=new_perm.permission_type,
                                     role=Group.objects.get(name=x),
                                     start_date=new_perm.start_date,
                                     end_date=new_perm.end_date,
                                     program=prog)
            newnew_perm.save()

    for perm_tup in perms:
        gen_perm(perm_tup)

    pac = ProgramAccountingController(prog)
    pac.setup_accounts()
    pac.setup_lineitemtypes(cost)
    prog.sibling_discount = sibling_discount  # property saves Tag, no explicit save needed

    return prog
Beispiel #16
0
 def line_item_type(self):
     pac = ProgramAccountingController(self.program)
     (donate_type, created) = pac.get_lineitemtypes().get_or_create(program=self.program, text=self.get_setting('donation_text'))
     return donate_type
Beispiel #17
0
    def test_finaid(self):
        """ Verify that financial aid behaves as specified. """

        program_cost = 25.0

        #   Set the cost of the program
        pac = ProgramAccountingController(self.program)
        pac.clear_all_data()
        pac.setup_accounts()
        pac.setup_lineitemtypes(program_cost)

        #   Choose a random student and sign up for a class
        student = random.choice(self.students)
        iac = IndividualAccountingController(self.program, student)
        sec = random.choice(self.program.sections())
        sec.preregister_student(student)

        #   Check that the student owes the cost of the program
        self.assertEqual(iac.amount_due(), program_cost)

        #   Apply for financial aid
        self.assertTrue( self.client.login( username=student.username, password='******' ), "Couldn't log in as student %s" % student.username )
        response = self.client.get(
                    '/learn/%s/finaid' % self.program.url,
                    **{'wsgi.url_scheme': 'https'})
        self.assertEqual(response.status_code, 200)

        form_settings = {
            'reduced_lunch': '',
            'household_income': '12345',
            'extra_explaination': 'No',
            'student_prepare': '',
        }
        response = self.client.post('/learn/%s/finaid' % self.program.getUrlBase(), form_settings)
        self.assertEqual(response.status_code, 302)
        self.assertIn('/learn/%s/studentreg' % self.program.url, response['Location'])

        #   Check that the student still owes the cost of the program
        self.assertEqual(iac.amount_due(), program_cost)

        #   Have an admin approve the financial aid app and check a few different cases:
        request = FinancialAidRequest.objects.get(user=student, program=self.program)

        #   - 100 percent
        (fg, created) = FinancialAidGrant.objects.get_or_create(request=request, percent=100)
        self.assertEqual(iac.amount_due(), 0.0)

        #   - absolute discount amount
        fg.percent = None
        fg.amount_max_dec = Decimal('15.0')
        fg.save()
        self.assertEqual(iac.amount_due(), program_cost - 15.0)

        #   - discount percentage
        fg.amount_max_dec = None
        fg.percent = 50
        fg.save()
        self.assertEqual(iac.amount_due(), program_cost / 2)

        #   Check that deleting the financial aid grant restores original program cost
        fg.delete()
        self.assertEqual(iac.amount_due(), program_cost)

        #   Check that the 'free/reduced lunch' option on the finaid results in zero amount due
        form_settings = {
            'reduced_lunch': 'checked',
            'household_income': '12345',
            'extra_explaination': 'No',
            'student_prepare': '',
        }
        response = self.client.post('/learn/%s/finaid' % self.program.getUrlBase(), form_settings)
        self.assertEqual(response.status_code, 302)
        self.assertIn('/learn/%s/studentreg' % self.program.url, response['Location'])
        self.assertEqual(iac.amount_due(), 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
Beispiel #19
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
Beispiel #20
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})
Beispiel #21
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
        })
    def test_finaid(self):
        """ Verify that financial aid behaves as specified. """

        program_cost = 25.0

        #   Set the cost of the program
        pac = ProgramAccountingController(self.program)
        pac.clear_all_data()
        pac.setup_accounts()
        pac.setup_lineitemtypes(program_cost)

        #   Choose a random student and sign up for a class
        student = random.choice(self.students)
        iac = IndividualAccountingController(self.program, student)
        sec = random.choice(self.program.sections())
        sec.preregister_student(student)

        #   Check that the student owes the cost of the program
        self.assertEqual(iac.amount_due(), program_cost)

        #   Apply for financial aid
        self.assertTrue( self.client.login( username=student.username, password='******' ), "Couldn't log in as student %s" % student.username )
        response = self.client.get(
                    '/learn/%s/finaid' % self.program.url,
                    **{'wsgi.url_scheme': 'https'})
        self.assertEqual(response.status_code, 200)

        form_settings = {
            'reduced_lunch': '',
            'household_income': '12345',
            'extra_explaination': 'No',
            'student_prepare': '',
        }
        response = self.client.post('/learn/%s/finaid' % self.program.getUrlBase(), form_settings)
        self.assertEqual(response.status_code, 302)
        self.assertIn('/learn/%s/studentreg' % self.program.url, response['Location'])

        #   Check that the student still owes the cost of the program
        self.assertEqual(iac.amount_due(), program_cost)

        #   Have an admin approve the financial aid app and check a few different cases:
        request = FinancialAidRequest.objects.get(user=student, program=self.program)

        #   - 100 percent
        (fg, created) = FinancialAidGrant.objects.get_or_create(request=request, percent=100)
        self.assertEqual(iac.amount_due(), 0.0)

        #   - absolute discount amount
        fg.percent = None
        fg.amount_max_dec = Decimal('15.0')
        fg.save()
        self.assertEqual(iac.amount_due(), program_cost - 15.0)

        #   - discount percentage
        fg.amount_max_dec = None
        fg.percent = 50
        fg.save()
        self.assertEqual(iac.amount_due(), program_cost / 2)

        #   Check that deleting the financial aid grant restores original program cost
        fg.delete()
        self.assertEqual(iac.amount_due(), program_cost)

        #   Check that the 'free/reduced lunch' option on the finaid results in zero amount due
        form_settings = {
            'reduced_lunch': 'checked',
            'household_income': '12345',
            'extra_explaination': 'No',
            'student_prepare': '',
        }
        response = self.client.post('/learn/%s/finaid' % self.program.getUrlBase(), form_settings)
        self.assertEqual(response.status_code, 302)
        self.assertIn('/learn/%s/studentreg' % self.program.url, response['Location'])
        self.assertEqual(iac.amount_due(), 0)
Beispiel #23
0
def migrate_program(program):

    docs = Document.objects.filter(anchor=program.anchor, doctype=2)
    num_uncategorized = 0
    num = 0
    found_for_program = False
    student_dict = {}

    #   Clear financial data for this program
    pac = ProgramAccountingController(program)
    pac.clear_all_data()
    lineitem_types = {'one': {}, 'multi': {}, 'select': []}
    lineitem_choices = {}

    #   Build a database of each student's financial transactions for the program
    for doc in docs:
        student_id = doc.user.id

        if student_id not in student_dict:
            student_dict[student_id] = {
                'admission': {},
                'finaid_amt': Decimal('0'),
                'items': [],
                'items_select': [],
                'payment': [],
            }
        lineitems = doc.txn.lineitem_set.all()
        """
		if lineitems.count() > 0:
			try:
				print '\nStudent: %s' % doc.user.name()
			except:
				print '\nStudent: %s' % doc.user.username
		"""
        for li in lineitems:
            found_for_program = True
            base_txt = '[%5d] %s: %.2f' % (li.id, li.text, li.amount)
            #   if li.anchor.uri == splash.anchor.uri + '/LineItemTypes/Required':
            if 'admission' in li.text.lower(
            ) or 'cost of attending' in li.text.lower():
                base_txt = '(Admission) ' + base_txt
                if li.text not in student_dict[student_id]['admission']:
                    student_dict[student_id]['admission'][
                        li.text] = li.amount.copy_abs()
            elif 'financial aid' in li.text.lower():
                base_txt = '(Finaid) ' + base_txt
                student_dict[student_id]['finaid_amt'] += li.amount
            elif 'payment received' in li.text.lower():
                base_txt = '(Payment) ' + base_txt
                student_dict[student_id]['payment'].append(
                    li.amount.copy_abs())
            elif 'expecting on-site payment' in li.text.lower():
                base_txt = '(Payment expected) ' + base_txt
                student_dict[student_id]['payment'].append(
                    li.amount.copy_abs())
            elif 'BuyMultiSelect' in li.anchor.uri:
                base_txt = '(Select: field "%s", choice "%s") ' % (
                    li.anchor.name, li.text) + base_txt
                student_dict[student_id]['items_select'].append(
                    (li.anchor.name, li.text, li.amount.copy_abs()))
                if li.anchor.name not in lineitem_types['select']:
                    lineitem_types['select'].append(li.anchor.name)
                    lineitem_choices[li.anchor.name] = []
                if (li.text, li.amount.copy_abs()
                    ) not in lineitem_choices[li.anchor.name]:
                    lineitem_choices[li.anchor.name].append(
                        (li.text, li.amount.copy_abs()))
            elif 'BuyMany' in li.anchor.uri or 'shirt' in li.text.lower():
                base_txt = '(Multi: field "%s") ' % (li.anchor.name) + base_txt
                student_dict[student_id]['items'].append(
                    (li.text, li.amount.copy_abs()))
                if li.text not in lineitem_types['multi']:
                    lineitem_types['multi'][li.text] = li.amount.copy_abs()
            elif 'BuyOne' in li.anchor.uri or 'lunch' in li.text.lower(
            ) or 'dinner' in li.text.lower() or 'photo' in li.text.lower():
                base_txt = '(Single: field "%s") ' % (
                    li.anchor.name) + base_txt
                student_dict[student_id]['items'].append(
                    (li.text, li.amount.copy_abs()))
                if li.text not in lineitem_types['one']:
                    lineitem_types['one'][li.text] = li.amount.copy_abs()
            else:
                num_uncategorized += 1
                print 'WARNING: Uncategorized line item: %s' % base_txt
                #   raise Exception('Uncategorized line item: %s' % base_txt)

            num += 1
            #   print '-- %s' % base_txt
        """
		if student_dict[student_id]['finaid_amt'] > 0:
			print student_dict[student_id]
		elif len(student_dict[student_id]['items_multi']) > 0:
			print student_dict[student_id]
		"""

    if found_for_program:
        num_programs = 1
    else:
        num_programs = 0

    #   Populate line item types for the program
    optional_items = []
    for item in lineitem_types['one']:
        optional_items.append((item, lineitem_types['one'][item], 1))
    for item in lineitem_types['multi']:
        optional_items.append((item, lineitem_types['multi'][item], 10))
    select_items = []
    for item in lineitem_types['select']:
        select_items.append((item, lineitem_choices[item]))

    #   print optional_items
    #   print select_items
    pac.setup_accounts()
    pac.setup_lineitemtypes(0.0, optional_items, select_items)

    #   Create new transfer records for this student
    for student_id in student_dict:
        user = ESPUser.objects.get(id=student_id)
        iac = IndividualAccountingController(program, user)
        rec = student_dict[student_id]

        #   Admission fee
        admission_total_cost = 0
        for key in rec['admission']:
            admission_total_cost += rec['admission'][key]
        if admission_total_cost > 0:
            initial_transfer = iac.add_required_transfers()[0]
            initial_transfer.amount_dec = admission_total_cost
            initial_transfer.save()

        #   Financial aid
        if rec['finaid_amt'] > 0:
            if rec['finaid_amt'] >= admission_total_cost:
                iac.grant_full_financial_aid()
            else:
                iac.set_finaid_params(rec['finaid_amt'], None)

        #   Optional items
        prefs = []
        for item in rec['items']:
            prefs.append((item[0], 1, item[1]))
        for item in rec['items_select']:
            prefs.append((item[0], 1, item[2]))
        iac.apply_preferences(prefs)

        #   Payments
        for payment in rec['payment']:
            iac.submit_payment(payment)

    try:
        attended_ids = program.students()['attended'].values_list('id',
                                                                  flat=True)

        #   Execute transfers for the students that are marked as attended
        pac.execute_pending_transfers(
            ESPUser.objects.filter(id__in=attended_ids))

        #   Clear transfers for the students that are marked as not attended
        pac.remove_pending_transfers(
            ESPUser.objects.exclude(id__in=attended_ids))
    except:
        print 'Unable to determine which students attended the program; all transfers remain unexecuted'

    print 'Converted %d line items for %s; %d uncategorized' % (
        num, program.niceName(), num_uncategorized)
    return num_programs