--- 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))
--- 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
--- 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))