Example #1
0
def twilio_ConfigurePracticeLocalNumber2010(practice, tw_number):
	"""
	Configures a practice's Twilio number to point to the correct Twilio IVR
	handler for the user, as well as sets the incoming phone number's friendly
	name appropriately.
	:param practice - practiceLocation which has mdcom_phone and mdcom_phone_sid set:
	2010 version - need different status callback url
	"""
	auth, uri = client.auth, client.account_uri
	tw_number = _getUSNumber(tw_number)
	# assume US number, need to add country code
	norm_number = '+1' + tw_number
	abs_uri = '://'.join([settings.SERVER_PROTOCOL, settings.SERVER_ADDRESS])
	url = reverse('MHLogin.DoctorCom.IVR.views_practice_v2.PracticeIVR_Main_New')
	statusurl = reverse('MHLogin.DoctorCom.IVR.views_practice_v2.PracticeIVR_Status')
	smsurl = reverse('MHLogin.DoctorCom.SMS.views.twilioSMS_incoming')

	d = {
		'PhoneNumber': norm_number,
		'FriendlyName': _('%s\'s DCom Number') % (practice.practice_name),
		'VoiceUrl': urljoin(abs_uri, url),
		'VoiceMethod': 'POST',
		'StatusCallback': urljoin(abs_uri, statusurl),
		'StatusCallbackMethod': 'POST',
		'SmsUrl': urljoin(abs_uri, smsurl),
	}
	# TODO: add resp = , Verify the resulting data from Twilio, in JSON format
	try:
		make_twilio_request('POST',
			uri + '/IncomingPhoneNumbers/' + practice.mdcom_phone_sid, auth=auth, data=d)
	except TwilioRestException:
		pass

	return True
Example #2
0
def twilio_ConfigurePracticeLocalNumber(practice, tw_number):
	"""
	DEPRECATED - in the future, it will be replaced by twilio_ConfigurePracticeLocalNumber2010
	Any changes here should be replicated to twilio_ConfigurePracticeLocalNumber2010
	Configures a practice's Twilio number to point to the correct Twilio IVR
	handler for the user, as well as sets the incoming phone number's friendly
	name appropriately.
	:param practice - PracticeLocation with mdcom_phone and mdcom_phone_sid set to the number
	whose urls is being set
	"""
	if (settings.TWILIO_PHASE == 2):
		return twilio_ConfigurePracticeLocalNumber2010(practice, tw_number)
	else:
		auth, uri = client2008.auth, client2008.account_uri

	abs_uri = '://'.join([settings.SERVER_PROTOCOL, settings.SERVER_ADDRESS])
	url = reverse('MHLogin.DoctorCom.IVR.views_practice.PracticeIVR_Main')
	smsurl = reverse('MHLogin.DoctorCom.SMS.views.twilioSMS_incoming')

	d = {
		'FriendlyName': _('%s\'s DCom Number') % (practice.practice_name),
		'Url': urljoin(abs_uri, url),
		'SmsUrl': urljoin(abs_uri, smsurl),
	}
	# TODO: add resp = , Verify the resulting data from Twilio, in JSON format
	try:
		make_twilio_request('POST',
			uri + '/IncomingPhoneNumbers/' + practice.mdcom_phone_sid, auth=auth, data=d)
	except TwilioRestException:
		pass

	return True
Example #3
0
def twilio_ConfigurePracticeLocalNumber(practice, tw_number):
    """
	DEPRECATED - in the future, it will be replaced by twilio_ConfigurePracticeLocalNumber2010
	Any changes here should be replicated to twilio_ConfigurePracticeLocalNumber2010
	Configures a practice's Twilio number to point to the correct Twilio IVR
	handler for the user, as well as sets the incoming phone number's friendly
	name appropriately.
	:param practice - PracticeLocation with mdcom_phone and mdcom_phone_sid set to the number
	whose urls is being set
	"""
    if (settings.TWILIO_PHASE == 2):
        return twilio_ConfigurePracticeLocalNumber2010(practice, tw_number)
    else:
        auth, uri = client2008.auth, client2008.account_uri

    abs_uri = '://'.join([settings.SERVER_PROTOCOL, settings.SERVER_ADDRESS])
    url = reverse('MHLogin.DoctorCom.IVR.views_practice.PracticeIVR_Main')
    smsurl = reverse('MHLogin.DoctorCom.SMS.views.twilioSMS_incoming')

    d = {
        'FriendlyName': _('%s\'s DCom Number') % (practice.practice_name),
        'Url': urljoin(abs_uri, url),
        'SmsUrl': urljoin(abs_uri, smsurl),
    }
    # TODO: add resp = , Verify the resulting data from Twilio, in JSON format
    try:
        make_twilio_request('POST',
                            uri + '/IncomingPhoneNumbers/' +
                            practice.mdcom_phone_sid,
                            auth=auth,
                            data=d)
    except TwilioRestException:
        pass

    return True
def test_make_twilio_request_bad_data(mock):
    resp = Mock()
    resp.ok = False
    mock.return_value = resp

    url = "http://random/url"
    make_twilio_request("POST", url)
    mock.assert_called_with("POST", "http://random/url.json",
                            headers=post_headers)
Example #5
0
def test_make_twilio_request_bad_data(mock):
    resp = Mock()
    resp.ok = False
    mock.return_value = resp

    url = "http://random/url"
    make_twilio_request("POST", url)
    mock.assert_called_with("POST", "http://random/url.json",
                            headers=post_headers)
