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()}
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)
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)
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
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)
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)
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
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
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 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
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
def stats(prog): # Create a dictionary to assemble the output dictOut = {"stats": []} classes = prog.classes().select_related() vitals = {'id': 'vitals'} class_num_list = [] class_num_list.append( ("Total # of Classes", classes.distinct().count())) class_num_list.append( ("Total # of Class Sections", prog.sections().select_related().distinct().count())) class_num_list.append( ("Total # of Lunch Classes", classes.filter(category__category="Lunch").filter( status=10).distinct().count())) class_num_list.append( ("Total # of Classes <span style='color: #00C;'>Unreviewed</span>", classes.filter(status=0).distinct().count())) class_num_list.append( ("Total # of Classes <span style='color: #0C0;'>Accepted</span>", classes.filter(status=10).distinct().count())) class_num_list.append( ("Total # of Classes <span style='color: #C00;'>Rejected</span>", classes.filter(status=-10).distinct().count())) class_num_list.append( ("Total # of Classes <span style='color: #990;'>Cancelled</span>", classes.filter(status=-20).distinct().count())) for ft in ClassFlagType.get_flag_types(prog): class_num_list.append( ('Total # of Classes with the "%s" flag' % ft.name, classes.filter(flags__flag_type=ft).distinct().count())) vitals['classnum'] = class_num_list # Display pretty labels for teacher and student numbers teacher_labels_dict = {} for module in prog.getModules(): teacher_labels_dict.update(module.teacherDesc()) vitals['teachernum'] = [] ## Ew, queries in a for loop... ## Not much to be done about it, though; ## the loop is iterating over a list of independent queries and running each. teachers = prog.teachers() for key in teachers.keys(): if key in teacher_labels_dict: vitals['teachernum'].append(( teacher_labels_dict[key], ## Unfortunately, teachers[key].filter(is_active=True).distinct().count())) else: vitals['teachernum'].append( (key, teachers[key].filter(is_active=True).distinct().count())) student_labels_dict = {} for module in prog.getModules(): student_labels_dict.update(module.studentDesc()) vitals['studentnum'] = [] ## Ew, another set of queries in a for loop... ## Same justification, though. students = prog.students() for key in students.keys(): if key in student_labels_dict: vitals['studentnum'].append( (student_labels_dict[key], students[key].filter(is_active=True).distinct().count())) else: vitals['studentnum'].append( (key, students[key].filter(is_active=True).distinct().count())) timeslots = prog.getTimeSlots() vitals['timeslots'] = [] shours = 0.0 chours = 0.0 crhours = 0.0 ## Write this as a 'for' loop because PostgreSQL can't do it in ## one go without a subquery or duplicated logic, and Django ## doesn't have enough power to expose either approach directly. ## At least there aren't any queries in the for loop... ## (In MySQL, this could I believe be done with a minimally-painful extra() clause.) ## Also, since we're iterating over a big data set, use .values() ## minimize the number of objects that we're creating. ## One dict and two Decimals per row, as opposed to ## an Object per field and all kinds of stuff... for cls in prog.classes().exclude(category__category='Lunch').annotate( num_sections=Count('sections'), subject_duration=Sum('sections__duration'), subject_students=Sum('sections__enrolled_students')).values( 'num_sections', 'subject_duration', 'subject_students', 'class_size_max'): if cls['subject_duration']: chours += float(cls['subject_duration']) shours += float(cls['subject_duration']) * (float( cls['class_size_max']) if cls['class_size_max'] else 0) crhours += float(cls['subject_duration']) * float( cls['subject_students']) / float(cls['num_sections']) vitals["hournum"] = [] vitals["hournum"].append(("Total # of Class-Hours", chours)) vitals["hournum"].append( ("Total # of Class-Student-Hours (capacity)", shours)) vitals["hournum"].append( ("Total # of Class-Student-Hours (registered)", crhours)) ## Prefetch enough data that get_meeting_times() and num_students() don't have to hit the db curclasses = ClassSection.prefetch_catalog_data( ClassSection.objects.filter( parent_class__parent_program=prog).select_related( 'parent_class', 'parent_class__category')) ## Is it really faster to do this logic in Python? ## It'd be even faster to just write a raw SQL query to do it. ## But this is probably good enough. timeslot_dict = defaultdict(list) timeslot_set = set(timeslots) for section in curclasses: for timeslot in set.intersection(timeslot_set, section.get_meeting_times()): timeslot_dict[timeslot].append(section) for timeslot in timeslots: curTimeslot = {'slotname': timeslot.short_description} curTimeslot['classcount'] = len(timeslot_dict[timeslot]) def student_count(clslist): lst = [0] + [ x.num_students() for x in clslist if x.category.category != 'Lunch' ] return reduce(operator.add, lst) def student_max_count(clslist): lst = [0] + [ x.capacity for x in clslist if x.category.category != 'Lunch' ] return reduce(operator.add, lst) curTimeslot['studentcount'] = { 'count': student_count(timeslot_dict[timeslot]), 'max_count': student_max_count(timeslot_dict[timeslot]) } vitals['timeslots'].append(curTimeslot) dictOut["stats"].append(vitals) shirt_data = { "id": "shirtnum" } adminvitals_shirt = prog.getShirtInfo() shirt_data["sizes"] = adminvitals_shirt['shirt_sizes'] shirt_data["types"] = adminvitals_shirt['shirt_types'] shirt_data["data"] = adminvitals_shirt['shirts'] dictOut["stats"].append(shirt_data) Q_categories = Q(program=prog) crmi = prog.classregmoduleinfo if crmi.open_class_registration: Q_categories |= Q(pk=prog.open_class_category.pk) # Introduce a separate query to get valid categories, since the single query seemed to introduce duplicates program_categories = ClassCategories.objects.filter( Q_categories).distinct().values_list('id', flat=True) annotated_categories = ClassCategories.objects.filter( cls__parent_program=prog, cls__status__gte=0).annotate( num_subjects=Count('cls', distinct=True), num_sections=Count('cls__sections'), num_class_hours=Sum('cls__sections__duration')).order_by( '-num_subjects').values('id', 'num_sections', 'num_subjects', 'num_class_hours', 'category').distinct() # Convert Decimal values to float for serialization for i in range(len(annotated_categories)): annotated_categories[i]['num_class_hours'] = float( annotated_categories[i]['num_class_hours']) dictOut["stats"].append({ "id": "categories", "data": filter(lambda x: x['id'] in program_categories, annotated_categories) }) ## Calculate the grade data: grades = [i for i in range(prog.grade_min, prog.grade_max + 1)] # We can't perfectly trust most_recent_profile, but it's good enough for stats students_grades = students['enrolled'].filter( registrationprofile__most_recent_profile=True) students_grades = students_grades.values_list( 'registrationprofile__student_info__graduation_year') students_grades = students_grades.annotate(Count('id', distinct=True)) grades_dict = {result[0]: result[1] for result in students_grades} grades_results = [] for g in grades: year = ESPUser.YOGFromGrade(g, ESPUser.program_schoolyear(prog)) grade_classes = classes.filter(status__gte=0, grade_min__lte=g, grade_max__gte=g) grade_sections = prog.sections().filter( status__gte=0, parent_class__in=grade_classes) grades_results.append({ 'grade': g, 'num_subjects': grade_classes.count(), 'num_sections': grade_sections.count(), 'num_students': grades_dict[year] if year in grades_dict else 0 }) dictOut["stats"].append({"id": "grades", "data": grades_results}) # Add SplashInfo statistics if our program has them splashinfo_data = {} splashinfo_modules = filter(lambda x: isinstance(x, SplashInfoModule), prog.getModules('learn')) if len(splashinfo_modules) > 0: splashinfo_module = splashinfo_modules[0] tag_data = Tag.getProgramTag('splashinfo_choices', prog) if tag_data: splashinfo_choices = json.loads(tag_data) else: splashinfo_choices = { 'lunchsat': SplashInfoForm.default_choices, 'lunchsun': SplashInfoForm.default_choices } for key in splashinfo_choices: counts = {} for item in splashinfo_choices[key]: filter_kwargs = {'program': prog} filter_kwargs[key] = item[0] counts[item[1]] = SplashInfo.objects.filter( **filter_kwargs).distinct().count() splashinfo_data[key] = counts splashinfo_data['siblings'] = { 'yes': SplashInfo.objects.filter( program=prog, siblingdiscount=True).distinct().count(), 'no': SplashInfo.objects.filter(program=prog).exclude( siblingdiscount=True).distinct().count() } dictOut["stats"].append({ "id": "splashinfo", "data": splashinfo_data }) # Add accounting stats pac = ProgramAccountingController(prog) (num_payments, total_payment) = pac.payments_summary() accounting_data = { 'num_payments': num_payments, # We need to convert to a float in order for json to serialize it. # Since we're not doing any computation client-side with these # numbers, this doesn't cause accuracy issues. If the # total_payment is None, just coerce it to zero for display # purposes. 'total_payments': float(total_payment or 0), } dictOut["stats"].append({"id": "accounting", "data": accounting_data}) return dictOut
def 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})
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)
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