Instructions
A call center has three levels of employees: respondent, manager,
and director. An incoming call must be first allocated to a respondent who is free. If the respondent can't handle the call, he or she must escalate the call to a manager. If the manager is not free or not able to handle it, then the call should be escalated to a director. Design the classes and
data structures for this problem. Implement a method dispatchCall() which assigns a call to the first available employee.
Approach
We will follow the approach suggested in Cracking the Coding Interview:
Step 1: Handle Ambiguity
Here we determine the constraints and assumptions we will use in the design.
- We assume the responders always get the initial calls and they only escalate to manager if they cannot handle it. The call is escalated to the director if the manager cannot handle it and the director can handle all calls.
- Calls are placed in queue awaiting to be handled.
Step 2: Define the Core Objects
The main objects are Employee, Call,and CallCenter.
The Respondent, Manager, and Director classes inherit from the Employee class.
We also include a Rank class to handle the employee's rank, CallState class to determine the state of each call.
Step 3: Analyze Relationships
An employee can only handle one call at a time.
Step 4: Investigate Actions
The Employee can receive and end a call.
The Responder and Manager can escalate calls.
The CallCenter dispatches calls to available employees.
Implementation
Create an enumeration to define the level of each employee.
We create an enumeration where the levels are functionally constant.
Learn more about enum here.
from enum import Enum
class Rank(Enum):
RESPONDER = 0
MANAGER = 1
DIRECTOR = 2
Create class Employee which is a super class of the Responder, Manager and Director classes.
We implement the Employee class as an abstract class with all the abstract methods common to all employees.
More about abstract classes in python here and here.
An abstract class inherits from the Abstract Base Class ABC. @abstractmethod is a decorator to define an abstract method.
from abc import ABCMeta, abstractmethod
class Employee(metaclass=ABCMeta):
def __init__(self, employee_id, name, rank, call_center):
self.employee_id = employee_id
self.name = name
self.rank = rank
self.call = None
self.call_center = call_center
def take_call(self, call):
"""Assume the employee will always successfully take the call."""
self.call = call
self.call.employee = self
self.call.state = CallState.IN_PROGRESS
def complete_call(self):
self.call.state = CallState.COMPLETE
self.call_center.notify_call_completed(self.call)
@abstractmethod
def escalate_call(self):
pass
def _escalate_call(self):
self.call.state = CallState.READY
call = self.call
self.call = None
self.call_center.notify_call_escalated(call)
Create Responder class inheriting from Employee.
Learn more about inheritance here.
RANK.RESPONDER returns the value of the responder's level
class Responder(Employee):
def __init__(self, employee_id, name):
super(Responder, self).__init__(employee_id, name, Rank.RESPONDER)
def escalate_call(self):
self.call.level = Rank.MANAGER
self._escalate_call()
Create the Manager Class inheriting from Employee.
class Manager(Employee):
def __init__(self, employee_id, name):
super(Responder, self).__init__(employee_id, name, Rank.MANAGER)
def escalate_call(self):
self.call.level = Rank.DIRECTOR
self._escalate_call()
Create class Director.
Director cannot escalate call; therefore, we throw an error.
class Director(Employee):
def __init__(self, employee_id, name):
super(Responder, self).__init__(employee_id, name, Rank.DIRECTOR)
def escalate_call(self):
raise NotImplementedError('Directors must be able to handle any call')
Create an enumeration to define the state of a given call.
class CallState(Enum):
READY = 0
IN_PROGRESS = 1
COMPLETE = 2
Create Call class.
class Call(object):
def __init__(self, rank):
self.state = CallState.READY
self.rank = rank
self.employee = None
Create the CallCenter class.
- Attributes: responders, managers, directors and calls.
- methods : dispatch_call, notify when call is escalated,completed,
from collections import deque
class CallCenter(object):
def __init__(self, responders, managers, directors):
self.responders = responders
self.managers = managers
self.directors = directors
self.queued_calls = deque()
def dispatch_call(self, call):
if call.rank not in (Rank.RESPONDER, Rank.MANAGER, Rank.DIRECTOR):
raise ValueError('Invalid call rank: {}'.format(call.rank))
employee = None
if call.rank == Rank.RESPONDER:
employee = self._dispatch_call(call, self.responders)
if call.rank == Rank.MANAGER or employee is None:
employee = self._dispatch_call(call, self.managers)
if call.rank == Rank.DIRECTOR or employee is None:
employee = self._dispatch_call(call, self.directors)
if employee is None:
self.queued_calls.append(call)
def _dispatch_call(self, call, employees):
for employee in employees:
if employee.call is None:
employee.take_call(call)
return employee
return None
def notify_call_escalated(self, call):
print("Call escalated")
def notify_call_completed(self, call):
print("Call completed")
You can find the entire code here.
Happy learning and designing !!!
References
- Cracking the Coding Interview
- System Design Primer
Top comments (0)