Example #6
0
    def request(self, method, uri, **kwargs):
        """
        Send an HTTP request to the resource.

        :raises: a :exc:`~twilio.TwilioRestException`
        """
        if 'timeout' not in kwargs and self.timeout is not UNSET_TIMEOUT:
            kwargs['timeout'] = self.timeout

        data = kwargs.get('data')
        if data is not None:
            udata = {}
            for k, v in six.iteritems(data):
                key = k.encode('utf-8')
                if isinstance(v, (list, tuple, set)):
                    udata[key] = [encode_atom(x) for x in v]
                elif isinstance(v, (six.integer_types, six.binary_type, six.string_types)):
                    udata[key] = encode_atom(v)
                else:
                    raise ValueError('data should be an integer, '
                                     'binary, or string, or sequence ')
            data = urlencode(udata, doseq=True)

        event = HttpEvent(method, uri, data)
        self.events.append(event)
        resp = make_twilio_request(method, uri, auth=self.auth, **kwargs)

        event.url = resp.url
        event.status_code = resp.status_code
        event.response_body = six.text_type(resp.content)

        if method == "DELETE":
            return resp, {}
        else:
            return resp, json.loads(resp.content)
Example #7
0
def twilio_old_get_available_number(area_code, incountry="US", intype="local"):
	"""
	request for number in area_code - DOES NOT RESPECT AREACODE restriction
	TO BE REMOVED - return only phone #
	"""
	auth, uri, = client.auth, client.account_uri
	abs_uri = '://'.join([settings.SERVER_PROTOCOL, settings.SERVER_ADDRESS])
	d = {
		'AreaCode': area_code,
	}
	num_tup = None
	try:
		resp = make_twilio_request('GET',
			uri + '/AvailablePhoneNumbers/US/Local', auth=auth, data=d)
		content = json.loads(resp.content)
		# get the first phone number entry that matches - phone # only
		number_info = content['available_phone_numbers'][0]['phone_number']
		# if we don't get any number, retry...?
		logger.debug('twilio_ProvisionNewLocalNumber2010 areacode %s got %s number %s' % (
			area_code, str(content), number_info))
		us_number_info = _getUSNumber(number_info)
	except TwilioRestException as re:
		# This error indicates that no number was available.
		if re.code not in (TWILIO_AREACODE_PARAMETER_NOT_SUPPORTED,
			TWILIO_INVALID_AREA_CODE, TWILIO_NO_PHONE_NUMBERS_IN_AREA_CODE):
			raise Exception(_('Provision Local number fail: %s' % re.msg))
	except KeyError:
		pass  # return None if json fields not in resp
	return us_number_info
Example #8
0
def twilio_old_get_available_number(area_code, incountry="US", intype="local"):
    """
	request for number in area_code - DOES NOT RESPECT AREACODE restriction
	TO BE REMOVED - return only phone #
	"""
    auth, uri, = client.auth, client.account_uri
    abs_uri = '://'.join([settings.SERVER_PROTOCOL, settings.SERVER_ADDRESS])
    d = {
        'AreaCode': area_code,
    }
    num_tup = None
    try:
        resp = make_twilio_request('GET',
                                   uri + '/AvailablePhoneNumbers/US/Local',
                                   auth=auth,
                                   data=d)
        content = json.loads(resp.content)
        # get the first phone number entry that matches - phone # only
        number_info = content['available_phone_numbers'][0]['phone_number']
        # if we don't get any number, retry...?
        logger.debug(
            'twilio_ProvisionNewLocalNumber2010 areacode %s got %s number %s' %
            (area_code, str(content), number_info))
        us_number_info = _getUSNumber(number_info)
    except TwilioRestException as re:
        # This error indicates that no number was available.
        if re.code not in (TWILIO_AREACODE_PARAMETER_NOT_SUPPORTED,
                           TWILIO_INVALID_AREA_CODE,
                           TWILIO_NO_PHONE_NUMBERS_IN_AREA_CODE):
            raise Exception(_('Provision Local number fail: %s' % re.msg))
    except KeyError:
        pass  # return None if json fields not in resp
    return us_number_info
Example #9
0
def PracticeIVR_LeaveRegularMsg(request):
	"""
	Sends office manager text message, in attachment there is voice file of recording
	"""		
	if 'CallStatus' in request.POST:
		logger.debug('%s: Into LeaveTextMsg with call status %s' % (
				request.session.session_key, request.POST['CallStatus']))
	if('CallStatus' in request.POST and request.POST['CallStatus'] == 'completed'):
		try:
			callSID = request.POST['CallSid']
			auth, uri, = client.auth, client.account_uri
			resp = make_twilio_request('GET', uri + '/Calls/%s' % callSID, auth=auth)
			content = json.loads(resp.content)
			log = callLog.objects.get(callSID=callSID)
			log.call_duration = content['TwilioResponse']['Call']['Duration']
			log.save()
		except TwilioRestException as tre:
			logger.critical('Unable to get call status: %s' % tre.msg)
		except ObjectDoesNotExist as odne:
			logger.warning('Call log does not exist for sid: %s. Caller may have '
				'hung up shortly after Twilio starts call process.' % str(odne))

	#if already got recording - this must be second ittiration	
	if ('ivr_makeRecording_recording' in request.session or 
			request.session['ivr_only_callbacknumber'] == True):
		# get a list of all office managers for this practice
		mgrs = get_all_practice_managers(request.session['practice_id'])

		# after unique'ifying save_answering_service_message() expects recips 
		# as a list, see https://redmine.mdcom.com/issues/1374 for details
		save_answering_service_message(request, False, list(set(m.user for m in mgrs)))

		r = twilio.Response()
		r.append(tts(_('Your message have been sent. Good Buy')))
		r.append(twilio.Hangup())
		return HttpResponse(str(r), mimetype=settings.TWILIO_RESPONSE_MIMETYPE)
	else:
		# first or second iteration, get call back number and message recoding
		if ('ivr_makeRecording_callbacknumber' in request.session):	
			# get recording for non urgent message
			request.session['ivr_makeRecording_maxLength'] = 600  # 10 minutes
			request.session['ivr_makeRecording_leadSilence'] = 2
			request.session['ivr_makeRecording_promptOnce'] = True
			request.session['ivr_makeRecording_prompt'] = tts(_('Please say your '
					'non urgent message after the beep. Please state your name and '
					'speak clearly. Press pound when finished.'))
			request.session['ivr_makeRecording_returnOnHangup'] = \
				'MHLogin.DoctorCom.IVR.views_practice.PracticeIVR_LeaveRegularMsg'
			request.session['ivr_call_stack'].append('PracticeIVR_LeaveRegularMsg')
			request.session.modified = True

			# Pass off the recording action to the getRecording function.
			return getQuickRecording(request)
		else:	 
			#get call back number
			request.session['ivr_call_stack'].append('PracticeIVR_LeaveRegularMsg')
			request.session['ivr_callback_returnOnHangup'] = \
				'MHLogin.DoctorCom.IVR.views_practice.PracticeIVR_LeaveRegularMsg'
			return getCallBackNumber(request)
