# HG changeset patch # User Sverre Rabbelier # Date 1236529577 0 # Node ID 17c7a7a48dc762b61bcb2b3bbb9efbf5aae6a114 # Parent 1ac2d27fdb6bb607ecc107d4b3380d31a11935b4 Switch from actual applications to popularity Patch by: Sverre Rabbelier diff -r 1ac2d27fdb6b -r 17c7a7a48dc7 app/soc/logic/allocations.py --- a/app/soc/logic/allocations.py Sun Mar 08 15:00:59 2009 +0000 +++ b/app/soc/logic/allocations.py Sun Mar 08 16:26:17 2009 +0000 @@ -47,24 +47,19 @@ # the convenience of any mathematicians that happen to read this # piece of code ;). - def __init__(self, orgs, applications, mentors, slots, + def __init__(self, orgs, popularity, mentors, slots, max_slots_per_org, min_slots_per_org, iterative): """Initializes the allocator. Args: orgs: a list of all the orgs that need to be allocated - applications: a dictionary with for each org a list of applicants + popularity: the amount of applications per org mentors: the amount of assigned mentors per org slots: the total amount of available slots max_slots_per_org: how many slots an org should get at most min_slots_per_org: how many slots an org should at least get """ - all_applications = [] - - for _, value in applications.iteritems(): - all_applications += value - self.locked_slots = {} self.adjusted_slots = {} self.adjusted_orgs = [] @@ -74,9 +69,8 @@ self.max_slots_per_org = max_slots_per_org self.min_slots_per_org = min_slots_per_org self.orgs = set(orgs) - self.applications = applications + self.initial_popularity = popularity self.mentors = mentors - self.all_applications = set(all_applications) self.iterative = iterative def allocate(self, locked_slots, adjusted_slots): @@ -92,6 +86,9 @@ self.buildSets() + if not sum(self.popularity.values()) or not sum(self.mentors.values()): + return {} + if self.iterative: return self.iterativeAllocation() else: @@ -101,8 +98,9 @@ """Allocates slots with the specified constraints """ + popularity = self.initial_popularity.copy() + # set s - all_applications = self.all_applications locked_slots = self.locked_slots adjusted_slots = self.adjusted_slots @@ -112,17 +110,16 @@ # set a' and b' unlocked_orgs = self.orgs.difference(locked_orgs) - # unadjusted_orgs = self.orgs.difference(adjusted_orgs) # set a*b and a'*b' locked_and_adjusted_orgs = locked_orgs.intersection(adjusted_orgs) - - # unlocked_and_unadjusted_orgs = unlocked_orgs.intersection(unadjusted_orgs) # a+o and b+o should be o locked_orgs_or_orgs = self.orgs.union(locked_orgs) adjusted_orgs_or_orgs = self.orgs.union(adjusted_orgs) + total_popularity = sum(popularity.values()) + # an item can be only a or b, so a*b should be empty if locked_and_adjusted_orgs: raise Error("Cannot have an org locked and adjusted") @@ -135,17 +132,11 @@ if len(adjusted_orgs_or_orgs) != len(self.orgs): raise Error("Unknown org as adjusted slot") - # set l and l' - locked_applications = set(itertools.chain(*locked_slots.keys())) - unlocked_applications = all_applications.difference(locked_applications) - self.adjusted_orgs = adjusted_orgs self.unlocked_orgs = unlocked_orgs self.locked_orgs = locked_orgs - self.unlocked_applications = unlocked_applications - - popularity = ((k, len(v)) for k, v in self.applications.iteritems()) - self.popularity = dict(popularity) + self.popularity = popularity + self.total_popularity = total_popularity def rangeSlots(self, slots, org): """Returns the amount of slots for the org within the required bounds. @@ -166,23 +157,20 @@ adjusted_slots = self.adjusted_slots locked_orgs = self.locked_orgs locked_slots = self.locked_slots - unlocked_applications = self.unlocked_applications - unlocked_applications_count = len(unlocked_applications) - unallocated_applications_count = unlocked_applications_count + unallocated_popularity = self.total_popularity - len(locked_slots) available_slots = self.slots allocations = {} for org in self.orgs: - org_applications = self.applications[org] - org_applications_count = len(org_applications) + popularity = self.popularity[org] mentors = self.mentors[org] if org in locked_orgs: slots = locked_slots[org] - elif unallocated_applications_count: - weight = float(org_applications_count) / float(unallocated_applications_count) + elif unallocated_popularity: + weight = float(popularity) / float(unallocated_popularity) slots = int(math.floor(weight*available_slots)) if org in adjusted_orgs: @@ -194,7 +182,7 @@ allocations[org] = slots available_slots -= slots - unallocated_applications_count -= org_applications_count + unallocated_popularity -= popularity return allocations @@ -207,9 +195,7 @@ locked_orgs = self.locked_orgs locked_slots = self.locked_slots unlocked_orgs = self.unlocked_orgs - unlocked_applications = self.unlocked_applications - - total_popularity = sum(self.popularity.values()) + total_popularity = self.total_popularity available_slots = self.slots allocations = {} @@ -223,6 +209,7 @@ total_popularity -= popularity available_slots -= slots allocations[org] = slots + del self.popularity[org] # adjust the orgs in need of adjusting for org in adjusted_orgs: @@ -231,6 +218,7 @@ adjustment = (float(total_popularity)/float(available_slots))*slots adjustment = int(math.ceil(adjustment)) self.popularity[org] += adjustment + total_popularity += adjustment # adjust the popularity so that the invariants are always met for org in unlocked_orgs: @@ -248,9 +236,6 @@ # do the actual calculation for org in unlocked_orgs: - org_applications = self.applications[org] - org_applications_count = len(org_applications) - popularity = self.popularity[org] raw_slots = (float(popularity)/float(total_popularity))*available_slots slots = int(math.floor(raw_slots)) diff -r 1ac2d27fdb6b -r 17c7a7a48dc7 app/soc/views/models/program.py --- a/app/soc/views/models/program.py Sun Mar 08 15:00:59 2009 +0000 +++ b/app/soc/views/models/program.py Sun Mar 08 16:26:17 2009 +0000 @@ -38,6 +38,7 @@ from soc.logic.models import mentor as mentor_logic from soc.logic.models import organization as org_logic from soc.logic.models import org_admin as org_admin_logic +from soc.logic.models import student_proposal as student_proposal_logic from soc.logic.models import program as program_logic from soc.logic.models import student as student_logic from soc.logic.models.document import logic as document_logic @@ -171,33 +172,37 @@ program = program_logic.logic.getFromKeyFields(kwargs) slots = program.slots + filter = { + 'scope': program, + 'status': 'active', + } + + query = org_logic.logic.getQueryForFields(filter=filter) + organizations = org_logic.logic.getAll(query) + + locked_slots = adjusted_slots = {} + if request.method == 'POST' and 'result' in request.POST: result = request.POST['result'] - from_json = simplejson.loads(result).iteritems() - - # filter out all orgs where the link_id is 'undefined' - orgs = dict( ((k,v) for k, v in from_json if k != 'undefined')) + from_json = simplejson.loads(result) - locked_slots = dicts.groupDictBy(orgs, 'locked', 'slots') - adjusted_slots = dicts.groupDictBy(orgs, 'adjustment') - else: - filter = { - 'scope': program, - } + locked_slots = dicts.groupDictBy(from_json, 'locked', 'slots') + adjusted_slots = dicts.groupDictBy(from_json, 'adjustment') + + orgs = [i.link_id for i in organizations] + applications = {} + mentors = {} - query = org_logic.logic.getQueryForFields(filter=filter) - entities = [i.toDict() for i in org_logic.logic.getAll(query)] - - # group orgs by link_id - orgs = dict( ((i['link_id'], i) for i in entities) ) - - # default to no orgs locked nor adjusted - locked_slots = adjusted_slots = {} - - # TODO(Lennard): use real data here - applications = dict( ((i, [1, 2]) for i in orgs.keys()) ) - mentors = dict( ((i, 1000) for i in orgs.keys()) ) + for org in organizations: + filter = { + 'org': org, + 'status': ['new', 'pending'] + } + query = student_proposal_logic.logic.getQueryForFields(filter=filter) + proposals = student_proposal_logic.logic.getAll(query) + applications[org.link_id] = len(proposals) + mentors[org.link_id] = len([i for i in proposals if i.mentor != None]) # TODO: Use configuration variables here max_slots_per_org = 40 diff -r 1ac2d27fdb6b -r 17c7a7a48dc7 tests/app/soc/logic/test_allocations.py --- a/tests/app/soc/logic/test_allocations.py Sun Mar 08 15:00:59 2009 +0000 +++ b/tests/app/soc/logic/test_allocations.py Sun Mar 08 16:26:17 2009 +0000 @@ -69,58 +69,22 @@ self.iterative = False apps = { - 'asf': self.allocate(20, 20), - 'gcc': self.allocate(15, 30), - 'git': self.allocate(6, 6), - 'google': self.allocate(3, 10), - 'melange': self.allocate(100, 3), + 'asf': (20, 20), + 'gcc': (15, 50), + 'git': (6, 6), + 'google': (3, 10), + 'melange': (100, 3), } - self.applications = dict([(k,a) for k, (m, a) in apps.iteritems()]) - self.mentors = dict([(k,m) for k, (m, a) in apps.iteritems()]) + self.popularity = dict([(k,a) for k, (a, m) in apps.iteritems()]) + self.mentors = dict([(k,m) for k, (a, m) in apps.iteritems()]) - self.orgs = self.applications.keys() + self.orgs = self.popularity.keys() self.allocater = allocations.Allocator( - self.orgs, self.applications, self.mentors, self.slots, + self.orgs, self.popularity, self.mentors, self.slots, self.max_slots_per_org, self.min_slots_per_org, self.iterative) - def allocate(self, count, max): - """Returns a list with count new student objects. - """ - - i = self.allocated - j = i + count - self.allocated += count - - return max, [Student(i) for i in range(i,j)] - - def testAllocate(self): - """Test that the allocate helper works properly. - - A meta-test, it never hurts to be certain. - """ - - stash = self.allocated - self.allocated = 0 - - expected = [Student(0), Student(1), Student(2)] - count, actual = self.allocate(3, 0) - self.failUnlessEqual(expected, actual) - self.failUnlessEqual(count, 0) - - expected = [] - count, actual = self.allocate(0, 10) - self.failUnlessEqual(expected, actual) - self.failUnlessEqual(count, 10) - - expected = [Student(3)] - count, actual = self.allocate(1, 5) - self.failUnlessEqual(expected, actual) - self.failUnlessEqual(count, 5) - - self.allocated = stash - def testInitialAllocation(self): """Test that an allocation with no arguments does not crash. """ @@ -220,7 +184,7 @@ with_adjusting = self.allocater.allocate(locked_slots, adjusted_slots) without_adjusting = self.allocater.allocate(locked_slots, {}) - expected = without_adjusting['gcc'] + expected = without_adjusting['gcc'] + 10 actual = with_adjusting['gcc'] - self.failUnless(actual > expected) + self.failIf(actual < expected, "%d < %d" % (actual, expected))