def viewlotteryprefs(self, request, tl, one, two, module, extra, prog): context = {} context['student'] = request.user priority_classids = set() uniquified_flags = [] priority_flags = StudentRegistration.valid_objects().filter(user=request.user, section__parent_class__parent_program=prog, relationship__name='Priority/1') for flag in priority_flags: if flag.section.id not in priority_classids: priority_classids.add(flag.section.id) uniquified_flags.append(flag) context['priority'] = uniquified_flags if priority_flags.count() == 0: context['pempty'] = True else: context['pempty'] = False interested_classids = set() uniquified_interested = [] interested = StudentRegistration.valid_objects().filter(user=request.user, section__parent_class__parent_program=prog, relationship__name='Interested') for flag in interested: if flag.section.id not in interested_classids: interested_classids.add(flag.section.id) uniquified_interested.append(flag) context['interested' ] = uniquified_interested if interested.count() == 0: context['iempty'] = True else: context['iempty'] = False return render_to_response(self.baseDir()+'view_lottery_prefs.html', request, context)
def students(self, QObject = False): Enrolled = Q(studentregistration__relationship__name='Enrolled') Par = Q(studentregistration__section__parent_class__parent_program=self.program) Unexpired = nest_Q(StudentRegistration.is_valid_qobject(), 'studentregistration') # Force Django to generate two subqueries without joining SRs to SSIs, # as efficiently as possible since it's still a big query. sr_ids = StudentRegistration.valid_objects().filter( section__parent_class__parent_program=self.program ).values('user').distinct() ssi_ids = StudentSubjectInterest.valid_objects().filter( subject__parent_program=self.program).values('user').distinct() any_reg_q = Q(id__in = sr_ids) | Q(id__in = ssi_ids) qobjects = { 'enrolled': Enrolled & Par & Unexpired, 'classreg': any_reg_q, } if QObject: return qobjects else: return {k: ESPUser.objects.filter(v).distinct() for k, v in qobjects.iteritems()}
def test_json_view(self): self.client.login(username='******', password='******') r = self.client.get('/onsite/' + self.program.url + '/unenroll_status') data = json.loads(r.content) self.assertGreaterEqual(len(data['student_timeslots']), 20) self.assertGreater(len(data['section_timeslots']), 0) self.assertGreater(len(data['enrollments']), 0) for student_id, ts_id in data['student_timeslots'].items(): self.assertTrue(self.timeslots.filter(id=ts_id).exists()) self.assertTrue(StudentRegistration.valid_objects().filter( user=student_id, section__meeting_times=ts_id).exists()) self.assertFalse( Record.objects.filter(event='attended', program=self.program, user=student_id).exists()) for section_id, ts_id in data['section_timeslots'].items(): self.assertTrue(self.timeslots.filter(id=ts_id).exists()) sec = ClassSection.objects.get(id=section_id) self.assertTrue(sec.meeting_times.filter(id=ts_id).exists()) self.assertTrue(StudentRegistration.valid_objects().filter( section=section_id).exists()) for sr_id, (user_id, section_id) in data['enrollments'].items(): sr = StudentRegistration.objects.get(id=sr_id) self.assertEqual(sr.user_id, user_id) self.assertEqual(sr.section_id, section_id) self.assertIn(str(user_id), data['student_timeslots']) self.assertIn(str(section_id), data['section_timeslots'])
def lottery_preferences_legacy(self, request, prog): # DEPRECATED: see comments in lottery_preferences method sections = list(prog.sections().values('id')) sections_interested = StudentRegistration.valid_objects().filter( relationship__name='Interested', user=request.user, section__parent_class__parent_program=prog).select_related( 'section__id').values_list('section__id', flat=True).distinct() sections_priority = StudentRegistration.valid_objects().filter( relationship__name='Priority/1', user=request.user, section__parent_class__parent_program=prog).select_related( 'section__id').values_list('section__id', flat=True).distinct() for item in sections: if item['id'] in sections_interested: item['lottery_interested'] = True else: item['lottery_interested'] = False if item['id'] in sections_priority: item['lottery_priority'] = True else: item['lottery_priority'] = False return {'sections': sections}
def test_json_view(self): self.client.login(username='******', password='******') r = self.client.get('/onsite/' + self.program.url + '/unenroll_status') data = json.loads(r.content) self.assertGreaterEqual(len(data['student_timeslots']), 20) self.assertGreater(len(data['section_timeslots']), 0) self.assertGreater(len(data['enrollments']), 0) for student_id, ts_id in data['student_timeslots'].items(): self.assertTrue(self.timeslots.filter(id=ts_id).exists()) self.assertTrue(StudentRegistration.valid_objects().filter( user=student_id, section__meeting_times=ts_id).exists()) self.assertFalse(Record.objects.filter( event='attended', program=self.program, user=student_id).exists()) for section_id, ts_id in data['section_timeslots'].items(): self.assertTrue(self.timeslots.filter(id=ts_id).exists()) sec = ClassSection.objects.get(id=section_id) self.assertTrue(sec.meeting_times.filter(id=ts_id).exists()) self.assertTrue(StudentRegistration.valid_objects().filter( section=section_id).exists()) for sr_id, (user_id, section_id) in data['enrollments'].items(): sr = StudentRegistration.objects.get(id=sr_id) self.assertEqual(sr.user_id, user_id) self.assertEqual(sr.section_id, section_id) self.assertIn(str(user_id), data['student_timeslots']) self.assertIn(str(section_id), data['section_timeslots'])
def students(self, QObject=False): Enrolled = Q(studentregistration__relationship__name='Enrolled') Par = Q(studentregistration__section__parent_class__parent_program=self .program) Unexpired = nest_Q(StudentRegistration.is_valid_qobject(), 'studentregistration') # Force Django to generate two subqueries without joining SRs to SSIs, # as efficiently as possible since it's still a big query. sr_ids = StudentRegistration.valid_objects().filter( section__parent_class__parent_program=self.program).values( 'user').distinct() ssi_ids = StudentSubjectInterest.valid_objects().filter( subject__parent_program=self.program).values('user').distinct() any_reg_q = Q(id__in=sr_ids) | Q(id__in=ssi_ids) qobjects = { 'enrolled': Enrolled & Par & Unexpired, 'classreg': any_reg_q, } if QObject: return qobjects else: return { k: ESPUser.objects.filter(v).distinct() for k, v in qobjects.iteritems() }
def viewlotteryprefs(self, request, tl, one, two, module, extra, prog): context = {} context['student'] = request.user context['program'] = prog priority_classids = set() uniquified_flags = [] priority_flags = StudentRegistration.valid_objects().filter(user=request.user, section__parent_class__parent_program=prog, relationship__name='Priority/1') for flag in priority_flags: if flag.section.id not in priority_classids: priority_classids.add(flag.section.id) uniquified_flags.append(flag) context['priority'] = uniquified_flags if priority_flags.count() == 0: context['pempty'] = True else: context['pempty'] = False interested_classids = set() uniquified_interested = [] interested = StudentRegistration.valid_objects().filter(user=request.user, section__parent_class__parent_program=prog, relationship__name='Interested') for flag in interested: if flag.section.id not in interested_classids: interested_classids.add(flag.section.id) uniquified_interested.append(flag) context['interested' ] = uniquified_interested if interested.count() == 0: context['iempty'] = True else: context['iempty'] = False return render_to_response(self.baseDir()+'view_lottery_prefs.html', request, (prog, tl), context)
def classchangerequest(self, request, tl, one, two, module, extra, prog): timeslots = prog.getTimeSlots() sections = prog.sections().filter(status=10, meeting_times__isnull=False).distinct() enrollments = {} for timeslot in timeslots: try: enrollments[timeslot] = ClassSubject.objects.get(nest_Q(StudentRegistration.is_valid_qobject(), 'sections__studentregistration'), sections__studentregistration__relationship__name="Enrolled", sections__studentregistration__user=request.user, sections__meeting_times=timeslot, parent_program=prog) except ClassSubject.DoesNotExist: enrollments[timeslot] = None context = {} context['timeslots'] = timeslots context['enrollments'] = enrollments context['user'] = request.user if 'success' in request.GET: context['success'] = True else: context['success'] = False if request.user.isStudent(): sections_by_slot = dict([(timeslot,[(section, 1 == StudentRegistration.valid_objects().filter(user=context['user'], section=section, relationship__name="Request").count()) for section in sections if section.get_meeting_times()[0] == timeslot and section.parent_class.grade_min <= request.user.getGrade(prog) <= section.parent_class.grade_max and section.parent_class not in enrollments.values() and ESPUser.getRankInClass(request.user, section.parent_class) in (5,10)]) for timeslot in timeslots]) else: sections_by_slot = dict([(timeslot,[(section, False) for section in sections if section.get_meeting_times()[0] == timeslot]) for timeslot in timeslots]) fields = {} for i, timeslot in enumerate(sections_by_slot.keys()): choices = [('0', "I'm happy with my current enrollment.")] initial = '0' for section in sections_by_slot[timeslot]: choices.append((section[0].emailcode(), section[0].emailcode()+": "+section[0].title())) if section[1]: initial = section[0].emailcode() fields['timeslot_'+str(i+1)] = forms.ChoiceField(label="Timeslot "+str(i+1)+" ("+timeslot.pretty_time()+")", choices=choices, initial=initial) form = type('ClassChangeRequestForm', (forms.Form,), fields) context['form'] = form() if request.method == "POST": old_requests = StudentRegistration.valid_objects().filter(user=context['user'], section__parent_class__parent_program=prog, relationship__name="Request") for r in old_requests: r.expire() form = form(request.POST) if form.is_valid(): for value in form.cleaned_data.values(): section = None for s in sections: if s.emailcode() == value: section = s break if not section: continue r = StudentRegistration.valid_objects().get_or_create(user=context['user'], section=section, relationship=RegistrationType.objects.get_or_create(name="Request", category="student")[0])[0] r.save() return HttpResponseRedirect(request.path.rstrip('/')+'/?success') else: return render_to_response(self.baseDir() + 'classchangerequest.html', request, context)
def __init__(self, program, **kwargs): """ Set constant parameters for class changes. """ assert isinstance(program,(Program,int)) self.program = program if isinstance(program,int): self.program = Program.objects.get(id=program) print self.program iscorrect = raw_input("Is this the correct program (y/[n])? ") assert (iscorrect.lower() == 'y' or iscorrect.lower() == 'yes') self.now = datetime.now() self.options = ClassChangeController.default_options.copy() self.options.update(kwargs) self.students_not_checked_in = [] self.deadline = self.now if 'deadline' in self.options.keys(): self.deadline = self.options['deadline'] self.Q_SR_NOW = nest_Q(StudentRegistration.is_valid_qobject(self.now), 'studentregistration') self.Q_SR_PROG = Q(studentregistration__section__parent_class__parent_program=self.program, studentregistration__section__meeting_times__isnull=False) & self.Q_SR_NOW self.Q_SR_REQ = Q(studentregistration__relationship__name="Request") & self.Q_SR_PROG self.Q_NOW = StudentRegistration.is_valid_qobject(self.now) self.Q_PROG = Q(section__parent_class__parent_program=self.program, section__meeting_times__isnull=False) & self.Q_NOW self.Q_REQ = Q(relationship__name="Request") & self.Q_PROG self.students = ESPUser.objects.filter(self.Q_SR_REQ).order_by('id').distinct() if 'students_not_checked_in' in self.options.keys() and isinstance(self.options['students_not_checked_in'],QuerySet): self.students_not_checked_in = list(self.options['students_not_checked_in'].values_list('id',flat=True).distinct()) else: self.students_not_checked_in = list(self.students.exclude(id__in=self.program.students()['attended']).values_list('id',flat=True).distinct()) self.priority_limit = self.program.priorityLimit() self._init_Q_objects() self.sections = self.program.sections().filter(status__gt=0, parent_class__status__gt=0, meeting_times__isnull=False).order_by('id').select_related('parent_class','parent_class__parent_program').distinct() if not self.options['use_closed_classes']: self.sections = self.sections.filter(registration_status=0).distinct() self.timeslots = self.program.getTimeSlots().order_by('id').distinct() self.num_timeslots = len(self.timeslots) self.num_students = len(self.students) self.num_sections = len(self.sections) numpy.random.seed(self.now.microsecond) self.initialize() if self.options['stats_display']: logger.info('Initialized lottery assignment for %d students, %d sections, %d timeslots', self.num_students, self.num_sections, self.num_timeslots)
def __init__(self, program, **kwargs): """ Set constant parameters for class changes. """ assert isinstance(program,(Program,int)) self.program = program if isinstance(program,int): self.program = Program.objects.get(id=program) print self.program iscorrect = raw_input("Is this the correct program (y/[n])? ") assert (iscorrect.lower() == 'y' or iscorrect.lower() == 'yes') self.now = datetime.now() self.options = ClassChangeController.default_options.copy() self.options.update(kwargs) self.students_not_checked_in = [] self.deadline = self.now if 'deadline' in self.options.keys(): self.deadline = self.options['deadline'] self.Q_SR_NOW = nest_Q(StudentRegistration.is_valid_qobject(self.now), 'studentregistration') self.Q_SR_PROG = Q(studentregistration__section__parent_class__parent_program=self.program, studentregistration__section__meeting_times__isnull=False) & self.Q_SR_NOW self.Q_SR_REQ = Q(studentregistration__relationship__name="Request") & self.Q_SR_PROG self.Q_NOW = StudentRegistration.is_valid_qobject(self.now) self.Q_PROG = Q(section__parent_class__parent_program=self.program, section__meeting_times__isnull=False) & self.Q_NOW self.Q_REQ = Q(relationship__name="Request") & self.Q_PROG self.students = ESPUser.objects.filter(self.Q_SR_REQ).order_by('id').distinct() if 'students_not_checked_in' in self.options.keys() and isinstance(self.options['students_not_checked_in'],QuerySet): self.students_not_checked_in = list(self.options['students_not_checked_in'].values_list('id',flat=True).distinct()) else: self.students_not_checked_in = list(self.students.exclude(id__in=self.program.students()['attended']).values_list('id',flat=True).distinct()) self.priority_limit = self.program.priorityLimit() self._init_Q_objects() self.sections = self.program.sections().filter(status__gt=0, parent_class__status__gt=0, meeting_times__isnull=False).order_by('id').select_related('parent_class','parent_class__parent_program','meeting_times').distinct() if not self.options['use_closed_classes']: self.sections = self.sections.filter(registration_status=0).distinct() self.timeslots = self.program.getTimeSlots().order_by('id').distinct() self.num_timeslots = len(self.timeslots) self.num_students = len(self.students) self.num_sections = len(self.sections) numpy.random.seed(self.now.microsecond) self.initialize() if self.options['stats_display']: logger.info('Initialized lottery assignment for %d students, %d sections, %d timeslots', self.num_students, self.num_sections, self.num_timeslots)
def lottery_preferences_legacy(self, request, prog): # DEPRECATED: see comments in lottery_preferences method sections = list(prog.sections().values('id')) sections_interested = StudentRegistration.valid_objects().filter(relationship__name='Interested', user=request.user, section__parent_class__parent_program=prog).select_related('section__id').values_list('section__id', flat=True).distinct() sections_priority = StudentRegistration.valid_objects().filter(relationship__name='Priority/1', user=request.user, section__parent_class__parent_program=prog).select_related('section__id').values_list('section__id', flat=True).distinct() for item in sections: if item['id'] in sections_interested: item['lottery_interested'] = True else: item['lottery_interested'] = False if item['id'] in sections_priority: item['lottery_priority'] = True else: item['lottery_priority'] = False return {'sections': sections}
def save_assignments(self, debug_display=False, try_mailman=False): """ Store lottery assignments in the database once they have been computed. This is a fairly time consuming step compared to computing the assignments. """ self.clear_saved_assignments() assignments = numpy.nonzero(self.student_sections) student_ids = self.student_ids[assignments[0]] section_ids = self.section_ids[assignments[1]] assert (student_ids.shape == section_ids.shape) relationship, created = RegistrationType.objects.get_or_create( name='Enrolled') self.now = datetime.now( ) # The time that all the registrations start at, in case all lottery registrations need to be manually reverted later StudentRegistration.objects.bulk_create([ StudentRegistration(user_id=student_ids[i], section_id=section_ids[i], relationship=relationship, start_date=self.now) for i in range(student_ids.shape[0]) ]) print "StudentRegistration enrollments all created to start at %s" % self.now if debug_display: print 'Created %d registrations' % student_ids.shape[0] #As mailman doesn't work, disable for now. if try_mailman: self.update_mailman_lists()
def getSchedule(program, student): schedule = """ Student schedule for %s: Time | Class | Room""" % student.name() regs = StudentRegistration.valid_objects().filter( user=student, section__parent_class__parent_program=program, relationship__name='Accepted') classes = [x.section.parent_class for x in regs] # now we sort them by time/title classes.sort() for cls in classes: rooms = cls.prettyrooms() if len(rooms) == 0: rooms = 'N/A' else: rooms = ", ".join(rooms) schedule += """ %s|%s|%s""" % (",".join( cls.friendly_times()).ljust(20), cls.title.ljust(25), rooms) return schedule
def num_users_enrolled(self, prog): # Querying for SRs and then extracting the users saves us joining the # users table. return StudentRegistration.valid_objects().filter( section__parent_class__parent_program=prog, relationship__name='Enrolled' ).values_list('user').distinct().count()
def num_users_enrolled(self, prog): # Querying for SRs and then extracting the users saves us joining the # users table. return StudentRegistration.valid_objects().filter( section__parent_class__parent_program=prog, relationship__name='Enrolled').values_list( 'user').distinct().count()
def getSchedule(program, student): schedule = u""" Student schedule for %s: Time | Class | Room""" % student.name() regs = StudentRegistration.valid_objects().filter(user=student, section__parent_class__parent_program=program, relationship__name='Accepted') classes = [x.section.parent_class for x in regs] # now we sort them by time/title classes.sort() for cls in classes: rooms = cls.prettyrooms() if len(rooms) == 0: rooms = u'N/A' else: rooms = u", ".join(rooms) schedule += u""" %s|%s|%s""" % (u",".join(cls.friendly_times()).ljust(20), cls.title.ljust(25), rooms) return schedule
def clear_saved_assignments(self, delete=False): """ Expire/delete all previous StudentRegistration enrollments associated with the program. """ old_registrations = StudentRegistration.objects.filter(section__parent_class__parent_program=self.program, relationship__name='Enrolled') if delete: old_registrations.delete() else: old_registrations.filter(StudentRegistration.is_valid_qobject()).update(end_date=datetime.now())
def students(self, QObject=False): q = Q(studentregistration__section__parent_class__parent_program=self. program) & nest_Q(StudentRegistration.is_valid_qobject(), 'studentregistration') if QObject: return {'lotteried_students': q} else: return {'lotteried_students': ESPUser.objects.filter(q).distinct()}
def lsr_submit_HSSP(request, program, priority_limit, data): # temporary function. will merge the two later -jmoldow 05/31 classes_flagged = [set() for i in range(0,priority_limit+1)] # 1-indexed sections_by_block = [defaultdict(set) for i in range(0,priority_limit+1)] # 1-indexed - sections_by_block[i][block] is a set of classes that were given priority i in timeblock block. This should hopefully be a set of size 0 or 1. for section_id, (priority, block_id) in data.iteritems(): section_id = int(section_id) priority = int(priority) block_id = int(block_id) classes_flagged[0].add(section_id) classes_flagged[priority].add(section_id) sections_by_block[priority][block_id].add(section_id) errors = [] for i in range(1, priority_limit+1): for block in sections_by_block[i].keys(): if len(sections_by_block[i][block]) > 1: errors.append({"text": "Can't flag two classes with the same priority at the same time!", "cls_sections": list(sections_by_block[i][block]), "block": block, "priority": i, "doubled_priority": True}) if len(errors): return HttpResponse(json.dumps(errors), mimetype='application/json') reg_priority = [(None,None)] + [RegistrationType.objects.get_or_create(name="Priority/"+str(i), category="student") for i in range(1,priority_limit+1)] reg_priority = [reg_priority[i][0] for i in range(0, priority_limit+1)] allStudentRegistrations = StudentRegistration.valid_objects().filter(section__parent_class__parent_program=program, user=request.user) oldRegistrations = [] #[[] for i in range(0, priority_limit+1)] # 1-indexed for priority registrations, the 0-index is for interested registrations for i in range(1, priority_limit+1): oldRegistrations += [(oldRegistration, i) for oldRegistration in list(allStudentRegistrations.filter(relationship=reg_priority[i]).select_related(depth=3))] for (oldRegistration, priority) in oldRegistrations: if oldRegistration.section.id not in classes_flagged[0]: oldRegistration.expire() continue for i in range(1, priority_limit + 1): if oldRegistration.section.id in classes_flagged[i]: if i != priority: oldRegistration.expire() elif i == priority: classes_flagged[i].remove(oldRegistration.section.id) classes_flagged[0].remove(oldRegistration.section.id) break flagworthy_sections = [None] + [ClassSection.objects.filter(id__in=classes_flagged[i]).select_related(depth=2).annotate(first_block=Min('meeting_times__start')) for i in range(1, priority_limit + 1)] for i in range(1, priority_limit + 1): for s in list(flagworthy_sections[i]): if not s.preregister_student(request.user, prereg_verb=reg_priority[i].name, overridefull=True): errors.append({"text": "Unable to add flagged class", "cls_sections": [s.id], "emailcode": s.emailcode(), "block": s.first_block, "flagged": True, "priority": i, "doubled_priority": False}) if len(errors) != 0: s = StringIO() pprint(errors, s) mail_admins('Error in class reg', s.getvalue(), fail_silently=True) return HttpResponse(json.dumps(errors), mimetype='application/json')
def lsr_submit_HSSP(request, program, priority_limit, data): # temporary function. will merge the two later -jmoldow 05/31 classes_flagged = [set() for i in range(0,priority_limit+1)] # 1-indexed sections_by_block = [defaultdict(set) for i in range(0,priority_limit+1)] # 1-indexed - sections_by_block[i][block] is a set of classes that were given priority i in timeblock block. This should hopefully be a set of size 0 or 1. for section_id, (priority, block_id) in data.iteritems(): section_id = int(section_id) priority = int(priority) block_id = int(block_id) classes_flagged[0].add(section_id) classes_flagged[priority].add(section_id) sections_by_block[priority][block_id].add(section_id) errors = [] for i in range(1, priority_limit+1): for block in sections_by_block[i].keys(): if len(sections_by_block[i][block]) > 1: errors.append({"text": "Can't flag two classes with the same priority at the same time!", "cls_sections": list(sections_by_block[i][block]), "block": block, "priority": i, "doubled_priority": True}) if len(errors): return HttpResponse(json.dumps(errors), content_type='application/json') reg_priority = [(None,None)] + [RegistrationType.objects.get_or_create(name="Priority/"+str(i), category="student") for i in range(1,priority_limit+1)] reg_priority = [reg_priority[i][0] for i in range(0, priority_limit+1)] allStudentRegistrations = StudentRegistration.valid_objects().filter(section__parent_class__parent_program=program, user=request.user) oldRegistrations = [] #[[] for i in range(0, priority_limit+1)] # 1-indexed for priority registrations, the 0-index is for interested registrations for i in range(1, priority_limit+1): oldRegistrations += [(oldRegistration, i) for oldRegistration in list(allStudentRegistrations.filter(relationship=reg_priority[i]))] for (oldRegistration, priority) in oldRegistrations: if oldRegistration.section.id not in classes_flagged[0]: oldRegistration.expire() continue for i in range(1, priority_limit + 1): if oldRegistration.section.id in classes_flagged[i]: if i != priority: oldRegistration.expire() elif i == priority: classes_flagged[i].remove(oldRegistration.section.id) classes_flagged[0].remove(oldRegistration.section.id) break flagworthy_sections = [None] + [ClassSection.objects.filter(id__in=classes_flagged[i]).annotate(first_block=Min('meeting_times__start')) for i in range(1, priority_limit + 1)] for i in range(1, priority_limit + 1): for s in list(flagworthy_sections[i]): if not s.preregister_student(request.user, prereg_verb=reg_priority[i].name, overridefull=True): errors.append({"text": "Unable to add flagged class", "cls_sections": [s.id], "emailcode": s.emailcode(), "block": s.first_block, "flagged": True, "priority": i, "doubled_priority": False}) if len(errors) != 0: s = StringIO() pprint(errors, s) mail_admins('Error in class reg', s.getvalue(), fail_silently=True) return HttpResponse(json.dumps(errors), content_type='application/json')
def enrollment_status(self, request, tl, one, two, module, extra, prog): resp = HttpResponse(mimetype='application/json') data = StudentRegistration.valid_objects().filter( section__status__gt=0, section__parent_class__status__gt=0, section__parent_class__parent_program=prog, relationship__name='Enrolled').values_list('user__id', 'section__id') simplejson.dump(list(data), resp) return resp
def load_data(self): lunch_registrations = list(StudentRegistration.valid_objects().filter( user=self.user, section__parent_class__category__category='Lunch', section__parent_class__parent_program=self.program)) if len(lunch_registrations) > 0: section = lunch_registrations[0].section if len(section.get_meeting_times()) > 0: print 'Set initial to %s' % section.get_meeting_times()[0] self.initial['timeslot'] = section.get_meeting_times()[0].id
def lottery_preferences_usepriority(self, request, prog): sections = list(prog.sections().values('id')) for i in range(1, prog.priorityLimit()+1, 1): priority_name = 'Priority/' + str(i) sections_priority = StudentRegistration.valid_objects().filter(relationship__name=priority_name, user=request.user, section__parent_class__parent_program=prog).select_related('section__id').values_list('section__id', flat=True).distinct() for item in sections: if item['id'] in sections_priority: item[priority_name] = True #else: # item['lottery_priority'] = False return {'sections': sections}
def unenroll_status(prog): """ Assemble the data necessary to compute the set of enrollments to expire for a given combination of student first class times and section start times. Returns: enrollments: { enrollment id -> (user id, section id) } student_timeslots: { user id -> event id of first class timeslot } section_timeslots: { section id -> event id of first timeslot } """ enrolled = RegistrationType.objects.get(name='Enrolled') sections = prog.sections().filter( status__gt=0, parent_class__status__gt=0) sections = sections.exclude( parent_class__category__category='Lunch') enrollments = StudentRegistration.valid_objects().filter( relationship=enrolled, section__in=sections) # students not checked in students = ESPUser.objects.filter( id__in=enrollments.values('user')) students = students.exclude( record__program=prog, record__event='attended') # enrollments for those students relevant = enrollments.filter(user__in=students).values_list( 'id', 'user', 'section', 'section__meeting_times') relevant = relevant.order_by('section__meeting_times__start') section_timeslots = {} # section -> starting timeslot id student_timeslots = {} # student -> starting timeslot id enrollments = {} # id -> (student, section) for id, student, section, ts in relevant: if ts is None: continue if section not in section_timeslots: section_timeslots[section] = ts if student not in student_timeslots: student_timeslots[student] = ts enrollments[id] = (student, section) return { 'section_timeslots': section_timeslots, 'student_timeslots': student_timeslots, 'enrollments': enrollments }
def review_students(self, request, tl, one, two, module, extra, prog): """ Show a roster of the students in the class, allowing the administrators to accept students into the program based on the teachers' reviews and the students' applications. """ try: cls = ClassSubject.objects.get(id=extra) except ClassSubject.DoesNotExist: raise ESPError(False), 'Cannot find class.' if not request.user.canEdit(cls): raise ESPError(False), 'You cannot edit class "%s"' % cls # Fetch any student even remotely related to the class. students_dict = cls.students_dict() students = [] for key in students_dict: students += students_dict[key] students = filter( lambda x: x.studentapplication_set.filter(program=self.program). count() > 0, students) for student in students: student.added_class = student.studentregistration_set.filter( section__parent_class=cls)[0].start_date try: student.app = student.studentapplication_set.get( program=self.program) except: student.app = None if student.app: reviews = student.app.reviews.all() else: reviews = [] if StudentRegistration.valid_objects().filter( user=student, section__parent_class=cls, relationship__name='Accepted').count() > 0: student.status = 'Accepted' else: student.status = 'Not accepted' students = list(students) students.sort(key=lambda x: x.last_name) return render_to_response(self.baseDir() + 'roster.html', request, { 'class': cls, 'students': students, 'program': prog })
def num_users_with_lottery(self, prog): # Past empirical observation has shown that doing the union in SQL is # much, much slower for unknown reasons; it also means we would have to # query over the users table, so this saves us joining that table. users_with_ssis = set(StudentSubjectInterest.valid_objects().filter( subject__parent_program=prog).values_list('user').distinct()) users_with_srs = set(StudentRegistration.valid_objects().filter( Q(relationship__name='Interested') | Q(relationship__name__contains='Priority/'), section__parent_class__parent_program=prog).values_list( 'user').distinct()) return len(users_with_ssis | users_with_srs)
def get_schedule_json(self, request, tl, one, two, module, extra, prog): resp = HttpResponse(content_type='application/json') result = {'user': None, 'user_grade': 0, 'sections': [], 'messages': []} try: result['user'] = int(request.GET['user']) except: result['messages'].append('Error: no user specified.') if result['user']: result['user_grade'] = ESPUser.objects.get(id=result['user']).getGrade(program=prog) result['sections'] = list(ClassSection.objects.filter(nest_Q(StudentRegistration.is_valid_qobject(), 'studentregistration'), status__gt=0, parent_class__status__gt=0, parent_class__parent_program=prog, studentregistration__relationship__name='Enrolled', studentregistration__user__id=result['user']).values_list('id', flat=True).distinct()) json.dump(result, resp) return resp
def get_schedule_json(self, request, tl, one, two, module, extra, prog): resp = HttpResponse(content_type='application/json') result = {'user': None, 'user_grade': 0, 'sections': [], 'messages': []} try: result['user'] = int(request.GET['user']) except: result['messages'].append('Error: no user specified.') if result['user']: result['user_grade'] = ESPUser.objects.get(id=result['user']).getGrade(program=prog) result['sections'] = list(ClassSection.objects.filter(nest_Q(StudentRegistration.is_valid_qobject(), 'studentregistration'), status__gt=0, parent_class__status__gt=0, parent_class__parent_program=prog, studentregistration__relationship__name='Enrolled', studentregistration__user__id=result['user']).values_list('id', flat=True).distinct()) json.dump(result, resp) return resp
def catalog_registered_classes_json(self, request, tl, one, two, module, extra, prog, timeslot=None): reg_bits = StudentRegistration.valid_objects().filter(user=request.user, section__parent_class__parent_program=prog).select_related() reg_bits_data = [ { 'user': b.user.username, 'section_id': b.section_id, 'type': b.relationship.name } for b in reg_bits ] resp = HttpResponse(content_type='application/json') json.dump(reg_bits_data, resp) return resp
def test_submit(self): self.client.login(username='******', password='******') r = self.client.get('/onsite/' + self.program.url + '/unenroll_status') data = json.loads(r.content) enrollment_ids = data['enrollments'].keys() r = self.client.post('/onsite/' + self.program.url + '/unenroll_students', {'selected_enrollments': ','.join(enrollment_ids)}) self.assertContains(r, 'Expired %d student registrations' % len(enrollment_ids)) self.assertContains(r, ', '.join(enrollment_ids)) enrollments = StudentRegistration.objects.filter(id__in=enrollment_ids) self.assertFalse(enrollments.filter(StudentRegistration.is_valid_qobject()).exists()) enrollments.update(end_date=None)
def load_data(self): lunch_registrations = StudentRegistration.valid_objects().filter( user=self.user, section__parent_class__category__category='Lunch', section__parent_class__parent_program=self.program).select_related( 'section').prefetch_related('section__meeting_times') lunch_registrations = [ lunch_registration for lunch_registration in lunch_registrations if list(lunch_registration.section.meeting_times.all()) [0].start.day == self.day.day ] if len(lunch_registrations) > 0: section = lunch_registrations[0].section if len(section.get_meeting_times()) > 0: self.initial['timeslot'] = section.get_meeting_times()[0].id
def num_users_with_lottery(self, prog): # Past empirical observation has shown that doing the union in SQL is # much, much slower for unknown reasons; it also means we would have to # query over the users table, so this saves us joining that table. users_with_ssis = set( StudentSubjectInterest.valid_objects() .filter(subject__parent_program=prog) .values_list('user').distinct()) users_with_srs = set( StudentRegistration.valid_objects() .filter( Q(relationship__name='Interested') | Q(relationship__name__contains='Priority/'), section__parent_class__parent_program=prog) .values_list('user').distinct()) return len(users_with_ssis | users_with_srs)
def lottery_preferences_usepriority(self, request, prog): sections = list(prog.sections().values('id')) for i in range(1, prog.priorityLimit() + 1, 1): priority_name = 'Priority/' + str(i) sections_priority = StudentRegistration.valid_objects().filter( relationship__name=priority_name, user=request.user, section__parent_class__parent_program=prog).select_related( 'section__id').values_list('section__id', flat=True).distinct() for item in sections: if item['id'] in sections_priority: item[priority_name] = True #else: # item['lottery_priority'] = False return {'sections': sections}
def review_students(self, request, tl, one, two, module, extra, prog): """ Show a roster of the students in the class, allowing the administrators to accept students into the program based on the teachers' reviews and the students' applications. """ try: cls = ClassSubject.objects.get(id = extra) except ClassSubject.DoesNotExist: raise ESPError('Cannot find class.', log=False) if not request.user.canEdit(cls): raise ESPError('You cannot edit class "%s"' % cls, log=False) # Fetch any student even remotely related to the class. students_dict = cls.students_dict() students = [] for key in students_dict: students += students_dict[key] students = filter(lambda x: x.studentapplication_set.filter(program=self.program).count() > 0, students) for student in students: student.added_class = student.studentregistration_set.filter(section__parent_class=cls)[0].start_date try: student.app = student.studentapplication_set.get(program = self.program) except: student.app = None if student.app: reviews = student.app.reviews.all() else: reviews = [] if StudentRegistration.valid_objects().filter(user=student, section__parent_class=cls, relationship__name='Accepted').count() > 0: student.status = 'Accepted' else: student.status = 'Not accepted' students = list(students) students.sort(key=lambda x: x.last_name) return render_to_response(self.baseDir()+'roster.html', request, {'class': cls, 'students':students, 'program': prog})
def test_submit(self): self.client.login(username='******', password='******') r = self.client.get('/onsite/' + self.program.url + '/unenroll_status') data = json.loads(r.content) enrollment_ids = data['enrollments'].keys() r = self.client.post( '/onsite/' + self.program.url + '/unenroll_students', {'selected_enrollments': ','.join(enrollment_ids)}) self.assertContains( r, 'Expired %d student registrations' % len(enrollment_ids)) self.assertContains(r, ', '.join(enrollment_ids)) enrollments = StudentRegistration.objects.filter(id__in=enrollment_ids) self.assertFalse( enrollments.filter( StudentRegistration.is_valid_qobject()).exists()) enrollments.update(end_date=None)
def students(self, QObject=False): q_sr = Q( studentregistration__section__parent_class__parent_program=self. program) & nest_Q(StudentRegistration.is_valid_qobject(), 'studentregistration') q_ssi = Q(studentsubjectinterest__subject__parent_program=self.program ) & nest_Q(StudentSubjectInterest.is_valid_qobject(), 'studentsubjectinterest') if QObject: return { 'twophase_star_students': q_ssi, 'twophase_priority_students': q_sr } else: return { 'twophase_star_students': ESPUser.objects.filter(q_ssi).distinct(), 'twophase_priority_students': ESPUser.objects.filter(q_sr).distinct() }
def catalog_registered_classes_json(self, request, tl, one, two, module, extra, prog, timeslot=None): reg_bits = StudentRegistration.valid_objects().filter( user=request.user, section__parent_class__parent_program=prog).select_related() reg_bits_data = [{ 'user': b.user.username, 'section_id': b.section_id, 'type': b.relationship.name } for b in reg_bits] resp = HttpResponse(content_type='application/json') json.dump(reg_bits_data, resp) return resp
def save_assignments(self, try_mailman=True): """ Store lottery assignments in the database once they have been computed. This is a fairly time consuming step compared to computing the assignments. """ self.clear_saved_assignments() assignments = numpy.nonzero(self.student_sections) student_ids = self.student_ids[assignments[0]] section_ids = self.section_ids[assignments[1]] assert(student_ids.shape == section_ids.shape) relationship, created = RegistrationType.objects.get_or_create(name='Enrolled') self.now = datetime.now() # The time that all the registrations start at, in case all lottery registrations need to be manually reverted later StudentRegistration.objects.bulk_create([StudentRegistration(user_id=student_ids[i], section_id=section_ids[i], relationship=relationship, start_date=self.now) for i in range(student_ids.shape[0])]) if self.options['stats_display']: logger.info("StudentRegistration enrollments all created to start at %s", self.now) logger.info('Created %d registrations', student_ids.shape[0]) #As mailman has sometimes not worked in the past, #leave the option to disable. if try_mailman: self.update_mailman_lists()
def students(self, QObject=False): from django.db.models import Q Enrolled = Q(studentregistration__relationship__name='Enrolled') Par = Q(studentregistration__section__parent_class__parent_program=self .program) Unexpired = nest_Q(StudentRegistration.is_valid_qobject(), 'studentregistration') if QObject: retVal = { 'enrolled': self.getQForUser(Enrolled & Par & Unexpired), 'classreg': self.getQForUser(Par & Unexpired) } else: retVal = { 'enrolled': ESPUser.objects.filter(Enrolled & Par & Unexpired).distinct(), 'classreg': ESPUser.objects.filter(Par & Unexpired).distinct() } allowed_student_types = Tag.getTag("allowed_student_types", target=self.program) if allowed_student_types: allowed_student_types = allowed_student_types.split(",") for stutype in allowed_student_types: GroupName = Q(groups__name=stutype) if QObject: retVal[stutype] = self.getQForUser(Par & Unexpired & Reg & VerbName & VerbParent) else: retVal[stutype] = ESPUser.objects.filter( Par & Unexpired & Reg & GroupName).distinct() return retVal
def review_students(self, request, tl, one, two, module, extra, prog): try: cls = ClassSubject.objects.get(id = extra) except ClassSubject.DoesNotExist: raise ESPError('Cannot find class.', log=False) if not request.user.canEdit(cls): raise ESPError('You cannot edit class "%s"' % cls, log=False) # Fetch any student even remotely related to the class. students_dict = cls.students_dict() students = [] for key in students_dict: students += students_dict[key] for student in students: now = datetime.now() student.added_class = StudentRegistration.valid_objects().filter(section__parent_class = cls, user = student)[0].start_date try: student.app = student.studentapplication_set.get(program = self.program) except: student.app = None if student.app: reviews = student.app.reviews.all().filter(reviewer=request.user, score__isnull=False) questions = student.app.questions.all().filter(subject=cls) else: reviews = [] questions = [] if len(reviews) > 0: student.app_reviewed = reviews[0] else: student.app_reviewed = None student.app_completed = False for i in questions: for j in i.studentappresponse_set.all(): if j.complete: student.app_completed = True students = list(students) students.sort(lambda x,y: cmp(x.added_class,y.added_class)) if 'prev' in request.GET: prev_id = int(request.GET.get('prev')) prev = students[0] current = None for current in students[1:]: if prev.id == prev_id and current.app_completed: from django.shortcuts import redirect url = "/%s/%s/%s/review_student/%s/?student=%s" % (tl, one, two, extra, current.id) return redirect(url) if prev.id != prev_id: prev = current return render_to_response(self.baseDir()+'roster.html', request, {'class': cls, 'students':students})
def review_student(self, request, tl, one, two, module, extra, prog): scrmi = prog.studentclassregmoduleinfo reg_nodes = scrmi.reg_verbs() try: cls = ClassSubject.objects.get(id = extra) except ClassSubject.DoesNotExist: raise ESPError('Cannot find class.', log=False) if not request.user.canEdit(cls): raise ESPError('You cannot edit class "%s"' % cls, log=False) student = request.GET.get('student',None) if not student: student = request.POST.get('student','') try: student = ESPUser.objects.get(id = int(student)) except ESPUser.DoesNotExist: raise ESPError('Cannot find student, %s' % student, log=False) not_registered = not StudentRegistration.valid_objects().filter(section__parent_class = cls, user = student).exists() if not_registered: raise ESPError('Student not a student of this class.', log=False) try: student.app = student.studentapplication_set.get(program = self.program) except: student.app = None raise ESPError('Error: Student did not start an application.', log=False) student.added_class = StudentRegistration.valid_objects().filter(section__parent_class = cls, user = student)[0].start_date teacher_reviews = student.app.reviews.all().filter(reviewer=request.user) if teacher_reviews.count() > 0: this_review = teacher_reviews.order_by('id')[0] else: this_review = StudentAppReview(reviewer=request.user) this_review.save() student.app.reviews.add(this_review) if request.method == 'POST': form = this_review.get_form(request.POST) if form.is_valid(): form.target.update(form) if 'submit_next' in request.POST or 'submit_return' in request.POST: url = '/%s/%s/%s/review_students/%s/' % (tl, one, two, extra) if 'submit_next' in request.POST: url += '?prev=%s' % student.id from django.shortcuts import redirect return redirect(url) # self.review_students(request, tl, one, two, module, extra, prog) else: form = this_review.get_form() return render_to_response(self.baseDir()+'review.html', request, {'class': cls, 'reviews': teacher_reviews, 'program': prog, 'student':student, 'form': form})
def select_students(self, request, tl, one, two, module, extra, prog): # Get preregistered and enrolled students try: sec = ClassSection.objects.get(id=extra) except (ValueError, ClassSection.DoesNotExist): raise ESPError( 'Class section not found. If you came from a link on our site, please notify the webmasters.', log=False) students_list = sec.students_prereg() if request.method == 'POST': # Handle form submission # result_strs = [] data = request.POST.copy() sections_dict = {} for key in data: key_dir = key.split('_') if key_dir[0] == 'regstatus' and len(key_dir) == 3: student_id = int(key_dir[1]) sec_id = int(key_dir[2]) if sec_id not in sections_dict: sections_dict[sec_id] = [{ 'id': student_id, 'status': data[key] }] else: sections_dict[sec_id].append({ 'id': student_id, 'status': data[key] }) for sec_id in sections_dict: sec = ClassSection.objects.get(id=sec_id) sec.cache['students'] = None sec.cache['num_students'] = None for item in sections_dict[sec_id]: student = ESPUser.objects.get(id=item['id']) ignore = False value = item['status'] if value == 'enroll': verb_name = 'Enrolled' elif value == 'reject': verb_name = 'Rejected' else: ignore = True if not ignore: rel = RegistrationType.get_map( include=['Enrolled', 'Rejected'], category='student')[verb_name] other_regs = sec.getRegistrations(student).filter( relationship__name__in=['Enrolled', 'Rejected']) found = False for reg in other_regs: if not found and reg.relationship == rel: found = True else: reg.expire() if not found: new_reg = StudentRegistration(user=student, relationship=rel, section=sec) new_reg.save() # Jazz up this information a little #Not having much luck with query count/performance when selecting related parent_class and parent_class__category #Creating a lookup dict instead to strip out duplicate ClassSubject instances student_regs = StudentRegistration.valid_objects().filter(user__in=students_list) \ .order_by('start_date').select_related('section','user','relationship') student_regs = student_regs.filter( section__parent_class__parent_program=self.program) student_sections_dict = defaultdict(set) student_reg_dict = defaultdict(set) #need a unique set of parent_class ids #creating lookup dicts to avoid hitting database(was not solved with #select_related or prefecth_related parent_class_id_set = set() sections = set() for reg in student_regs: student_sections_dict[reg.user].add(reg.section) display_name = reg.relationship.displayName or reg.relationship.name sections.add(reg.section) parent_class_id_set.add(reg.section.parent_class_id) student_reg_dict['%i_%i' % ( reg.user.id, reg.section.id, )].add(display_name) subjects = ClassSubject.objects.filter( id__in=parent_class_id_set).select_related('category') subject_categories_dict = dict([(s.id, (s, s.category)) for s in subjects]) for student in students_list: student.bits = student_reg_dict['%i_%i' % ( student.id, sec.id, )] #this is a bit of a problem because it produces the side affect of application #creation if not found student.app = student.getApplication(self.program, False) student.other_classes = [] for section in student_sections_dict[student]: parent_class, category = subject_categories_dict.get( section.parent_class_id) regtypes = student_reg_dict['%i_%i' % ( student.id, section.id, )] section_row = (section, regtypes, parent_class, category) student.other_classes.append(section_row) preregs = sec.getRegistrations(student).exclude( relationship__name__in=['Enrolled', 'Rejected']) if preregs.count() != 0: student.added_class = preregs[0].start_date if 'Enrolled' in student.bits: student.enrolled = True elif 'Rejected' in student.bits: student.rejected = True # Detect if there is an application module from esp.program.modules.handlers.studentjunctionappmodule import StudentJunctionAppModule has_app_module = False for module in prog.getModules(): if isinstance(module, StudentJunctionAppModule): has_app_module = True return render_to_response( self.baseDir() + 'select_students.html', request, { 'has_app_module': has_app_module, 'prog': prog, 'sec': sec, 'students_list': students_list })
def select_students(self, request, tl, one, two, module, extra, prog): # Get preregistered and enrolled students try: sec = ClassSection.objects.filter(id=extra)[0] except: raise ESPError( False ), 'Class section not found. If you came from a link on our site, please notify the webmasters.' students_list = sec.students_prereg() if request.method == 'POST': # Handle form submission # result_strs = [] data = request.POST.copy() sections_dict = {} for key in data: key_dir = key.split('_') if key_dir[0] == 'regstatus' and len(key_dir) == 3: student_id = int(key_dir[1]) sec_id = int(key_dir[2]) if sec_id not in sections_dict: sections_dict[sec_id] = [{ 'id': student_id, 'status': data[key] }] else: sections_dict[sec_id].append({ 'id': student_id, 'status': data[key] }) for sec_id in sections_dict: sec = ClassSection.objects.get(id=sec_id) sec.cache['students'] = None sec.cache['num_students'] = None for item in sections_dict[sec_id]: student = ESPUser(User.objects.get(id=item['id'])) ignore = False value = item['status'] if value == 'enroll': verb_name = 'Enrolled' elif value == 'reject': verb_name = 'Rejected' else: ignore = True if not ignore: rel = RegistrationType.get_map( include=['Enrolled', 'Rejected'], category='student')[verb_name] other_regs = sec.getRegistrations(student).filter( relationship__name__in=['Enrolled', 'Rejected']) found = False for reg in other_regs: if not found and reg.relationship == rel: found = True else: reg.expire() # result_strs.append('Expired: %s' % bit) if not found: new_reg = StudentRegistration(user=student, relationship=rel, section=sec) new_reg.save() # Jazz up this information a little for student in students_list: student.bits = sec.getRegVerbs(student) student.app = student.getApplication(self.program, False) student.other_classes = [ (sec2, sec2.getRegVerbs(student)) for sec2 in student.getSections(self.program).exclude( id=sec.id) ] preregs = sec.getRegistrations(student).exclude( relationship__name__in=['Enrolled', 'Rejected']) if preregs.count() != 0: student.added_class = preregs[0].start_date if 'Enrolled' in student.bits: student.enrolled = True elif 'Rejected' in student.bits: student.rejected = True # Detect if there is an application module from esp.program.modules.handlers.studentjunctionappmodule import StudentJunctionAppModule has_app_module = False for module in prog.getModules(): if isinstance(module, StudentJunctionAppModule): has_app_module = True return render_to_response( self.baseDir() + 'select_students.html', request, { 'has_app_module': has_app_module, 'prog': prog, 'sec': sec, 'students_list': students_list })
def num_prefs(self, prog): num_srs = StudentRegistration.valid_objects().filter( section__parent_class__parent_program=prog).count() return num_srs + self.num_ssis(prog)
def students(self, QObject = False): q = Q(studentregistration__section__parent_class__parent_program=self.program) & nest_Q(StudentRegistration.is_valid_qobject(), 'studentregistration') if QObject: return {'lotteried_students': q} else: return {'lotteried_students': ESPUser.objects.filter(q).distinct()}
def save_priorities(self, request, tl, one, two, module, extra, prog): """ Saves the priority preferences for student registration phase 2. """ if not 'json_data' in request.POST: return HttpResponseBadRequest('JSON data not included in request.') try: json_data = json.loads(request.POST['json_data']) except ValueError: return HttpResponseBadRequest('JSON data mis-formatted.') try: [timeslot_id] = json_data.keys() except ValueError: return HttpResponseBadRequest('JSON data mis-formatted.') if not isinstance(json_data[timeslot_id], dict): return HttpResponseBadRequest('JSON data mis-formatted.') timeslot = Event.objects.get(pk=timeslot_id) priorities = json_data[timeslot_id] for rel_index, cls_id in priorities.items(): rel_name = 'Priority/%s' % rel_index rel = RegistrationType.objects.get(name=rel_name, category='student') # Pull up any registrations that exist (including expired ones) srs = StudentRegistration.objects.annotate( Min('section__meeting_times__start')) srs = srs.filter( user=request.user, section__parent_class__parent_program=prog, section__meeting_times__start__min=timeslot.start, relationship=rel) if cls_id == '': # Blank: nothing selected, expire existing registrations for sr in srs: sr.expire() continue cls_id = int(cls_id) sec = ClassSection.objects.annotate(Min('meeting_times__start')) try: sec = sec.get(parent_class=cls_id, parent_class__parent_program=prog, meeting_times__start__min=timeslot.start) except (ClassSection.DoesNotExist, ClassSection.MultipleObjectsReturned): # XXX: what if a class has multiple sections in a timeblock? logger.warning("Could not save priority for class %s in " "timeblock %s", cls_id, timeslot_id) continue # sanity checks if (not sec.status > 0 or not sec.parent_class.status > 0): logger.warning("Class '%s' was not approved. Not letting " "user '%s' register.", sec, request.user) if (not sec.parent_class.grade_min <= request.user.getGrade(prog) or not sec.parent_class.grade_max >= request.user.getGrade(prog)): logger.warning("User '%s' not in class grade range; not " "letting them register.", request.user) continue if not srs.exists(): # Create a new registration sr = StudentRegistration( user=request.user, relationship=rel) else: # Pull the first StudentRegistration, expire the others for sr in srs[1:]: sr.expire() sr = srs[0] # Modify as needed to ensure the section is correct and # expiration date is valid if sr.section_id is None or sr.section.parent_class.id != cls_id: sr.section = sec sr.unexpire(save=False) sr.save() return self.goToCore(tl)
def initialize(self): """ Gather all of the information needed to run the lottery assignment. This includes: - Students' interest (priority and interested bits) - Class schedules and capacities - Timeslots (incl. lunch periods for each day) """ self.interest = numpy.zeros((self.num_students, self.num_sections), dtype=numpy.bool) self.priority = numpy.zeros((self.num_students, self.num_sections), dtype=numpy.bool) self.section_schedules = numpy.zeros( (self.num_sections, self.num_timeslots), dtype=numpy.bool) self.section_capacities = numpy.zeros((self.num_sections, ), dtype=numpy.uint32) self.section_overlap = numpy.zeros( (self.num_sections, self.num_sections), dtype=numpy.bool) # One array to keep track of the utility of each student # (defined as hours of interested class + 1.5*hours of priority classes) # and the other arrary to keep track of student weigths (defined as # of classes signed up for) self.student_utility_weights = numpy.zeros((self.num_students, ), dtype=numpy.float) self.student_utilities = numpy.zeros((self.num_students, ), dtype=numpy.float) # Get student, section, timeslot IDs and prepare lookup table (self.student_ids, self.student_indices) = self.get_ids_and_indices( self.program.students()['lotteried_students']) (self.section_ids, self.section_indices) = self.get_ids_and_indices( self.program.sections().filter(status__gt=0, registration_status=0)) (self.timeslot_ids, self.timeslot_indices) = self.get_ids_and_indices( self.program.getTimeSlots()) # Get IDs of timeslots allocated to lunch by day # (note: requires that this is constant across days) self.lunch_schedule = numpy.zeros((self.num_timeslots, )) lunch_timeslots = Event.objects.filter( meeting_times__parent_class__parent_program=self.program, meeting_times__parent_class__category__category='Lunch').order_by( 'start').distinct() # Note: this code should not be necessary once lunch-constraints branch is merged (provides Program.dates()) dates = [] for ts in self.program.getTimeSlots(): ts_day = date(ts.start.year, ts.start.month, ts.start.day) if ts_day not in dates: dates.append(ts_day) lunch_by_day = [[] for x in dates] ts_count = 0 for ts in lunch_timeslots: d = date(ts.start.year, ts.start.month, ts.start.day) lunch_by_day[dates.index(d)].append(ts.id) self.lunch_schedule[self.timeslot_indices[ts.id]] = True for i in range(len(lunch_by_day)): if len(lunch_by_day[i]) > ts_count: ts_count = len(lunch_by_day[i]) self.lunch_timeslots = numpy.zeros((len(lunch_by_day), ts_count), dtype=numpy.int32) for i in range(len(lunch_by_day)): self.lunch_timeslots[i, :len(lunch_by_day[i])] = numpy.array( lunch_by_day[i]) # Populate interest matrix interest_regs = StudentRegistration.valid_objects().filter( section__parent_class__parent_program=self.program, relationship__name='Interested').values_list( 'user__id', 'section__id').distinct() ira = numpy.array(interest_regs, dtype=numpy.uint32) self.interest[self.student_indices[ira[:, 0]], self.section_indices[ira[:, 1]]] = True # Populate priority matrix priority_regs = StudentRegistration.valid_objects().filter( section__parent_class__parent_program=self.program, relationship__name='Priority/1').values_list( 'user__id', 'section__id').distinct() pra = numpy.array(priority_regs, dtype=numpy.uint32) self.priority[self.student_indices[pra[:, 0]], self.section_indices[pra[:, 1]]] = True # Set student utility weights. Counts number of classes that students selected. Used only for computing the overall_utility stat # NOTE: Uses fixed (interest + priority) formula, needs attention when multiple priority levels are merged. self.student_utility_weights = numpy.sum( self.interest.astype(float), 1) + numpy.sum( self.priority.astype(float), 1) # Populate section schedule section_times = numpy.array(self.program.sections().filter( status__gt=0, registration_status=0).filter( meeting_times__id__isnull=False).distinct().values_list( 'id', 'meeting_times__id')) self.section_schedules[self.section_indices[section_times[:, 0]], self.timeslot_indices[section_times[:, 1]]] = True # Populate section overlap matrix parent_classes = numpy.array(self.program.sections().filter( status__gt=0, registration_status=0).order_by('id').values_list( 'parent_class__id', flat=True)) for i in range(self.num_sections): group_ids = numpy.nonzero(parent_classes == parent_classes[i])[0] self.section_overlap[numpy.meshgrid(group_ids, group_ids)] = True # Populate section grade limits self.section_grade_min = numpy.array(self.program.sections().filter( status__gt=0, registration_status=0).order_by('id').values_list( 'parent_class__grade_min', flat=True), dtype=numpy.uint32) self.section_grade_max = numpy.array(self.program.sections().filter( status__gt=0, registration_status=0).order_by('id').values_list( 'parent_class__grade_max', flat=True), dtype=numpy.uint32) # Populate student grades; grade will be assumed to be 0 if not entered on profile self.student_grades = numpy.zeros((self.num_students, )) gradyear_pairs = numpy.array(RegistrationProfile.objects.filter( user__id__in=list(self.student_ids), most_recent_profile=True, student_info__graduation_year__isnull=False).values_list( 'user__id', 'student_info__graduation_year'), dtype=numpy.uint32) self.student_grades[self.student_indices[ gradyear_pairs[:, 0]]] = 12 + ESPUser.current_schoolyear( ) - gradyear_pairs[:, 1] # Find section capacities (TODO: convert to single query) for sec in self.program.sections().filter(status__gt=0, registration_status=0): self.section_capacities[self.section_indices[ sec.id]] = sec.capacity # Populate section lengths (hours) self.section_lengths = numpy.array( [x.nonzero()[0].size for x in self.section_schedules])
def num_priority1s(self, prog): return StudentRegistration.valid_objects().filter( relationship__name='Priority/1', section__parent_class__parent_program=prog).count()
def enrollment_status(self, request, tl, one, two, module, extra, prog): resp = HttpResponse(content_type='application/json') data = StudentRegistration.valid_objects().filter(section__status__gt=0, section__parent_class__status__gt=0, section__parent_class__parent_program=prog, relationship__name='Enrolled').values_list('user__id', 'section__id') json.dump(list(data), resp) return resp
def update_schedule_json(self, request, tl, one, two, module, extra, prog): resp = HttpResponse(content_type='application/json') result = {'user': None, 'sections': [], 'messages': []} try: user = ESPUser.objects.get(id=int(request.GET['user'])) except: user = None result['messages'].append('Error: could find user %s' % request.GET.get('user', None)) try: desired_sections = json.loads(request.GET['sections']) except: result['messages'].append('Error: could not parse requested sections %s' % request.GET.get('sections', None)) desired_sections = None # Check in student, since if they're using this view they must be onsite q = Record.objects.filter(user=user, program=prog, event='attended') if not q.exists(): new_rec, created = Record.objects.get_or_create(user=user, program=prog, event='attended') if user and desired_sections is not None: override_full = (request.GET.get("override", "") == "true") current_sections = list(ClassSection.objects.filter(nest_Q(StudentRegistration.is_valid_qobject(), 'studentregistration'), status__gt=0, parent_class__status__gt=0, parent_class__parent_program=prog, studentregistration__relationship__name='Enrolled', studentregistration__user__id=user.id).values_list('id', flat=True).order_by('id').distinct()) sections_to_remove = ClassSection.objects.filter(id__in=list(set(current_sections) - set(desired_sections))) sections_to_add = ClassSection.objects.filter(id__in=list(set(desired_sections) - set(current_sections))) failed_add_sections = [] for sec in sections_to_add: if sec.isFull() and not override_full: result['messages'].append('Failed to add %s (%s) to %s: %s (%s). Error was: %s' % (user.name(), user.id, sec.emailcode(), sec.title(), sec.id, 'Class is currently full.')) failed_add_sections.append(sec.id) if len(failed_add_sections) == 0: # Remove sections the student wants out of for sec in sections_to_remove: sec.unpreregister_student(user) result['messages'].append('Removed %s (%s) from %s: %s (%s)' % (user.name(), user.id, sec.emailcode(), sec.title(), sec.id)) # Remove sections that conflict with those the student wants into sec_times = sections_to_add.select_related('meeting_times__id').values_list('id', 'meeting_times__id').order_by('meeting_times__id').distinct() sm = ScheduleMap(user, prog) existing_sections = [] for (sec, ts) in sec_times: if ts and ts in sm.map and len(sm.map[ts]) > 0: # We found something we need to remove for sm_sec in sm.map[ts]: if sm_sec.id not in sections_to_add: sm_sec.unpreregister_student(user) result['messages'].append('Removed %s (%s) from %s: %s (%s)' % (user.name(), user.id, sm_sec.emailcode(), sm_sec.title(), sm_sec.id)) else: existing_sections.append(sm_sec) # Add the sections the student wants for sec in sections_to_add: if sec not in existing_sections and sec.id not in failed_add_sections: error = sec.cannotAdd(user, not override_full) if not error: reg_result = sec.preregister_student(user, overridefull=override_full) if not reg_result: error = 'Class is currently full.' else: reg_result = False if reg_result: result['messages'].append('Added %s (%s) to %s: %s (%s)' % (user.name(), user.id, sec.emailcode(), sec.title(), sec.id)) else: result['messages'].append('Failed to add %s (%s) to %s: %s (%s). Error was: %s' % (user.name(), user.id, sec.emailcode(), sec.title(), sec.id, error)) result['user'] = user.id result['sections'] = list(ClassSection.objects.filter(nest_Q(StudentRegistration.is_valid_qobject(), 'studentregistration'), status__gt=0, parent_class__status__gt=0, parent_class__parent_program=prog, studentregistration__relationship__name='Enrolled', studentregistration__user__id=result['user']).values_list('id', flat=True).distinct()) json.dump(result, resp) return resp
def get_regdate(self, ordering='start_date'): regs = StudentRegistration.valid_objects().filter(user=self.user, section__parent_class__parent_program=self.program, relationship__name='Enrolled') if regs.exists(): return regs.order_by(ordering).values_list('start_date', flat=True)[0].strftime("%Y-%m-%d %H:%M:%S")
def students(self, QObject = False): q_sr = Q(studentregistration__section__parent_class__parent_program=self.program) & nest_Q(StudentRegistration.is_valid_qobject(), 'studentregistration') q_ssi = Q(studentsubjectinterest__subject__parent_program=self.program) & nest_Q(StudentSubjectInterest.is_valid_qobject(), 'studentsubjectinterest') if QObject: return {'twophase_star_students': q_ssi, 'twophase_priority_students' : q_sr} else: return {'twophase_star_students': ESPUser.objects.filter(q_ssi).distinct(), 'twophase_priority_students': ESPUser.objects.filter(q_sr).distinct()}
# lunch randomly for users with both lunch blocks free. from script_setup import * import random from esp.cal.models import Event from esp.program.models import Program, StudentRegistration, RegistrationType from esp.program.models.class_ import ClassSection from esp.users.models import ESPUser program = Program.objects.get(id=115) # Change me! (Splash 2014) relationship = RegistrationType.objects.get(name='Enrolled') srs = StudentRegistration.valid_objects().filter( section__parent_class__parent_program=program, relationship=relationship) srs_pairs = srs.values_list('user', 'section') users = ESPUser.objects.filter(id__in=srs.values_list('user')) sections = ClassSection.objects.filter( parent_class__parent_program=program ).prefetch_related('meeting_times') timeblocks = Event.objects.filter( program=program, event_type__description='Class Time Block') users_by_id = {user.id: user for user in users} sections_by_id = {section.id: section for section in sections} timeblocks_by_id = {timeblock.id: timeblock for timeblock in timeblocks} sections_by_user_timeblock = {
def isCompleted(self): return StudentRegistration.valid_objects().filter(user=get_current_request().user, relationship__name="Request").exists()
def studentreg2phase(self, request, tl, one, two, module, extra, prog): """ Serves the two-phase student reg page. This page includes instructions for registration, and links to the phase1/phase2 sub-pages. """ timeslot_dict = {} # Populate the timeslot dictionary with the priority to class title # mappings for each timeslot. priority_regs = StudentRegistration.valid_objects().filter( user=request.user, relationship__name__startswith='Priority') priority_regs = priority_regs.select_related( 'relationship', 'section', 'section__parent_class') for student_reg in priority_regs: rel = student_reg.relationship title = student_reg.section.parent_class.title sec = student_reg.section times = sec.meeting_times.all().order_by('start') if times.count() == 0: continue timeslot = times[0].id if not timeslot in timeslot_dict: timeslot_dict[timeslot] = {rel: title} else: timeslot_dict[timeslot][rel] = title star_counts = {} interests = StudentSubjectInterest.valid_objects().filter( user=request.user, subject__parent_program=prog) interests = interests.select_related( 'subject').prefetch_related('subject__sections__meeting_times') for interest in interests: cls = interest.subject for sec in cls.sections.all(): times = sec.meeting_times.all() if len(times) == 0: continue timeslot = min(times, key=lambda t: t.start).id if not timeslot in star_counts: star_counts[timeslot] = 1 else: star_counts[timeslot] += 1 # Iterate through timeslots and create a list of tuples of information prevTimeSlot = None blockCount = 0 schedule = [] timeslots = prog.getTimeSlots(types=['Class Time Block', 'Compulsory']) for i in range(len(timeslots)): timeslot = timeslots[i] if prevTimeSlot != None: if not Event.contiguous(prevTimeSlot, timeslot): blockCount += 1 if timeslot.id in timeslot_dict: priority_dict = timeslot_dict[timeslot.id] priority_list = sorted(priority_dict.items()) else: priority_list = [] if timeslot.id in star_counts: priority_list.append(( 'Starred', "(%d classes)" % star_counts[timeslot.id])) schedule.append((timeslot, priority_list, blockCount + 1)) prevTimeSlot = timeslot context = {} context['timeslots'] = schedule return render_to_response( self.baseDir()+'studentregtwophase.html', request, context)
from django.db.models.aggregates import Min parser = argparse.ArgumentParser(description='Unenroll missing students from classes.') parser.add_argument('program_id', type=int, help='ID of the current program') parser.add_argument('--per-hour', dest='per_hour', action='store_true', help='Include this argument to clear registrations only for classes starting in the next 60 minutes.') args = parser.parse_args() enrolled = RegistrationType.objects.get(name='Enrolled') prog = Program.objects.get(id=args.program_id) relevant_sections = prog.sections().annotate(begin_time=Min("meeting_times__start")).filter(status=10, parent_class__status=10).exclude(parent_class__category__category='Lunch') # classes that started more than 60 minutes ago passed_sections = relevant_sections.filter(begin_time__lt=datetime.now() - timedelta(minutes=60)) # students who are enrolled in a class that started more than 60 minutes ago, who have not checked in students = ESPUser.objects.filter(studentregistration__in=StudentRegistration.valid_objects(), studentregistration__relationship=enrolled, studentregistration__section__in=passed_sections).distinct().exclude(record__program=prog, record__event='attended') # classes that start in the next 60 minutes upcoming_sections = relevant_sections.filter(begin_time__gt=datetime.now()) if args.per_hour: upcoming_sections = upcoming_sections.filter(begin_time__lt=datetime.now() + timedelta(minutes=60)) # registrations of missing students for upcoming classes registrations = StudentRegistration.valid_objects().filter(user__in=students, section__in=upcoming_sections, relationship=enrolled) print "Candidate Registrations to Delete:", len(registrations) print registrations cmd_str = raw_input("Would you like to delete these registrations [y/N]? --> ") if cmd_str.strip().lower() == 'y': registrations.update(end_date=datetime.now()) print 'Expired:', registrations else: print 'Action cancelled.'
def isCompleted(self): return bool(StudentRegistration.valid_objects().filter(section__parent_class__parent_program=self.program, user=get_current_request().user))