Example #10
0
def Twilio_call_initiate(request, type, url):
	# type is the model type that we're going to use.
	# 		1: TwilioCallGatherTest
	#		2: TwilioRecordTest
	caller = MHLUser.objects.get(id=request.user.id)
	called = caller

	if (not caller.mobile_phone):
		return errlib.err500(request, err_msg="You do not have a mobile phone number "
							"in your profile. Please enter one to use this function.")
	if (not called.mobile_phone):
		return errlib.err500(request, err_msg="The person you are trying to call "
							"doesn't have a mobile phone number listed.")

	if (type == 1):
		log = TwilioCallGatherTest()
	elif (type == 2):
		log = TwilioRecordTest()
	log.tester = caller
	log.save()

	# TODO: When confirmation gets figured out, generate an appropriate error message here.
	if (not caller.mobile_confirmed):
		pass
	if (not called.mobile_confirmed):
		pass

	response_args = ''
	if (request.GET and request.GET.has_key()):
		response_args = '?record=True'

	d = {
		'Caller': settings.TWILIO_CALLER_ID,
		'Called': caller.mobile_phone,
		'Url': url,
		'Method': 'POST',
		'Timeout': 60,
	}
	auth, uri = client.auth, client.account_uri 
	call_info = make_twilio_request('POST', uri + '/Calls', auth=auth, data=d)
	sid = json.loads(call_info.content).get('sid', '')

	if not validate_sid(sid):
		# This only occurs if the call SID is invalid or has changed formats.
		return errlib.err500(request)

	log.callid = sid
	log.save()

	if (type == 2):
		return sid

	context = get_context(request)
	context['called'] = called
	context['test_type'] = 10
	context['test_id'] = log.id
	context['form_action'] = reverse('MHLogin.tests.views.confirm_test',)
	return render_to_response("tests/confirm_test.html", context)
Example #11
0
def get_call_by_sid(callSid):
    """ 
	Helper to get call info based on SID
	"""
    if (settings.TWILIO_PHASE == 2):
        auth, uri, call = client.auth, client.account_uri, None
        try:
            params = '/Calls/%s.json' % (callSid)
            # twilio req appends .json in wrong place when params and no headers
            resp = make_twilio_request(
                'GET',
                uri + params,
                auth=auth,
                **{'headers': {
                    'Accept': 'application/json'
                }})
            content = json.loads(resp.content)
            call = content['call']
            logger.debug('get_call_by_sid content %s' % (call))
        except (TwilioRestException, ValueError, KeyError) as err:
            logger.critical("Problems querying call sid %s: %s" %
                            (callSid, str(err)))
    else:
        auth, uri, call = client2008.auth, client2008.account_uri, None
        try:
            params = '/Calls/%s' % (callSid)
            # twilio req appends .json in wrong place when params and no headers
            resp = make_twilio_request(
                'GET',
                uri + params,
                auth=auth,
                **{'headers': {
                    'Accept': 'application/json'
                }})
            content = json.loads(resp.content)
            logger.debug('get_call_by_sid content %s ' % content)
            call = content['TwilioResponse']['Call']
        except (TwilioRestException, ValueError, KeyError) as err:
            logger.critical("Problems querying call sid %s: %s" %
                            (callSid, str(err)))
    return call
Example #12
0
def twilio_ProvisionNewLocalNumber(area_code):
    """
	DEPRECATED - in the future, it will be replaced by twilio_ProvisionNewLocalNumber2010
	Any changes here should be replicated to twilio_ProvisionNewLocalNumber2010
	:param area_code:
		area_code may be any valid three-digit string with the first digit
		being 2-9 (inclusive), second digit being 0-8 (inclusive) and last digit
		being 0-9 (inclusive). Toll-free numbers are excluded.

	:returns:
		The newly provisioned number on success, or None if Twilio doesn't have
		any available numbers for the requested area code.

	:raises:
		A generic exception if an invalid area code is requested.
		Raises an HTTPError if the Twilio request results in an error other than
		listed above.
	Note: twilio 2010 uses 2 phase call: 1 to call to AvailablePhoneNumbers
	and with the resulting number, call to IncomingPhoneNumbers
	"""
    if (settings.TWILIO_PHASE == 2):
        return twilio_ProvisionNewLocalNumber2010(area_code)
    else:
        auth, uri, = client2008.auth, client2008.account_uri
    abs_uri = '://'.join([settings.SERVER_PROTOCOL, settings.SERVER_ADDRESS])
    url = reverse('MHLogin.DoctorCom.IVR.views_generic.UnaffiliatedNumber')
    d = {
        #'From' : settings.TWILIO_CALLER_ID,
        'AreaCode': area_code,
        'FriendlyName': 'Unassigned Number',
        'Url': urljoin(abs_uri, url),
    }
    num_tup = None
    try:
        resp = make_twilio_request('POST',
                                   uri + '/IncomingPhoneNumbers/Local',
                                   auth=auth,
                                   data=d)
        content = json.loads(resp.content)
        number_info = content['TwilioResponse']['IncomingPhoneNumber'][
            'PhoneNumber']
        sid = content['TwilioResponse']['IncomingPhoneNumber']['Sid']
        num_tup = (number_info, sid)
    except TwilioRestException as re:
        # This error indicates that no number was available.
        if re.code not in (TWILIO_AREACODE_PARAMETER_NOT_SUPPORTED,
                           TWILIO_INVALID_AREA_CODE,
                           TWILIO_NO_PHONE_NUMBERS_IN_AREA_CODE):
            raise Exception(_('Provision Local number fail: %s' % re.msg))
    except KeyError:
        pass  # return None if json fields not in resp
    return num_tup
