class CommandInterpreter(cmd.Cmd): call_center = CallCenter() # ----- commands ----- def do_register_operator(self, arg): 'register a new operator with id <arg>' self.call_center.new_operator(arg) def do_call(self, arg): 'makes application receive a call whose id is <arg> ' self.call_center.new_call(arg) def do_answer(self, arg): 'makes operator <arg> answer a call being delivered to it' self.call_center.answer(arg) def do_reject(self, arg): 'makes operator <arg> reject a call being delivered to it' self.call_center.reject(arg) def do_hangup(self, arg): 'makes call whose id is <arg> be finished' self.call_center.hangup(arg) def do_exit(self, arg): return True
def __init__(self, config, redisPublisher): super(AMIBridge, self).__init__() ## Initialize logger global logger # global MY_SITE_IDENTIFICATION global MY_ALARM_SPAN try: MY_SITE_IDENTIFICATION = config.get("global","site_id") except NoOptionError: logger.warning("AMIBridge : Parameter site_id missing. \ Please check the configuration file odinf1com.conf. Value site_id missing from the section global") # try: MY_ALARM_SPAN = config.get('f1com', 'alarm_span_code') except NoOptionError: logger.warning("AMIBridge : Parameter site_id missing. \ Please check the configuration file odinf1com.conf. Value site_id missing from the section global") # self._redisPublisher = redisPublisher self.originateCalls = {} self.channels = {} self.bridgedCalls = {} self.parkedCalls = {} # self._callCenter = CallCenter(self) # try: self._asteriskServer = config.get("global", "asterisk") except NoOptionError: logger.error("AMIBridge : Parameter asterisk missing. \ Please check the configuration file odinf1com.conf. Value asterisk missing from the section global") reactor.stop() # self.amiDataHandlers = { 'createchannel' : self.handler_ami_createchannel, 'updatechannel' : self.handler_ami_updatechannel, 'removechannel' : self.handler_ami_removechannel, 'createbridge' : self.handler_ami_createbridge, 'updatebridge' : self.handler_ami_updatebridge, 'removebridge' : self.handler_ami_removebridge, 'updatepeer' : self.handler_ami_updatepeer, 'createparkedcall' : self.handler_ami_createparkedcall, 'removeparkedcall' : self.handler_ami_removeparkedcall, 'userevent' : self.handler_ami_userevent, 'alarm' : self.handler_ami_alarm }
from callcenter import CallCenter from prediction import get_weather_predictions weather_events = get_weather_predictions() print("Prediction of severe weather conditions:", weather_events) call_center = CallCenter("time.csv") regular_day_sche = call_center.create_regular_schedule() call_center.load_schedule(regular_day_sche, weather_events) # a schedule and its utilizaiton can be computed upfront. # It could be easily optimized even without running the simulation print("Utilization:", call_center.get_utilization()) call_center.run_simulation() print("Avg. wait time:", call_center.get_avg_wait()) print("QoS (% under 20s wait):", call_center.get_qos()) call_center.write_stats("wtime-output.csv")
from mathdojo import MathDojo from hospital import Patient from hospital import Hospital from car import Car from callcenter import Call from callcenter import CallCenter from bike import Bike from animals import Animal from animals import Dog from animals import Dragon md = MathDojo(0) print md hosp = Hospital("Great Hospital", 6) print hosp patient = Patient(1, "Bob") print patient car = Car(5000, "75mph", "Full", "25mpg") print car call = Call(1, "Stephen", "770-789-7038", 8.30, "For Fun") print call call_center = CallCenter() print call_center bike = Bike(150, "15mph") print bike fox = Animal("Fox") print fox pitbull = Dog("Pitbull") print pitbull trogdor = Dragon("Trogdor the Burninator") print trogdor
class AMIBridge(object): def __init__(self, config, redisPublisher): super(AMIBridge, self).__init__() ## Initialize logger global logger # global MY_SITE_IDENTIFICATION global MY_ALARM_SPAN try: MY_SITE_IDENTIFICATION = config.get("global","site_id") except NoOptionError: logger.warning("AMIBridge : Parameter site_id missing. \ Please check the configuration file odinf1com.conf. Value site_id missing from the section global") # try: MY_ALARM_SPAN = config.get('f1com', 'alarm_span_code') except NoOptionError: logger.warning("AMIBridge : Parameter site_id missing. \ Please check the configuration file odinf1com.conf. Value site_id missing from the section global") # self._redisPublisher = redisPublisher self.originateCalls = {} self.channels = {} self.bridgedCalls = {} self.parkedCalls = {} # self._callCenter = CallCenter(self) # try: self._asteriskServer = config.get("global", "asterisk") except NoOptionError: logger.error("AMIBridge : Parameter asterisk missing. \ Please check the configuration file odinf1com.conf. Value asterisk missing from the section global") reactor.stop() # self.amiDataHandlers = { 'createchannel' : self.handler_ami_createchannel, 'updatechannel' : self.handler_ami_updatechannel, 'removechannel' : self.handler_ami_removechannel, 'createbridge' : self.handler_ami_createbridge, 'updatebridge' : self.handler_ami_updatebridge, 'removebridge' : self.handler_ami_removebridge, 'updatepeer' : self.handler_ami_updatepeer, 'createparkedcall' : self.handler_ami_createparkedcall, 'removeparkedcall' : self.handler_ami_removeparkedcall, 'userevent' : self.handler_ami_userevent, 'alarm' : self.handler_ami_alarm } def stop(self): logger.debug("AMIBridge : Stopped.") def set_f1com_factory(self, factory): self.f1ComFactory = factory def process_ami_data(self, data): logger.debug("AMIBridge : Get data from ami worker") request = json.loads(data) requestId = request['id'] if id == None: logger.error("AMIBridge : Cant' find request id, the request ignored.") return logger.info("I'm processing request : %s" % (requestId)) handler = self.amiDataHandlers.get(requestId) if handler: reactor.callWhenRunning(handler, request) else: logger.error("AMIBridge : Can't find the request handler %s" %(requestId)) #Helpers def _look_for_channel_by_uniqueid(self, uniqueid): ''' ''' channel = None for i in self.channels.keys(): chan = self.channels[i] if uniqueid == chan.uniqueid: channel = chan logger.debug("AMIBridge : Get the channel %s for uniqueid %s." % (chan.channel, chan.uniqueid)) break # return channel def _set_callerid_for_bridge(self, bridge): ''' ''' channel = self._look_for_channel_by_uniqueid(bridge.uniqueid) if channel: bridge.calleridnum = channel.calleridnum bridge.calleridname = channel.calleridname else: logger.debug("AMIBridge : Can't find originate channel %s for the bridge uniqueid %s." % (bridge.channel, bridge.uniqueid)) # bridgedChannel = self._look_for_channel_by_uniqueid(bridge.bridgeduniqueid) if bridgedChannel: bridge.bridgecalleridnum = bridgedChannel.calleridnum bridge.bridgecalleridname = bridgedChannel.calleridname else: logger.debug("AMIBridge : Can't find bridged channel %s for the bridge uniqueid %s." % (bridge.bridgedchannel, bridge.bridgeduniqueid)) def _look_for_call_by_number(self, number): ''' Look for a call by the number First pass look into bridgedCalls by channel(the source of the call) Second pass look into bridgedCalls by bridgedchannel(the destination of the call) On success return the tuple bridgedchannel and channel TODO : remplace by one time pass ''' channel = None bridgedchannel = None for i in self.bridgedCalls.keys(): bridge = self.bridgedCalls[i] logger.debug("AMIBridge : Look for the bridgecalleridnum %s and compare with %s." % (bridge.bridgecalleridnum, number)) if bridge.bridgecalleridnum == number: channel = bridge.channel bridgedchannel = bridge.bridgedchannel break if not channel: for i in self.bridgedCalls.keys(): bridge = self.bridgedCalls[i] logger.debug("AMIBridge : Look for(second pass) the bridgecalleridnum %s and compare with %s. ." % (bridge.calleridnum, number)) if bridge.calleridnum == number: channel = bridge.bridgedchannel bridgedchannel = bridge.channel break # return (bridgedchannel, channel) def _createChannel(self, data): # createChannel = data['channel'] uniqueid = createChannel['uniqueid'] channel = createChannel['channel'] if not self.channels.has_key(uniqueid): # newChannel = BasicObject("Channel") newChannel.uniqueid = uniqueid newChannel.channel = channel newChannel.state = createChannel['state'] newChannel.calleridnum = createChannel['calleridnum'] newChannel.calleridname = createChannel['calleridname'] newChannel.monitor = createChannel['monitor'] newChannel.spy = createChannel['spy'] newChannel.starttime = time.time() self.channels[uniqueid] = newChannel else: logger.warning("AMIBridge : Channel %s with unique id %s already exists." %(channel, uniqueid)) def _updateChannel(self, data): # updateChannel = data['channel'] channel = updateChannel['channel'] uniqueid = updateChannel['uniqueid'] try: chan = self.channels.get(uniqueid) if chan: logger.debug("Channel update : %s." % (channel)) for k, v in updateChannel.items(): if chan.__dict__.has_key(k): chan.__dict__[k] = v else: logger.warning("AMIBridge : Channel %s does not have attribute %s" % (channel, k)) if logging.DUMPOBJECTS: logger.debug("AMIBridge : Channel updated :%s", chan) #udpate incall state self._callCenter.udpate_call_from_channel(chan) else: logger.warning("AMIBridge : Channel not found: %s." % (channel)) except: logger.exception("AMIBridge : Channel Unhandled exception updating channel: %s." % (channel)) def _removeChannel(self, data): # uniqueid = data['channel']['uniqueid'] channel = data['channel']['channel'] if self.channels.has_key(uniqueid): del self.channels[uniqueid] logger.debug("AMIBridge : Channel %s with the uniqueid %s removed." %(channel, uniqueid)) self._callCenter.check_and_remove_call(uniqueid) else: logger.warning("AMIBridge : Can't find and remove the channel %s with unique id %s." %(channel, uniqueid)) #F1Com commands part def originate_call(self, src, dest): ''' Origiante a call from src, I add the technologie to the src cause F1Com protocol don't carry aboit it TODO : Must look for the solution for indicate the technologie ''' tech = "SIP" peer = tech + "/" + src to_json = {"id": "originate", "servername": self._asteriskServer, "type": "dial", "user": "******", "source": peer, "destination": dest} message = json.dumps(to_json) self._redisPublisher.publish("odin_ami_request_channel", message) #originateCall = OriginateCall({'src': src, 'dest': dest}) originateCall = BasicObject("OriginateCall") originateCall.src = src originateCall.dest = dest #Set the key as the source number, the key will be migrate to uniqueid into create channel handler self.originateCalls[src] = originateCall #OriginateCall({'src': src, 'dest': dest}) logger.debug("AMIBridge : Originate call from %s to %s " % (src,dest)) def transffer_call(self, channel, ext): to_json = {"id": "transfer", "servername": self._asteriskServer, "type": "dial", "user": "******", "source": channel, "destination": ext} message = json.dumps(to_json) self._redisPublisher.publish("odin_ami_request_channel", message) def __hangup_call_by_number(self, number): ''' Look for a call by number If the call existe send a message to hangup the call ''' done = False bridgedchannel, channel = self._look_for_call_by_number(number) if channel: to_json = {"id": 'hangupchannel', "servername": self._asteriskServer , "user": "******", "channel": channel} message = json.dumps(to_json) self._redisPublisher.publish("odin_ami_request_channel", message) done = True return done def hangup_call(self, src, dest): ''' Look for a call by src into the bridged calls I the call is not located look for destination number ''' done = False # done = self.__hangup_call_by_number(src) if done == False and len(dest): ''' try by destination ''' done = self.__hangup_call_by_number(dest) if done : logger.debug("AMIBridge : Call located and hangup sent to the iPBX for the call with the source number %s." % (src)) else: logger.warning("AMIBridge : Call can't be located for hangup with the source number %s." % (src)) return done def hangup_call_by_callref(self, callref): logger.debug("AMIBridge : Hangup call by callref [ %s ]. " % (callref)) channel = self._callCenter.get_channel_by_callref(callref) done = False if channel: to_json = {"id": 'hangupchannel', "servername": self._asteriskServer, "user": "******", "channel": channel} message = json.dumps(to_json) self._redisPublisher.publish("odin_ami_request_channel", message) done = True else: logger.warning("AMIBridge : Can't find a call for the callref %s." % (callref)) return done def park_call(self, number): ''' Park a call by the called phone number , the call looked into bridged calls map It is a future of the F1COM protocol the channel is not used. ''' logger.info("AMIBridge : Looking an active call for the number and try to park." % (number)) done = False bridgedchannel, channel = self._look_for_call_by_number(number) if channel: to_json = {"id": 'parkchannel', "servername": self._asteriskServer, "user": "******", "channel": bridgedchannel, "announce" : channel} message = json.dumps(to_json) self._redisPublisher.publish("odin_ami_request_channel", message) done = True #check if the call existe and return value if done == True: logger.warning("AMIBridge : Park the call for the number %s." % (number)) else: logger.warning("AMIBridge : Park call the number is not parked , can't find the active call for the number %s." % (number)) return done def commut_call(self, internal, external): ''' Commut an internal extention to the parked call by the parked extention Please see the configuration of the asterisk box and parkedcalls context External can have two types of information : Reference of a call if begin with @, it is a incomming call and processed by Call Center module Otherwise this is an external number and looked into parked calls ''' done = False parkedExten = None if external[0] == '@': # done = self._callCenter.commut_call(internal, external) if done == True: return done # for i in self.parkedCalls.keys(): parkedCall = self.parkedCalls[i] if parkedCall.calleridnum == external: parkedExten = parkedCall.exten break #so far so good if parkedExten: self.originate_call(internal, parkedExten) logger.debug("AMIBridge : Call commuted for the internal %s to the external %s by the parked extention %s." % (internal, external, parkedExten)) done = True else: logger.debug("AMIBridge : Call can't be commuted for the internal %s and the external %s." %(internal, external)) return done def send_alarm_event(self, type, code, pstn, optional): if self.f1ComFactory: self.f1ComFactory.send_alarm_event(type, code, pstn, optional) else: logger.error("AMIBridge : Alarm event can't be send to F1COM clients.") def __send_callcenter_event(self, code, extention, callcenter, callref, caller, post=""): ''' Send a callcenter message to F1COM clients for indicate the state if an incomming call ''' logger.debug("AMIBridge : __send_callcenter_message [ %s %s %s %s ]" % (extention, callcenter, callref, caller)) if self.f1ComFactory: type = "1" pstn = "1" optional ="#SDA=" + extention if len(post) > 0: optional +="#POSTE=" + extention optional +="#IDCOMM=" + callref optional +="#CALLCENTER=" + callcenter optional +="#APPELANT=" + caller optional += "#AT=F1#NOMENT=27" self.f1ComFactory.send_alarm_event(type, code, pstn, optional) else: logger.error("AMIBridge : CallCenter event can't be send to F1COM clients.") def __process_incomming_call(self, data): logger.debug("AMIBridge : __process_incomming_call %s " % (data)) self._callCenter.new_call(data) #AMI handlers def handler_ami_updatepeer(self, data): pass def _send_call_state(self, state, dest): if self.f1ComFactory: self.f1ComFactory.send_result_ext_call(state, dest) logger.debug("AMIBridge : _send_call_state send %c for the destination %s to the F1 clients " % (state, dest)) else: logger.debug("AMIBridge : handler_ami_createchannel can't find into originate calls") def handler_ami_createchannel(self, data): logger.debug("AMIBridge : handler_ami_createchannel ") calleridnum = data['channel']['calleridnum'] if self.originateCalls.has_key(calleridnum): originateCall = self.originateCalls[calleridnum] uniqueid = data['channel']['uniqueid'] self.originateCalls[uniqueid] = originateCall originateCall.bridgedStatus ="" del self.originateCalls[calleridnum] originateCall.srcChannel = data['channel']['uniqueid'] self._send_call_state(0x30, originateCall.dest) else: logger.debug("AMIBridge : handler_ami_createchannel can't find into originate calls") # self._createChannel(data) def handler_ami_updatechannel(self, data): logger.debug("AMIBridge : handler_ami_updatechannel") self._updateChannel(data) def handler_ami_removechannel(self, data): logger.debug("AMIBridge : handler_ami_removechannel") uniqueid = data['channel']['uniqueid'] if self.originateCalls.has_key(uniqueid): originateCall = self.originateCalls[uniqueid] callState = 0x33 if originateCall.bridgedStatus == 'Link': callState=0x33 elif originateCall.bridgedStatus == 'Dial': callState=0x43 # dest = self.originateCalls[uniqueid].dest self._send_call_state(callState, dest) #cleanup call from the table del self.originateCalls[uniqueid] else: logger.debug("AMIBridge : handler_ami_removechannel can't find into originate calls") # self._removeChannel(data) def handler_ami_createbridge(self, data): logger.debug("AMIBridge handler_ami_createbridge") uniqueid = data['bridge']['uniqueid'] bridgeduniqueid = data['bridge']['bridgeduniqueid'] if self.originateCalls.has_key(uniqueid): originateCall = self.originateCalls[uniqueid] originateCall.srcChannel = data['bridge']['uniqueid'] originateCall.bridgetChannel = data['bridge']['bridgedchannel'] originateCall.bridgetUniqueId = data['bridge']['bridgeduniqueid'] bridgedStatus = data['bridge']['status'] originateCall.bridgedStatus = bridgedStatus if bridgedStatus == 'Dial': self._send_call_state(0x31, self.originateCalls[uniqueid].dest) else: logger.debug("AMIBridge : handler_ami_createbridge skip to send the status %s to F1" % (bridgedStatus)) else: logger.debug("AMIBridge : handler_ami_createbridge can't find into originate calls") #create bridged call bridgekey = (uniqueid, bridgeduniqueid) if not self.bridgedCalls.has_key(bridgekey): ''' ''' bridge = BasicObject("Bridge") bridge.uniqueid = uniqueid bridge.bridgeduniqueid = data['bridge']['bridgeduniqueid'] bridge.channel = data['bridge']['channel'] bridge.bridgedchannel = data['bridge']['bridgedchannel'] bridge.status = data['bridge']['status'] bridge.dialtime = data['bridge']['dialtime'] #bridge.linktime = data['bridge']['linktime'] self._set_callerid_for_bridge(bridge) if logging.DUMPOBJECTS: logger.debug("Bridge create :%s", bridge) self.bridgedCalls[bridgekey] = bridge #update an incomming call if existe self._callCenter.update_call_from_bridge(bridge) else: logger.warning("AMIBridge : bridge call already existe %s with %s" % (data['bridge']['channel'], data['bridge']['bridgedchannel'])) def handler_ami_updatebridge(self, data): logger.debug("AMIBridge handler_ami_updatebridge") uniqueid = data['bridge']['uniqueid'] bridgeduniqueid = data['bridge']['bridgeduniqueid'] bridgedStatus = data['bridge']['status'] if self.originateCalls.has_key(uniqueid): originateCall = self.originateCalls[uniqueid] # if bridgedStatus != originateCall.bridgedStatus : originateCall.bridgedStatus = bridgedStatus if bridgedStatus == 'Link': self._send_call_state(0x32, self.originateCalls[uniqueid].dest) else: logger.debug("AMIBridge : handler_ami_createbridge skip to send the status %s to F1" % (bridgedStatus)) else: logger.debug("AMIBridge : handler_ami_updatebridge can't find into originate calls") # bridgekey = (uniqueid, bridgeduniqueid) bridge = self.bridgedCalls.get(bridgekey) if bridge: bridge.status = data['bridge']['status'] bridge.linktime = data['bridge']['linktime'] #Set caller and called ids self._set_callerid_for_bridge(bridge) #check if the call is incomming self._callCenter.update_call_from_bridge(bridge) if logging.DUMPOBJECTS: logger.debug("Bridge updated :%s", bridge) def handler_ami_removebridge(self, data): logger.debug("AMIBridge handler_ami_removebridge") uniqueid = data['bridge']['uniqueid'] bridgeduniqueid = data['bridge']['bridgeduniqueid'] if self.originateCalls.has_key(uniqueid): originateCall = self.originateCalls[uniqueid] callState = 0x33 dest = self.originateCalls[uniqueid].dest self._send_call_state(callState, dest) #cleanup call from the table del self.originateCalls[uniqueid] logger.debug("AMIBridge : handler_ami_removebridge originate call for [%s] uniqueid removed." %(uniqueid)) else: logger.debug("AMIBridge : handler_ami_removebridge can't find an originate call for uniqueid [%s]." %(uniqueid)) # bridgekey = (uniqueid, bridgeduniqueid) bridgekey = (uniqueid, bridgeduniqueid) bridge = self.bridgedCalls.get(bridgekey) if bridge: # #check if the call is incomming self._callCenter.remove_call(bridge) del self.bridgedCalls[bridgekey] logger.warning("AMIBridge : handler_ami_removebridge a call removed %s with %s" % (data['bridge']['channel'], data['bridge']['bridgedchannel'])) def handler_ami_createparkedcall(self, data): logger.debug("AMIBridge handler_ami_createparkedcall") channel = data['parkedcall']['channel'] parked = self.parkedCalls.get(channel) if not parked : parked = BasicObject("ParkedCall") #populate the parked parked.channel = channel parked.exten = data['parkedcall']['exten'] parked.channelFrom = data['parkedcall']['parkedFrom'] parked.calleridnum = data['parkedcall']['calleridnum'] parked.calleridnumFrom = data['parkedcall']['calleridnumFrom'] parked.timeout = data['parkedcall']['timeout'] parked.calleridname = data['parkedcall']['calleridname'] parked.calleridnameFrom = data['parkedcall']['calleridnameFrom'] self.parkedCalls[channel] = parked def handler_ami_removeparkedcall(self, data): logger.debug("AMIBridge : handler_ami_removeparkedcall %s " % (data)) channel = data['parkedcall']['channel'] parked = self.parkedCalls.get(channel) if parked : del self.parkedCalls[parked.channel] logger.debug("AMIBridge : handler_ami_removeparkedcall the parked channel %s removed." % (parked.channel)) def handler_ami_userevent(self, data): ''' Handler for user event from asterisk dialplan Therea are different type of events to idicate what I must do :-) AMI is boss for me ''' type = data['event']['type'] if type == 'incommingcall': self.__process_incomming_call(data) def handler_ami_alarm(self, data): ''' Handler for alarm events from asterisk ''' eventType = data['alarmevent']['event'] type = '0' pstn = '1' code = "SPANAL" record = "" if eventType == 'SpanAlarm': type = '1' elif eventType == 'SpanAlarmClear': type = '2' elif eventType == 'RecordStart': type = '1' code = 'RECORD' record = '#ENREGISTREMENT=%s'%(data['alarmevent']['file']) elif eventType == 'RecordEnd': type = '2' code = 'RECORD' record = '#ENREGISTREMENT=%s'%(data['alarmevent']['file']) else : logger.warning("AMIBridge : handler_ami_alarm can not find type for the alarm event [%s]."% (data)) return # optional = "#SITE=%s#TEXT=%s" % (data['server'], data['alarmevent']['event']) if len(record) > 0: optional = optional + record # self.send_alarm_event(type, code, pstn,optional)