Tuesday 22 September 2009

Generator for distributing calls

At work I was confronted with the problem of 'How to distribute calls between 2 call centers in a fair and even way according to some yet-to-be-determined percentage split ration?' Taking 30% split, we cannot send the first 30 to call centre 1 and the remaining 70 to call centre 2. The calls must be distributed as evenly as possible throughout; for example split 30% or 10 calls would give: [1,2,1,1,2,1,1,2,1,1] and not [1,1,1,1,1,1,1,2,2,2]!

When thinking about these kinds of problems, it helps to abstract away from the context of calls and call centres and think about the underlying problem; distribution over a descrete choice space. From here, we might find a solution in another domain that solves our problem...

It turns out that the problem of call distribution distils to the same problem that Bresenham tackled when trying to draw a line on a computer screen matrix. Imagine that you have a graph of cells 100 pixels on the X axis and 100 pixels on the Y axis. Now imagine you are using the line drawing algorithm to paint a line from the X/Y origin to, say, the coordinates '100,30'.

The line drawing algorithm will, after 100 interations, have distributed pixels evenly between 70 in the X direction and 30 in the Y direction (but always moving towards X). This is exactly the same as distributing 30 calls to call centre 1 and the rest to call centre 2.

To model this, it makes sense to write a python generator that 'yields' the correct call centre when asked:


class CallDistributor():
def __init__(self, split=50):
self.split = split
if split > 100 or split <= 0:
raise ValueError

def _execute(self):
error = 0.0
step = 0
deltaErr = self.split / 100.00
yield 1
while True:
error += deltaErr
if abs(error) >= 0.5:
yield 2
error -= 1.0
else:
yield 1

def __iter__(self):
return iter(self._execute( ))


if __name__ == "__main__":
dist1 = CallDistributor(split=50)
anIter = iter(dist1)
for i in range(10):
print anIter.next()


If I have chance I'd like to expand this to take a 'number of call centres' parameter, rather than having it restricted to 2.

SqlSoup is cool

I've been playing with our internal wiki's database, and with SqlSoup. I've found that SqlSoup did exactly what I wanted it to do; read a database, infer its table layout, create classes that map attributes to those table columns and allow me to manipulate those classes before adding the modifications back.

This code prints out all the rows in the 'People' table:

from sqlalchemy.ext.sqlsoup import SqlSoup
db = SqlSoup("mysql://root@localhost/movements")
people = db.People.all()
people.sort()
import pprint
pprint.pprint(people)

How simple is that! :-)