Example #13
0
def twilio_ConfigurePracticeLocalNumber2010(practice, tw_number):
    """
	Configures a practice's Twilio number to point to the correct Twilio IVR
	handler for the user, as well as sets the incoming phone number's friendly
	name appropriately.
	:param practice - practiceLocation which has mdcom_phone and mdcom_phone_sid set:
	2010 version - need different status callback url
	"""
    auth, uri = client.auth, client.account_uri
    tw_number = _getUSNumber(tw_number)
    # assume US number, need to add country code
    norm_number = '+1' + tw_number
    abs_uri = '://'.join([settings.SERVER_PROTOCOL, settings.SERVER_ADDRESS])
    url = reverse(
        'MHLogin.DoctorCom.IVR.views_practice_v2.PracticeIVR_Main_New')
    statusurl = reverse(
        'MHLogin.DoctorCom.IVR.views_practice_v2.PracticeIVR_Status')
    smsurl = reverse('MHLogin.DoctorCom.SMS.views.twilioSMS_incoming')

    d = {
        'PhoneNumber': norm_number,
        'FriendlyName': _('%s\'s DCom Number') % (practice.practice_name),
        'VoiceUrl': urljoin(abs_uri, url),
        'VoiceMethod': 'POST',
        'StatusCallback': urljoin(abs_uri, statusurl),
        'StatusCallbackMethod': 'POST',
        'SmsUrl': urljoin(abs_uri, smsurl),
    }
    # TODO: add resp = , Verify the resulting data from Twilio, in JSON format
    try:
        make_twilio_request('POST',
                            uri + '/IncomingPhoneNumbers/' +
                            practice.mdcom_phone_sid,
                            auth=auth,
                            data=d)
    except TwilioRestException:
        pass

    return True
Example #14
0
def twilio_ProvisionNewTollFreeNumber(area_code):
    """
	DEPRECATED - in the future, it will be replaced by twilio_ProvisionNewTollFreeNumber2010
	Any changes here should be replicated to twilio_ProvisionNewTollFreeNumber2010
	:param area_code:
		area_code may be one of 800, 888, 877 or 866, as per Twilio's
		documentation. If it isn't one of these values, this function will
		raise an Exception.

	:returns:
		The newly provisioned number on success, or None if Twilio doesn't have
		any available numbers for the requested area code.

	:raises:
		Raises a generic exception if an invalid area code is requested.
		Raises an HTTPError if the Twilio request results in an error other than
		listed above.
	"""
    if (settings.TWILIO_PHASE == 2):
        return twilio_ProvisionNewTollFreeNumber2010(area_code)
    else:
        auth, uri = client2008.auth, client2008.account_uri
    abs_uri = '://'.join([settings.SERVER_PROTOCOL, settings.SERVER_ADDRESS])
    url = reverse('MHLogin.DoctorCom.IVR.views_generic.UnaffiliatedNumber')
    d = {
        #'From' : settings.TWILIO_CALLER_ID,
        'AreaCode': area_code,
        'FriendlyName': _('Unassigned Number'),
        'Url': urljoin(abs_uri, url),
    }
    num_tup = None
    try:
        resp = make_twilio_request('POST',
                                   uri + '/IncomingPhoneNumbers/TollFree',
                                   auth=auth,
                                   data=d)
        content = json.loads(resp.content)
        number_info = content['TwilioResponse']['IncomingPhoneNumber'][
            'PhoneNumber']
        sid = content['TwilioResponse']['IncomingPhoneNumber']['Sid']
        num_tup = (number_info, sid)
    except TwilioRestException as re:
        # This error indicates that no number was available.
        if re.code not in (TWILIO_AREACODE_PARAMETER_NOT_SUPPORTED,
                           TWILIO_INVALID_AREA_CODE,
                           TWILIO_NO_PHONE_NUMBERS_IN_AREA_CODE):
            raise Exception(_('TollFree number fail: %s' % re.msg))
    except KeyError:
        pass  # return None if json fields not in resp
    return num_tup
Example #15
0
def twilio_ProvisionNewLocalNumber(area_code):
	"""
	DEPRECATED - in the future, it will be replaced by twilio_ProvisionNewLocalNumber2010
	Any changes here should be replicated to twilio_ProvisionNewLocalNumber2010
	:param area_code:
		area_code may be any valid three-digit string with the first digit
		being 2-9 (inclusive), second digit being 0-8 (inclusive) and last digit
		being 0-9 (inclusive). Toll-free numbers are excluded.

	:returns:
		The newly provisioned number on success, or None if Twilio doesn't have
		any available numbers for the requested area code.

	:raises:
		A generic exception if an invalid area code is requested.
		Raises an HTTPError if the Twilio request results in an error other than
		listed above.
	Note: twilio 2010 uses 2 phase call: 1 to call to AvailablePhoneNumbers
	and with the resulting number, call to IncomingPhoneNumbers
	"""
	if (settings.TWILIO_PHASE == 2):
		return twilio_ProvisionNewLocalNumber2010(area_code)
	else:
		auth, uri, = client2008.auth, client2008.account_uri
	abs_uri = '://'.join([settings.SERVER_PROTOCOL, settings.SERVER_ADDRESS])
	url = reverse('MHLogin.DoctorCom.IVR.views_generic.UnaffiliatedNumber')
	d = {
		#'From' : settings.TWILIO_CALLER_ID,
		'AreaCode': area_code,
		'FriendlyName': 'Unassigned Number',
		'Url': urljoin(abs_uri, url),
	}
	num_tup = None
	try:
		resp = make_twilio_request('POST',
			uri + '/IncomingPhoneNumbers/Local', auth=auth, data=d)
		content = json.loads(resp.content)
		number_info = content['TwilioResponse']['IncomingPhoneNumber']['PhoneNumber']
		sid = content['TwilioResponse']['IncomingPhoneNumber']['Sid']
		num_tup = (number_info, sid)
	except TwilioRestException as re:
		# This error indicates that no number was available.
		if re.code not in (TWILIO_AREACODE_PARAMETER_NOT_SUPPORTED,
			TWILIO_INVALID_AREA_CODE, TWILIO_NO_PHONE_NUMBERS_IN_AREA_CODE):
			raise Exception(_('Provision Local number fail: %s' % re.msg))
	except KeyError:
		pass  # return None if json fields not in resp
	return num_tup
Example #16
0
def twilio_ProvisionNewTollFreeNumber(area_code):
	"""
	DEPRECATED - in the future, it will be replaced by twilio_ProvisionNewTollFreeNumber2010
	Any changes here should be replicated to twilio_ProvisionNewTollFreeNumber2010
	:param area_code:
		area_code may be one of 800, 888, 877 or 866, as per Twilio's
		documentation. If it isn't one of these values, this function will
		raise an Exception.

	:returns:
		The newly provisioned number on success, or None if Twilio doesn't have
		any available numbers for the requested area code.

	:raises:
		Raises a generic exception if an invalid area code is requested.
		Raises an HTTPError if the Twilio request results in an error other than
		listed above.
	"""
	if (settings.TWILIO_PHASE == 2):
		return twilio_ProvisionNewTollFreeNumber2010(area_code)
	else:
		auth, uri = client2008.auth, client2008.account_uri
	abs_uri = '://'.join([settings.SERVER_PROTOCOL, settings.SERVER_ADDRESS])
	url = reverse('MHLogin.DoctorCom.IVR.views_generic.UnaffiliatedNumber')
	d = {
		#'From' : settings.TWILIO_CALLER_ID,
		'AreaCode': area_code,
		'FriendlyName': _('Unassigned Number'),
		'Url': urljoin(abs_uri, url),
	}
	num_tup = None
	try:
		resp = make_twilio_request('POST',
			uri + '/IncomingPhoneNumbers/TollFree', auth=auth, data=d)
		content = json.loads(resp.content)
		number_info = content['TwilioResponse']['IncomingPhoneNumber']['PhoneNumber']
		sid = content['TwilioResponse']['IncomingPhoneNumber']['Sid']
		num_tup = (number_info, sid)
	except TwilioRestException as re:
		# This error indicates that no number was available.
		if re.code not in (TWILIO_AREACODE_PARAMETER_NOT_SUPPORTED,
			TWILIO_INVALID_AREA_CODE, TWILIO_NO_PHONE_NUMBERS_IN_AREA_CODE):
			raise Exception(_('TollFree number fail: %s' % re.msg))
	except KeyError:
		pass  # return None if json fields not in resp
	return num_tup
Example #17
0
def ProviderIVR_LeaveMsg(request):
    """
	Records a voicemail message.

	Arguments:
		request - The standard Django request argument

	request.session Keys:
		provider_id - The ID of the Provider user who owns this voicemail box.
	"""
    # TODO: Update this code so that users can hit the pound key to
    # pop back and log into their own voicemail box.
    provider = Provider.objects.get(id=request.session['provider_id'])

    config = None
    config_complete = provider.vm_config.count(
    ) == 1 and provider.vm_config.get().config_complete
    if (config_complete):
        config = provider.vm_config.get()

    if ('CallStatus' in request.POST
            and request.POST['CallStatus'] == 'completed'):
        try:
            callSID = request.POST['CallSid']
            auth, uri, = client.auth, client.account_uri
            resp = make_twilio_request('GET',
                                       uri + '/Calls/%s' % callSID,
                                       auth=auth)
            content = json.loads(resp.content)
            log = callLog.objects.get(callSID=callSID)
            log.call_duration = content['TwilioResponse']['Call']['Duration']
            log.save()
        except:
            pass  # this is really ugly, but letting this exception fall through
            # destroys a message analytics will pick up the duration later on if it's missing

    if ('ivr_makeRecording_recording' in request.session):
        provider_qs = Provider.objects.filter(
            mobile_phone=request.session['Caller'])
        if (provider_qs):
            request.session['ivr_makeRecording_callbacknumber'] = provider_qs[
                0].mdcom_phone
            subject = "Voice Mail from %s %s" % (provider_qs[0].first_name,
                                                 provider_qs[0].last_name)
        else:
            request.session[
                'ivr_makeRecording_callbacknumber'] = request.session['Caller']
            subject = "Voice Mail from %s" % request.session[
                'ivr_makeRecording_callbacknumber']
        save_message(request, subject, [provider.user], None, "VM", False)
        request.session.pop('ivr_makeRecording_recording')

        r = twilio.Response()
        r.append(tts('Good bye'))
        r.append(twilio.Hangup())
        return HttpResponse(str(r), mimetype=settings.TWILIO_RESPONSE_MIMETYPE)

    # Get the configuration file for this Provider
    if (not config_complete):
        # FIXME:
        # Probably not the best way to create the spoken number. We need to
        # break the number down so that there are spaces between each digit, and
        # so that there are commas after the third and sixth digits.
        if (not 'Called' in request.session
                or (not request.session['Called'])):
            # This will occur if click2call calls this function
            request.session['ivr_makeRecording_prompt'] = tts(
                'The user is not available. '
                'Please leave a message after the beep. Press pound when finished for options.'
            )
        else:
            number = request.session['Called']
            spoken_number = []
            [spoken_number.extend([i, ' ']) for i in number]
            spoken_number.pop()  # drop the last element
            spoken_number.insert(5, ',')
            spoken_number.insert(12, ',')
            request.session['ivr_makeRecording_prompt'] = tts(
                'The person at %s '
                'is not available. Please leave a message after the beep. Press pound '
                'when finished for options.' % (''.join(spoken_number), ))
    else:
        p = re.compile('http')
        if (p.match(config.greeting)):
            # This is a Twilio recording.
            request.session['ivr_makeRecording_prompt'] = twilio.Play(
                config.greeting)
        else:
            # TODO:
            raise Exception('Unimplemented playback of local files')

    request.session['ivr_makeRecording_maxLength'] = 120  # 2 minutes
    request.session['ivr_makeRecording_leadSilence'] = 2
    request.session['ivr_makeRecording_promptOnce'] = True
    request.session['ivr_makeRecording_returnOnHangup'] = \
     'MHLogin.DoctorCom.IVR.views_provider.ProviderIVR_LeaveMsg'
    request.session['ivr_call_stack'].append('ProviderIVR_LeaveMsg')
    request.session.modified = True

    # Pass off the recording action to the getRecording function.
    return getRecording(request)
Example #18
0
def PracticeIVR_LeaveUrgentMsg(request):
	"""
	Records a voicemail message for the doctor on call
	and leave notification to the doctor based on preferences.
	"""
	# TODO: Update this code so that users can hit the pound key to pop 
	# back and log into their own voicemail box.
	if('CallStatus' in request.POST and request.POST['CallStatus'] == 'completed'):
		try:
			callSID = request.POST['CallSid']
			auth, uri, = client.auth, client.account_uri
			resp = make_twilio_request('GET', uri + '/Calls/%s' % callSID, auth=auth)
			content = json.loads(resp.content)
			log = callLog.objects.get(callSID=callSID)
			log.call_duration = content['TwilioResponse']['Call']['Duration']
			log.save()
		except TwilioRestException as tre:
			logger.critical('Unable to get call status: %s' % tre.msg)
		except ObjectDoesNotExist as odne:
			logger.warning('Call log does not exist for sid: %s.' % str(odne))

	logger.debug('%s: Into PracticeIVR_LeaveUrgentMsg with call status %s' %
				(request.session.session_key, request.POST['CallStatus']))

	if ('ivr_makeRecording_callbacknumber' in request.session):
		callback = request.session['ivr_makeRecording_callbacknumber']

	provider = _getRecentOncallProvider(request)
	# TODO: we do nothing with config
	config = None
	config_complete = provider.vm_config.count() == 1 and provider.vm_config.get().config_complete
	if (config_complete):
		config = provider.vm_config.get()

	if ('ivr_makeRecording_recording' in request.session or 
			request.session['ivr_only_callbacknumber'] == True):
		if (request.session['ivr_only_callbacknumber'] == False):
			mgrs = get_all_practice_managers(request.session['practice_id'])
			# after unique'ifying save_answering_service_message() expects recips 
			# as a list, see https://redmine.mdcom.com/issues/1374 for details
			save_answering_service_message(request, True, [provider], 
										list(set(m.user for m in mgrs)))
			#this is calling doctor on call we keep events for that even if called 
			#via answering service, just like doctor com does
			# Cleanup
			del request.session['ivr_makeRecording_recording']
		else:
			if('CallStatus' in request.POST and request.POST['CallStatus'] == 'completed'):
				mgrs = get_all_practice_managers(request.session['practice_id'])
				# after unique'ifying save_answering_service_message() expects recips 
				# as a list, see https://redmine.mdcom.com/issues/1374 for details
				save_answering_service_message(request, True, [provider], 
											list(set(m.user for m in mgrs)))

		# Notify the user, always by sms, but provider better have mobile phone
		if (provider.mobile_phone):
			if (request.session['ivr_only_callbacknumber'] == False):
				body = render_to_string('DoctorCom/IVR/voicemail_sms.txt', {
						#'caller': '(%s) %s-%s'%(request.session['Caller'][:3], 
						#request.session['Caller'][3:6], request.session['Caller'][6:]),
						'caller': '%s' % (request.session['ivr_makeRecording_callbacknumber']),
						})
			else:
				if ('ivr_no_pound' in request.session and request.session['ivr_no_pound'] == True):
					body = _("You have a new call from DoctorCom. Caller hung up from "
						"CONFIRMED number %s") % (request.session['ivr_makeRecording_callbacknumber'])
				else:
					body = _("You have a new call from DoctorCom. Caller hung up "
							"from unconfirmed number %s") % \
								(request.session['ivr_makeRecording_callbacknumber'])
			# TODO: we do nothing with body, sms send?
		# if pager number is entered, also page call back number
		r = twilio.Response()
		r.append(tts(_('Your message has been sent. Good bye')))
		r.append(twilio.Hangup())
		return HttpResponse(str(r), mimetype=settings.TWILIO_RESPONSE_MIMETYPE)

	if ('ivr_makeRecording_callbacknumber' in request.session):	
		request.session['ivr_makeRecording_prompt'] = tts(_('Please say your message '
				'for the doctor on call after the beep. Press pound when finished.'))

		request.session['ivr_makeRecording_maxLength'] = 600  # 10 minutes
		request.session['ivr_makeRecording_leadSilence'] = 2
		request.session['ivr_makeRecording_promptOnce'] = True

		request.session['ivr_makeRecording_returnOnHangup'] = \
			'MHLogin.DoctorCom.IVR.views_practice.PracticeIVR_LeaveUrgentMsg'
		request.session['ivr_call_stack'].append('PracticeIVR_LeaveUrgentMsg')
		request.session.modified = True

		# Pass off the recording action to the getRecording function.
		return getQuickRecording(request)

	#get call back number
	request.session['ivr_call_stack'].append('PracticeIVR_LeaveUrgentMsg')
	request.session['ivr_callback_returnOnHangup'] = \
		'MHLogin.DoctorCom.IVR.views_practice.PracticeIVR_LeaveUrgentMsg'
	return getCallBackNumber(request)
Example #19
0
def test_make_twilio_request_headers(mock):
    url = "http://random/url"
    make_twilio_request("POST", url)
    mock.assert_called_with("POST", "http://random/url.json",
                            headers=post_headers)
Example #20
0
def test_make_twilio_request_headers(mock):
    url = "http://random/url"
    make_twilio_request("POST", url)
    mock.assert_called_with("POST",
                            "http://random/url.json",
                            headers=post_headers)
Example #21
0
def get_active_call(mhphone, ctype=TYPE_CALLED):
    """ Helper to get active call, defaults to in_progress state. 
		for ref: http://www.twilio.com/docs/api/2008-08-01/rest/call

	:param mhphone: the Doctorcom number we are querying
	:param ctype: call type is called, caller, or either
	:param status: 0 = Not Yet Dialed, 1 = In Progress, 
		2 = Complete, 3 = Busy, 4 = Application Error, 5 = No Answer
	:returns: call or None if no match found
	* note:
	status is changed to string in 2010 twilio API, since this is active calls,
	I've changed status to 'in_progress'.
	Flags does not exist;
	Called = To; Caller = From; 
	"""
    if (settings.TWILIO_PHASE == 2):
        auth, uri, call = client.auth, client.account_uri, None
        status = "in-progress"
        usmhphone = _makeUSNumber(mhphone)
        try:
            if (ctype == TYPE_EITHER):
                params = '/Calls.json?Status=%s' % (status)
            else:
                if (ctype == TYPE_CALLED):
                    params = '/Calls.json?Status=%s&To=%s' % (status,
                                                              str(usmhphone))
                else:
                    params = '/Calls.json?Status=%s&From=%s' % (status,
                                                                str(usmhphone))
            # twilio req appends .json in wrong place when params and no headers
            resp = make_twilio_request(
                'GET',
                uri + params,
                auth=auth,
                **{'headers': {
                    'Accept': 'application/json'
                }})
            content = json.loads(resp.content)
            calls = content['calls']
            logger.debug('get_active_call new params %s content %s calls %s' %
                         (params, content, calls))
            call = calls[0] if calls else None
        except (TwilioRestException, ValueError, KeyError) as err:
            logger.critical("Problems querying active call list: %s" %
                            str(err))
    else:
        auth, uri, call = client2008.auth, client2008.account_uri, None
        status = 1
        try:
            params = '/Calls.json?Status=%d&Flags=4' % (status) if ctype == TYPE_EITHER \
             else '/Calls.json?Status=%d&%s=%s' % (status, CTYPES[ctype], str(mhphone))
            # twilio req appends .json in wrong place when params and no headers
            resp = make_twilio_request(
                'GET',
                uri + params,
                auth=auth,
                **{'headers': {
                    'Accept': 'application/json'
                }})
            content = json.loads(resp.content)
            logger.debug('get_active_call content %s ' % content)
            calls = content['TwilioResponse']['Calls']
            fn = lambda call: mhphone in (call['Call']['Called'], call['Call']['Caller']) \
             if ctype == TYPE_EITHER else mhphone in (call['Call'][CTYPES[ctype]], )
            results = filter(fn, calls)
            if results:  # get 1st match but should be only 1
                if len(results) > 1:
                    logger.error("Unexpected additional results for: %s" %
                                 str(mhphone))
                call = results[0]['Call']
        except (TwilioRestException, ValueError, KeyError) as err:
            logger.critical("Problems querying active call list: %s" %
                            str(err))

    return call
Example #22
0
def PracticeIVR_Main(request):
	"""
	DEPRECATED - to be replaced by PracticeIVR_Main_New in views_practice_v2.py
	entry point when call comes in to doctor.com number associated with Practice
	"""

	#if the practice's answering service is not available
	called = request.POST['Called']
	try:
		practice = PracticeLocation.objects.get(mdcom_phone=called)
		can_have_answering_service = practice.get_setting_attr('can_have_answering_service')
		if (not can_have_answering_service):
			r = twilio.Response()
			r.append(tts(_("I'm sorry, answering service is not available. Good bye.")))
			return HttpResponse(str(r), mimetype=settings.TWILIO_RESPONSE_MIMETYPE)
	except MultipleObjectsReturned:
		raise Exception(_('Multiple Practice Locations have mdcom_phone %s') % (called,))
	except ObjectDoesNotExist:
		raise Exception(_('No Practice Location have mdcom_phone %s') % (called,))

	if (request.COOKIES and 'CallStatus' not in request.POST or
			request.POST['CallStatus'] == 'completed'):

		callSID = request.POST['CallSid']
		try:
			if 'Duration' in request.POST:
				# if getrecording starts redirecting here on call completed, this becomes wrong
				# the 2008 api overloads 'Duration' for both call duration (minutes), and recording duration (seconds)
				log = callLog.objects.get(callSID=callSID)
				log.duration = int(request.POST['Duration'])
				log.save()
			else:
				auth, uri, = client.auth, client.account_uri
				resp = make_twilio_request('GET', uri + '/Calls/%s' % callSID, auth=auth)
				content = json.loads(resp.content)
				log = callLog.objects.get(callSID=callSID)
				log.call_duration = content['TwilioResponse']['Call']['Duration']
				log.save()
		except TwilioRestException as tre:
			logger.critical('Unable to get call status: %s' % tre.msg)
		except ObjectDoesNotExist as odne:
			logger.warning('Call log does not exist for sid: %s. Caller may have '
				'hung up shortly after Twilio starts call process.' % str(odne))

		r = twilio.Response()		
		return HttpResponse(str(r), mimetype=settings.TWILIO_RESPONSE_MIMETYPE)

	# ready to initialize this call, request.session[practice_id] in request stack
	practice = PracticeIVR_Init(request)

	############################################################################	
	#see if this is office manager calling, if yes - set up
	#TO DO - MANAGERS CELL ALSO NEED TO BE ADDED, also if not set up yet
	if (request.session['practice_phone'] == request.REQUEST['Caller']
		or practice.accessnumber_set.filter(number=request.REQUEST['Caller'])):
		# Great, now we know that this is the owner calling in. First up, check to
		# see if the user has a configuration file and that they've completed setup
		# it is stored with practice location, for now act like first time
		# Now, check to ensure that user voicemail configuration was completed successfully.
		if (not practice.config_complete):
			request.session['ivr_call_stack'].append('PracticeIVR_Main')
			request.session.modified = True
			return PracticeIVR_Setup(request)
		else:  # config was complete, but call is made, ask if want to change settings
			# make sure they have pin for setting
			if (not 'authenticated' in request.session):
				request.session['ivr_call_stack'].append('PracticeIVR_Main')
				request.session.modified = True	
				r = twilio.Response()
				return authenticateSession(request, r)

			request.session['ivr_call_stack'].append('PracticeIVR_Main')
			request.session.modified = True
			return PracticeIVR_TreeRoot(request)

	############################################################################	
	#phone call from outside called, they want to call answering service
	#lets see if we open/close/or lunch - based on that we decide on call tree
	#get current time in timezone of our practice, use gmt and then make it into local, 
	#calltime_local is the time we use.

	gmttz = timezone('GMT')
	mytz = timezone(practice.time_zone)
	calltime_gmt = datetime.now(gmttz)
	calltime_local = calltime_gmt.astimezone(mytz)

	request.session['calltime_local_string'] = calltime_local.strftime("%Y-%m-%d %H:%M:%S")
	request.session['calltime_local'] = calltime_local

	#if this is newly V2 and later set up practice, call new version of getting input
	if (practice.uses_original_answering_serice()):
		return PracticeIVR_CallerResponse(request)  # old way
	else:
		return PracticeIVR_V2_CallerResponse(request)  # V2 way
Example #23
0
def ProviderIVR_LeaveMsg(request):
	"""
	Records a voicemail message.

	Arguments:
		request - The standard Django request argument

	request.session Keys:
		provider_id - The ID of the Provider user who owns this voicemail box.
	"""
	# TODO: Update this code so that users can hit the pound key to 
	# pop back and log into their own voicemail box.
	provider = Provider.objects.get(id=request.session['provider_id'])

	config = None
	config_complete = provider.vm_config.count() == 1 and provider.vm_config.get().config_complete
	if (config_complete):
		config = provider.vm_config.get()

	if('CallStatus' in request.POST and request.POST['CallStatus'] == 'completed'):
		try:
			callSID = request.POST['CallSid']
			auth, uri, = client.auth, client.account_uri
			resp = make_twilio_request('GET', uri + '/Calls/%s' % callSID, auth=auth)
			content = json.loads(resp.content)
			log = callLog.objects.get(callSID=callSID)
			log.call_duration = content['TwilioResponse']['Call']['Duration']
			log.save()
		except:
			pass  # this is really ugly, but letting this exception fall through
				# destroys a message analytics will pick up the duration later on if it's missing

	if ('ivr_makeRecording_recording' in request.session):
		provider_qs = Provider.objects.filter(mobile_phone=request.session['Caller'])
		if(provider_qs):
			request.session['ivr_makeRecording_callbacknumber'] = provider_qs[0].mdcom_phone
			subject = "Voice Mail from %s %s" % (provider_qs[0].first_name, provider_qs[0].last_name)
		else:
			request.session['ivr_makeRecording_callbacknumber'] = request.session['Caller']
			subject = "Voice Mail from %s" % request.session['ivr_makeRecording_callbacknumber']
		save_message(request, subject, [provider.user], None, "VM", False)
		request.session.pop('ivr_makeRecording_recording')

		r = twilio.Response()
		r.append(tts('Good bye'))
		r.append(twilio.Hangup())
		return HttpResponse(str(r), mimetype=settings.TWILIO_RESPONSE_MIMETYPE)

	# Get the configuration file for this Provider
	if (not config_complete):
		# FIXME:
		# Probably not the best way to create the spoken number. We need to
		# break the number down so that there are spaces between each digit, and
		# so that there are commas after the third and sixth digits.
		if (not 'Called' in request.session or (not request.session['Called'])):
			# This will occur if click2call calls this function
			request.session['ivr_makeRecording_prompt'] = tts('The user is not available. '
					'Please leave a message after the beep. Press pound when finished for options.')
		else:
			number = request.session['Called']
			spoken_number = []
			[spoken_number.extend([i, ' ']) for i in number]
			spoken_number.pop()  # drop the last element
			spoken_number.insert(5, ',')
			spoken_number.insert(12, ',')
			request.session['ivr_makeRecording_prompt'] = tts('The person at %s '
				'is not available. Please leave a message after the beep. Press pound '
				'when finished for options.' % (''.join(spoken_number),))
	else:
		p = re.compile('http')
		if (p.match(config.greeting)):
			# This is a Twilio recording.
			request.session['ivr_makeRecording_prompt'] = twilio.Play(config.greeting)
		else:
			# TODO: 
			raise Exception('Unimplemented playback of local files')

	request.session['ivr_makeRecording_maxLength'] = 120  # 2 minutes
	request.session['ivr_makeRecording_leadSilence'] = 2
	request.session['ivr_makeRecording_promptOnce'] = True
	request.session['ivr_makeRecording_returnOnHangup'] = \
		'MHLogin.DoctorCom.IVR.views_provider.ProviderIVR_LeaveMsg'
	request.session['ivr_call_stack'].append('ProviderIVR_LeaveMsg')
	request.session.modified = True

	# Pass off the recording action to the getRecording function.
	return getRecording(request)