diff --git a/dg/media/social_website/uploads/loop/__init__.py b/dg/media/social_website/uploads/loop/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/loop/admin.py b/loop/admin.py index ab1f3b2ccc..6164953c76 100644 --- a/loop/admin.py +++ b/loop/admin.py @@ -151,6 +151,29 @@ class AggregatorShareOutlierAdmin(admin.ModelAdmin): class IncentiveParameterAdmin(admin.ModelAdmin): list_display = ('notation','parameter_name', 'notation_equivalent') +class HelplineExpertAdmin(admin.ModelAdmin): + list_display = ('id', 'name', 'phone_number', 'email_id', 'expert_status') + list_filter = ('expert_status',) + search_fields = ['name', 'phone_number', 'email_id', 'expert_status'] + +class HelplineIncomingAdmin(admin.ModelAdmin): + list_display = ('id', 'from_number', 'to_number', 'call_status', 'incoming_time', 'last_incoming_time', 'resolved_time', 'recording_url', 'resolved_by', 'acknowledge_user') + list_filter = ('call_status', 'resolved_by') + search_fields = ['call_id', 'from_number', 'to_number', 'call_status', 'resolved_by'] + +class HelplineOutgoingAdmin(admin.ModelAdmin): + list_display = ('id', 'call_id', 'from_number', 'to_number', 'outgoing_time', 'incoming_call') + search_fields = ['call_id', 'from_number', 'to_number', 'outgoing_time'] + +class HelplineCallLogAdmin(admin.ModelAdmin): + list_display = ('id', 'call_id', 'from_number', 'to_number', 'start_time', 'call_type') + list_filter = ('call_type',) + search_fields = ['call_id', 'from_number', 'to_number', 'start_time', 'call_type'] + +class HelplineSmsLogAdmin(admin.ModelAdmin): + list_display = ('id', 'sms_id', 'from_number', 'to_number', 'sent_time') + search_fields = ['from_number', 'to_number', 'sent_time'] + loop_admin = LoopAdmin(name='loop_admin') loop_admin.register(Village, VillageAdmin) loop_admin.register(Block) @@ -177,3 +200,8 @@ class IncentiveParameterAdmin(admin.ModelAdmin): loop_admin.register(IncentiveModel,IncentiveModelAdmin) loop_admin.register(IncentiveParameter,IncentiveParameterAdmin) loop_admin.register(AggregatorShareOutliers,AggregatorShareOutlierAdmin) +loop_admin.register(HelplineExpert,HelplineExpertAdmin) +loop_admin.register(HelplineIncoming,HelplineIncomingAdmin) +loop_admin.register(HelplineOutgoing,HelplineOutgoingAdmin) +loop_admin.register(HelplineCallLog,HelplineCallLogAdmin) +loop_admin.register(HelplineSmsLog,HelplineSmsLogAdmin) diff --git a/loop/helpline_view.py b/loop/helpline_view.py new file mode 100644 index 0000000000..412b5c5f4e --- /dev/null +++ b/loop/helpline_view.py @@ -0,0 +1,168 @@ +import time +import datetime +import requests +import unicodecsv as csv +import xml.etree.ElementTree as xml_parse +from pytz import timezone + +from loop.models import HelplineExpert, HelplineIncoming, HelplineOutgoing, \ + HelplineCallLog, HelplineSmsLog + +from dg.settings import EXOTEL_ID, EXOTEL_TOKEN, EXOTEL_HELPLINE_NUMBER, MEDIA_ROOT + +from loop.utils.ivr_helpline.helpline_data import CALL_STATUS_URL, CALL_REQUEST_URL, \ + CALL_RESPONSE_URL, SMS_REQUEST_URL, APP_REQUEST_URL, APP_URL + +HELPLINE_LOG_FILE = '%s/loop/helpline_log.log'%(MEDIA_ROOT,) + +def write_log(log_file,module,log): + curr_india_time = datetime.datetime.now(timezone('Asia/Kolkata')) + with open(log_file, 'ab') as csvfile: + file_write = csv.writer(csvfile, quoting=csv.QUOTE_ALL) + file_write.writerow([curr_india_time,module,log]) + +# Call Type is 0 for Incoming Call and 1 for Outgoing Call +def save_call_log(call_id,from_number,to_number,call_type,start_time): + call_obj = HelplineCallLog(call_id=call_id,from_number=from_number,to_number=to_number,call_type=call_type,start_time=start_time) + try: + call_obj.save() + except Exception as e: + # if error then log + module = 'save_call_log' + write_log(HELPLINE_LOG_FILE,module,str(e)) + +def save_sms_log(sms_id,from_number,to_number,sms_body,sent_time): + sms_obj = HelplineSmsLog(sms_id=sms_id,from_number=from_number,to_number=to_number,sms_body=sms_body,sent_time=sent_time) + try: + sms_obj.save() + except Exception as e: + # if error then log + module = 'save_sms_log' + write_log(HELPLINE_LOG_FILE,module,str(e)) + +def get_status(call_id): + call_status_url = CALL_STATUS_URL%(EXOTEL_ID,EXOTEL_TOKEN,EXOTEL_ID,call_id) + response = requests.get(call_status_url) + call_status = dict() + # Status code 200 if API call is successful, 429 if Too many request + if response.status_code == 200: + response_tree = xml_parse.fromstring((response.text).encode('utf-8')) + call_detail = response_tree.findall('Call')[0] + call_status['response_code'] = 200 + call_status['status'] = str(call_detail.find('Status').text) + call_status['to'] = str(call_detail.find('To').text) + call_status['from'] = str(call_detail.find('From').text) + call_status['start_time'] = str(call_detail.find('StartTime').text) + call_status['end_time'] = str(call_detail.find('EndTime').text) + extra_detail = call_detail.findall('Details')[0] + call_status['from_status'] = str(extra_detail.find('Leg1Status').text) + call_status['to_status'] = str(extra_detail.find('Leg2Status').text) + else: + call_status['response_code'] = response.status_code + return call_status + +def get_info_through_api(outgoing_call_id): + call_status = get_status(outgoing_call_id) + if call_status['response_code'] == 200: + # Search latest pending Incoming object + incoming_obj = HelplineIncoming.objects.filter(from_number=call_status['to'],call_status=0).order_by('-id') + expert_obj = HelplineExpert.objects.filter(phone_number=call_status['from']) + if len(incoming_obj) > 0 and len(expert_obj) > 0: + incoming_obj = incoming_obj[0] + expert_obj = expert_obj[0] + to_number = call_status['to'] + return (incoming_obj,expert_obj,to_number) + return '' + +def update_incoming_acknowledge_user(incoming_call_obj,acknowledge_user): + if acknowledge_user == 0: + incoming_call_obj.acknowledge_user = 0 + else: + incoming_call_obj.acknowledge_user += 1 + try: + incoming_call_obj.save() + except Exception as e: + module = 'update_incoming_acknowledge_user' + write_log(HELPLINE_LOG_FILE,module,str(e)) + +# When we do not want to acknowledge User in case of call is not successfull +# then acknowledge_user parameter is more than 1 +# (For cases like call generated from queue module) +def make_helpline_call(incoming_call_obj,from_number_obj,to_number,acknowledge_user=0): + call_request_url = CALL_REQUEST_URL%(EXOTEL_ID,EXOTEL_TOKEN,EXOTEL_ID) + call_response_url = CALL_RESPONSE_URL + from_number = from_number_obj.phone_number + # CallType is either Transactional or Promotional + parameters = {'From':from_number,'To':to_number,'CallerId':EXOTEL_HELPLINE_NUMBER,'CallType':'trans','StatusCallback':call_response_url} + response = requests.post(call_request_url,data=parameters) + module = 'make_helpline_call' + if response.status_code == 200: + update_incoming_acknowledge_user(incoming_call_obj,acknowledge_user) + response_tree = xml_parse.fromstring((response.text).encode('utf-8')) + call_detail = response_tree.findall('Call')[0] + outgoing_call_id = str(call_detail.find('Sid').text) + outgoing_call_time = str(call_detail.find('StartTime').text) + save_call_log(outgoing_call_id,from_number,to_number,1,outgoing_call_time) + outgoing_obj = HelplineOutgoing(call_id=outgoing_call_id,incoming_call=incoming_call_obj,outgoing_time=outgoing_call_time,from_number=from_number_obj,to_number=to_number) + try: + outgoing_obj.save() + except Exception as e: + # Save Errors in Logs + write_log(HELPLINE_LOG_FILE,module,str(e)) + else: + # Enter in Log + log = 'Status Code: %s (Parameters: %s)'%(str(response.status_code),parameters) + write_log(HELPLINE_LOG_FILE,module,log) + +def send_helpline_sms(from_number,to_number,sms_body): + sms_request_url = SMS_REQUEST_URL%(EXOTEL_ID,EXOTEL_TOKEN,EXOTEL_ID) + parameters = {'From':from_number,'To':to_number,'Body':sms_body,'Priority':'high'} + response = requests.post(sms_request_url,data=parameters) + if response.status_code == 200: + response_tree = xml_parse.fromstring((response.text).encode('utf-8')) + sms_detail = response_tree.findall('SMSMessage')[0] + sms_id = str(sms_detail.find('Sid').text) + sent_time = str(sms_detail.find('DateCreated').text) + save_sms_log(sms_id,from_number,to_number,sms_body,sent_time) + else: + module = 'send_helpline_sms' + log = "Status Code: %s (Parameters: %s)"%(str(response.status_code),parameters) + write_log(HELPLINE_LOG_FILE,module,log) + +def connect_to_app(to_number,app_id): + app_request_url = APP_REQUEST_URL%(EXOTEL_ID,EXOTEL_TOKEN,EXOTEL_ID) + app_url = APP_URL%(app_id,) + parameters = {'From':to_number,'CallerId':EXOTEL_HELPLINE_NUMBER,'CallType':'trans','Url':app_url} + response = requests.post(app_request_url,data=parameters) + module = 'connect_to_app' + log = "App Id: %s Status Code: %s (Response text: %s)"%(app_id,str(response.status_code),str(response.text)) + write_log(HELPLINE_LOG_FILE,module,log) + +def fetch_info_of_incoming_call(request): + call_id = str(request.GET.getlist('CallSid')[0]) + farmer_number = str(request.GET.getlist('From')[0]) + dg_number = str(request.GET.getlist('To')[0]) + incoming_time = str(request.GET.getlist('StartTime')[0]) + return (call_id,farmer_number,dg_number,incoming_time) + +def update_incoming_obj(incoming_obj,call_status,recording_url,expert_obj,resolved_time): + incoming_obj.call_status = call_status + incoming_obj.recording_url = recording_url + incoming_obj.resolved_by = expert_obj + incoming_obj.resolved_time = resolved_time + try: + incoming_obj.save() + except Exception as e: + # if error in updating incoming object then Log + module = 'update_incoming_obj' + write_log(HELPLINE_LOG_FILE,module,str(e)) + +def send_acknowledge(incoming_call_obj): + if incoming_call_obj.acknowledge_user == 0: + return 0 + else: + return 1 + +def send_voicemail(farmer_number,OFF_HOURS_VOICEMAIL_APP_ID): + time.sleep(2) + connect_to_app(farmer_number,OFF_HOURS_VOICEMAIL_APP_ID) diff --git a/loop/management/commands/helpline_queue.py b/loop/management/commands/helpline_queue.py new file mode 100644 index 0000000000..c381fe518a --- /dev/null +++ b/loop/management/commands/helpline_queue.py @@ -0,0 +1,64 @@ +import time +import datetime +from datetime import timedelta +from pytz import timezone + +from django.core.management.base import BaseCommand +from django.utils.timezone import now + +from dg.settings import EXOTEL_ID, EXOTEL_TOKEN, EXOTEL_HELPLINE_NUMBER, MEDIA_ROOT + +from loop.models import HelplineExpert, HelplineIncoming, HelplineOutgoing +from loop.helpline_view import get_status, make_helpline_call, write_log + +HELPLINE_LOG_FILE = '%s/loop/helpline_log.log'%(MEDIA_ROOT,) + +class Command(BaseCommand): + + def check_pending_or_not(self,incoming_call_id): + incoming_call_obj = HelplineIncoming.objects.filter(id=incoming_call_id) + if incoming_call_obj: + incoming_call_obj = incoming_call_obj[0] + # Check If Status of call is pending + if incoming_call_obj.call_status == 0: + # Fetch latest outgoing of respective Incoming + latest_outgoing_of_incoming = HelplineOutgoing.objects.filter(incoming_call=incoming_call_obj).order_by('-id').values_list('call_id', flat=True)[:1] + if len(latest_outgoing_of_incoming) != 0: + call_status = get_status(latest_outgoing_of_incoming[0]) + else: + call_status = '' + # Check If Pending call is already in-progress + if call_status != '' and call_status['response_code'] == 200 and (call_status['status'] in ('ringing', 'in-progress')): + return '' + return incoming_call_obj + # If Status of call is not pending + else: + return '' + # If Incoming Call object not found + else: + return '' + + def handle(self, *args, **options): + expert_obj = HelplineExpert.objects.filter(expert_status=1)[:1] + if expert_obj: + expert_obj = expert_obj[0] + else: + #Log in file that no expert available + module = "helpline_queue" + write_log(HELPLINE_LOG_FILE,module,"No Expert Available") + return + old_time = datetime.datetime.now(timezone('Asia/Kolkata'))-timedelta(days=2) + old_time = old_time.replace(tzinfo=None) + try: + HelplineIncoming.objects.filter(call_status=0,last_incoming_time__lte=old_time).update(call_status=2) + except Exception as e: + module = "helpline_queue" + write_log(HELPLINE_LOG_FILE,module,str(e)) + pending_incoming_call_id = HelplineIncoming.objects.filter(call_status=0).order_by('id').values_list('id', flat=True) + for ids in pending_incoming_call_id: + incoming_call_obj = self.check_pending_or_not(ids) + if incoming_call_obj: + farmer_number = incoming_call_obj.from_number + # Last parameter more than 0 only when we do not want to acknowledge User if call is not successfull + make_helpline_call(incoming_call_obj,expert_obj,farmer_number,1) + time.sleep(120) diff --git a/loop/migrations/0007_auto_20170214_1040.py b/loop/migrations/0007_auto_20170214_1040.py new file mode 100644 index 0000000000..c836fcb79f --- /dev/null +++ b/loop/migrations/0007_auto_20170214_1040.py @@ -0,0 +1,112 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models +from django.conf import settings + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('loop', '0006_auto_20161230_0814'), + ] + + operations = [ + migrations.CreateModel( + name='HelplineCallLog', + fields=[ + ('time_created', models.DateTimeField(auto_now_add=True, null=True)), + ('time_modified', models.DateTimeField(auto_now=True, null=True)), + ('id', models.AutoField(serialize=False, primary_key=True)), + ('call_id', models.CharField(max_length=100)), + ('from_number', models.CharField(max_length=20)), + ('to_number', models.CharField(max_length=20)), + ('call_type', models.IntegerField(choices=[(0, b'Incoming'), (1, b'Outgoing')])), + ('start_time', models.DateTimeField()), + ('user_created', models.ForeignKey(related_name='loop_helplinecalllog_created', blank=True, editable=False, to=settings.AUTH_USER_MODEL, null=True)), + ('user_modified', models.ForeignKey(related_name='loop_helplinecalllog_related_modified', blank=True, editable=False, to=settings.AUTH_USER_MODEL, null=True)), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='HelplineExpert', + fields=[ + ('time_created', models.DateTimeField(auto_now_add=True, null=True)), + ('time_modified', models.DateTimeField(auto_now=True, null=True)), + ('id', models.AutoField(serialize=False, primary_key=True)), + ('name', models.CharField(max_length=100)), + ('phone_number', models.CharField(unique=True, max_length=20)), + ('email_id', models.CharField(max_length=50)), + ('expert_status', models.IntegerField(default=1, choices=[(0, b'Inactive'), (1, b'Active')])), + ('user_created', models.ForeignKey(related_name='loop_helplineexpert_created', blank=True, editable=False, to=settings.AUTH_USER_MODEL, null=True)), + ('user_modified', models.ForeignKey(related_name='loop_helplineexpert_related_modified', blank=True, editable=False, to=settings.AUTH_USER_MODEL, null=True)), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='HelplineIncoming', + fields=[ + ('time_created', models.DateTimeField(auto_now_add=True, null=True)), + ('time_modified', models.DateTimeField(auto_now=True, null=True)), + ('id', models.AutoField(serialize=False, primary_key=True)), + ('call_id', models.CharField(max_length=100)), + ('from_number', models.CharField(max_length=20, db_index=True)), + ('to_number', models.CharField(max_length=20)), + ('incoming_time', models.DateTimeField()), + ('last_incoming_time', models.DateTimeField()), + ('resolved_time', models.DateTimeField(null=True, blank=True)), + ('call_status', models.IntegerField(default=0, db_index=True, choices=[(0, b'Pending'), (1, b'Resolved'), (2, b'Declined')])), + ('recording_url', models.CharField(max_length=200, null=True, blank=True)), + ('acknowledge_user', models.IntegerField(default=0)), + ('resolved_by', models.ForeignKey(blank=True, to='loop.HelplineExpert', null=True)), + ('user_created', models.ForeignKey(related_name='loop_helplineincoming_created', blank=True, editable=False, to=settings.AUTH_USER_MODEL, null=True)), + ('user_modified', models.ForeignKey(related_name='loop_helplineincoming_related_modified', blank=True, editable=False, to=settings.AUTH_USER_MODEL, null=True)), + ], + ), + migrations.CreateModel( + name='HelplineOutgoing', + fields=[ + ('time_created', models.DateTimeField(auto_now_add=True, null=True)), + ('time_modified', models.DateTimeField(auto_now=True, null=True)), + ('id', models.AutoField(serialize=False, primary_key=True)), + ('call_id', models.CharField(max_length=100)), + ('to_number', models.CharField(max_length=20)), + ('outgoing_time', models.DateTimeField()), + ('from_number', models.ForeignKey(to='loop.HelplineExpert')), + ('incoming_call', models.ForeignKey(to='loop.HelplineIncoming')), + ('user_created', models.ForeignKey(related_name='loop_helplineoutgoing_created', blank=True, editable=False, to=settings.AUTH_USER_MODEL, null=True)), + ('user_modified', models.ForeignKey(related_name='loop_helplineoutgoing_related_modified', blank=True, editable=False, to=settings.AUTH_USER_MODEL, null=True)), + ], + ), + migrations.CreateModel( + name='HelplineSmsLog', + fields=[ + ('time_created', models.DateTimeField(auto_now_add=True, null=True)), + ('time_modified', models.DateTimeField(auto_now=True, null=True)), + ('id', models.AutoField(serialize=False, primary_key=True)), + ('sms_id', models.CharField(max_length=100)), + ('from_number', models.CharField(max_length=20)), + ('to_number', models.CharField(max_length=20)), + ('sms_body', models.CharField(max_length=2000, null=True, blank=True)), + ('sent_time', models.DateTimeField()), + ('user_created', models.ForeignKey(related_name='loop_helplinesmslog_created', blank=True, editable=False, to=settings.AUTH_USER_MODEL, null=True)), + ('user_modified', models.ForeignKey(related_name='loop_helplinesmslog_related_modified', blank=True, editable=False, to=settings.AUTH_USER_MODEL, null=True)), + ], + options={ + 'abstract': False, + }, + ), + migrations.AlterUniqueTogether( + name='helplineoutgoing', + unique_together=set([('call_id', 'incoming_call', 'from_number', 'outgoing_time')]), + ), + migrations.AlterUniqueTogether( + name='helplineincoming', + unique_together=set([('call_id', 'from_number', 'incoming_time')]), + ), + ] diff --git a/loop/models.py b/loop/models.py index 4d125ff3bb..071d89b2f0 100644 --- a/loop/models.py +++ b/loop/models.py @@ -10,6 +10,9 @@ ModelChoice = ((1, "Direct Sell"), (2, "Aggregate")) DISCOUNT_CRITERIA = ((0, "Volume"), (1, "Amount")) MODEL_TYPES = ((0, "Direct"), (1, "Tax Based"), (2, "Slab Based")) +CALL_TYPES = ((0, "Incoming"), (1, "Outgoing")) +CALL_STATUS = ((0, "Pending"), (1, "Resolved"), (2, "Declined")) +EXPERT_STATUS = ((0, "Inactive"), (1, "Active")) class LoopModel(models.Model): @@ -472,3 +475,73 @@ class Log(models.Model): action = models.IntegerField() entry_table = models.CharField(max_length=100) model_id = models.IntegerField(null=True) + + +class HelplineExpert(LoopModel): + id = models.AutoField(primary_key=True) + name = models.CharField(max_length=100) + phone_number = models.CharField(max_length=20, unique=True) + email_id = models.CharField(max_length=50) + expert_status = models.IntegerField(choices=EXPERT_STATUS, default=1) + + def __unicode__(self): + return "%s (%s)" % (self.name, self.phone_number) + + +class HelplineIncoming(LoopModel): + id = models.AutoField(primary_key=True) + call_id = models.CharField(max_length=100) + from_number = models.CharField(max_length=20, db_index=True) #User No. + to_number = models.CharField(max_length=20) #DG Exotel No. + incoming_time = models.DateTimeField() + last_incoming_time = models.DateTimeField() + resolved_time = models.DateTimeField(null=True, blank=True) + call_status = models.IntegerField(choices=CALL_STATUS, default=0, db_index=True) + recording_url = models.CharField(max_length=200, null=True , blank=True) + resolved_by = models.ForeignKey(HelplineExpert, null=True, blank=True) + acknowledge_user = models.IntegerField(default=0) + + def __unicode__(self): + return "%s (%s)" % (self.from_number, self.incoming_time) + + class Meta: + unique_together = ("call_id", "from_number", "incoming_time") + + +class HelplineOutgoing(LoopModel): + id = models.AutoField(primary_key=True) + call_id = models.CharField(max_length=100) + incoming_call = models.ForeignKey(HelplineIncoming) + from_number = models.ForeignKey(HelplineExpert) #DG Expert No. + to_number = models.CharField(max_length=20) #User No. + outgoing_time = models.DateTimeField() + + def __unicode__(self): + return "%s (%s)" % (self.from_number, self.to_number) + + class Meta: + unique_together = ("call_id", "incoming_call", "from_number", "outgoing_time") + + +class HelplineCallLog(LoopModel): + id = models.AutoField(primary_key=True) + call_id = models.CharField(max_length=100) + from_number = models.CharField(max_length=20) + to_number = models.CharField(max_length=20) + call_type = models.IntegerField(choices=CALL_TYPES) + start_time = models.DateTimeField() + + def __unicode__(self): + return "%s (%s) (%s)" % (self.from_number, self.to_number, self.call_type) + + +class HelplineSmsLog(LoopModel): + id = models.AutoField(primary_key=True) + sms_id = models.CharField(max_length=100) + from_number = models.CharField(max_length=20) + to_number = models.CharField(max_length=20) + sms_body = models.CharField(max_length=2000,null=True,blank=True) + sent_time = models.DateTimeField() + + def __unicode__(self): + return "%s (%s) (%s)" % (self.from_number, self.to_number, self.sent_time) diff --git a/loop/urls.py b/loop/urls.py index 4cdb633e15..950f971780 100644 --- a/loop/urls.py +++ b/loop/urls.py @@ -45,4 +45,7 @@ url(r'^payments/',payments), url(r'^farmer_payment_update/',farmer_payments), url(r'^chaining/', include('smart_selects.urls')), + url(r'^helpline_incoming/',helpline_incoming), + url(r'^helpline_call_response/',helpline_call_response), + url(r'^helpline_offline/',helpline_offline), ) diff --git a/loop/utils/__init__.py b/loop/utils/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/loop/utils/ivr_helpline/__init__.py b/loop/utils/ivr_helpline/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/loop/utils/ivr_helpline/helpline_data.py b/loop/utils/ivr_helpline/helpline_data.py new file mode 100644 index 0000000000..53f300e290 --- /dev/null +++ b/loop/utils/ivr_helpline/helpline_data.py @@ -0,0 +1,9 @@ +# -*- coding: utf-8 -*- + +helpline_data = {'sms_body':'लूप मे कॉल के लिए धन्यवाद,आपको कुछ समय मे संपर्क करेगे, न.-01139595953'} +CALL_STATUS_URL = 'https://%s:%s@twilix.exotel.in/v1/Accounts/%s/Calls/%s?details=true' +CALL_REQUEST_URL = 'https://%s:%s@twilix.exotel.in/v1/Accounts/%s/Calls/connect' +CALL_RESPONSE_URL = 'http://www.digitalgreen.org/loop/helpline_call_response/' +SMS_REQUEST_URL = 'https://%s:%s@twilix.exotel.in/v1/Accounts/%s/Sms/send' +APP_REQUEST_URL = 'https://%s:%s@twilix.exotel.in/v1/Accounts/%s/Calls/connect' +APP_URL = 'http://my.exotel.in/exoml/start/%s' diff --git a/loop/views.py b/loop/views.py index 187eb9c24e..39e8a1a399 100644 --- a/loop/views.py +++ b/loop/views.py @@ -1,7 +1,10 @@ import json import xlsxwriter +import requests from django.http import JsonResponse from io import BytesIO +from threading import Thread +import xml.etree.ElementTree as xml_parse from django.contrib.auth.models import User from django.views.decorators.csrf import csrf_exempt @@ -14,15 +17,29 @@ from tastypie.models import ApiKey, create_api_key from models import LoopUser, CombinedTransaction, Village, Crop, Mandi, Farmer, DayTransportation, Gaddidar, \ Transporter, Language, CropLanguage, GaddidarCommission, GaddidarShareOutliers, AggregatorIncentive, \ - AggregatorShareOutliers, IncentiveParameter, IncentiveModel + AggregatorShareOutliers, IncentiveParameter, IncentiveModel, HelplineExpert, HelplineIncoming, HelplineOutgoing, \ + HelplineCallLog, HelplineSmsLog from loop_data_log import get_latest_timestamp from loop.payment_template import * +from loop.utils.ivr_helpline.helpline_data import helpline_data +import unicodecsv as csv +import time +import datetime +from pytz import timezone import inspect + +from dg.settings import EXOTEL_ID, EXOTEL_TOKEN, EXOTEL_HELPLINE_NUMBER, NO_EXPERT_GREETING_APP_ID, OFF_HOURS_GREETING_APP_ID, \ + OFF_HOURS_VOICEMAIL_APP_ID, MEDIA_ROOT + +from loop.helpline_view import write_log, save_call_log, save_sms_log, get_status, get_info_through_api, \ + update_incoming_acknowledge_user, make_helpline_call, send_helpline_sms, connect_to_app, fetch_info_of_incoming_call, \ + update_incoming_obj, send_acknowledge, send_voicemail + # Create your views here. HELPLINE_NUMBER = "01139595953" ROLE_AGGREGATOR = 2 - +HELPLINE_LOG_FILE = '%s/loop/helpline_log.log'%(MEDIA_ROOT,) @csrf_exempt def login(request): @@ -659,3 +676,186 @@ def payments(request): data = json.dumps(chart_dict, cls=DjangoJSONEncoder) return HttpResponse(data) + +def helpline_incoming(request): + if request.method == 'GET': + call_id,farmer_number,dg_number,incoming_time = fetch_info_of_incoming_call(request) + save_call_log(call_id,farmer_number,dg_number,0,incoming_time) + incoming_call_obj = HelplineIncoming.objects.filter(from_number=farmer_number,call_status=0).order_by('-id') + # If No pending call with this number + if len(incoming_call_obj) == 0: + incoming_call_obj = HelplineIncoming(call_id=call_id, from_number=farmer_number, to_number=dg_number, incoming_time=incoming_time, last_incoming_time=incoming_time) + try: + incoming_call_obj.save() + except Exception as e: + # Write Exception to Log file + module = 'helpline_incoming (New Call)' + write_log(HELPLINE_LOG_FILE,module,str(e)) + return HttpResponse(status=500) + expert_obj = HelplineExpert.objects.filter(expert_status=1)[:1] + # Initiate Call if Expert is available + if len(expert_obj) > 0: + make_helpline_call(incoming_call_obj,expert_obj[0],farmer_number) + # Send Greeting and Sms if No Expert is available + else: + sms_body = helpline_data['sms_body'] + send_helpline_sms(EXOTEL_HELPLINE_NUMBER,farmer_number,sms_body) + # Send greeting to user for notify about no expert available at this time. + connect_to_app(farmer_number,NO_EXPERT_GREETING_APP_ID) + return HttpResponse(status=200) + # If pending call exist for this number + else: + # Update last incoming time for this pending call + incoming_call_obj = incoming_call_obj[0] + incoming_call_obj.last_incoming_time = incoming_time + try: + incoming_call_obj.save() + except Exception as e: + # Write Exception to Log file + module = 'helpline_incoming (Old Call)' + write_log(HELPLINE_LOG_FILE,module,str(e)) + latest_outgoing_of_incoming = HelplineOutgoing.objects.filter(incoming_call=incoming_call_obj).order_by('-id').values_list('call_id', flat=True)[:1] + if len(latest_outgoing_of_incoming) != 0: + call_status = get_status(latest_outgoing_of_incoming[0]) + else: + call_status = '' + # Check If Pending call is already in-progress + if call_status != '' and call_status['response_code'] == 200 and (call_status['status'] in ('ringing', 'in-progress')): + return HttpResponse(status=200) + expert_obj = HelplineExpert.objects.filter(expert_status=1)[:1] + # Initiate Call if Expert is available + if len(expert_obj) > 0: + make_helpline_call(incoming_call_obj,expert_obj[0],farmer_number) + # Send Greeting and Sms if No Expert is available + else: + sms_body = helpline_data['sms_body'] + send_helpline_sms(EXOTEL_HELPLINE_NUMBER,farmer_number,sms_body) + # Send greeting to user for notify about no expert available at this time. + connect_to_app(farmer_number,NO_EXPERT_GREETING_APP_ID) + return HttpResponse(status=200) + else: + return HttpResponse(status=403) + +@csrf_exempt +def helpline_call_response(request): + if request.method == 'POST': + status = str(request.POST.getlist('Status')[0]) + outgoing_call_id = str(request.POST.getlist('CallSid')[0]) + outgoing_obj = HelplineOutgoing.objects.filter(call_id=outgoing_call_id).select_related('incoming_call','from_number').order_by('-id') + outgoing_obj = outgoing_obj[0] if len(outgoing_obj) > 0 else '' + # If call Successfully completed then mark call as resolved + if status == 'completed': + recording_url = str(request.POST.getlist('RecordingUrl')[0]) + resolved_time = str(request.POST.getlist('DateUpdated')[0]) + if outgoing_obj: + incoming_obj = outgoing_obj.incoming_call + expert_obj = outgoing_obj.from_number + update_incoming_obj(incoming_obj,1,recording_url,expert_obj,resolved_time) + else: + # if outgoing object not found then get detail by call Exotel API + call_detail = get_info_through_api(outgoing_call_id) + if call_detail != '': + incoming_obj = call_detail[0] + expert_obj = call_detail[1] + update_incoming_obj(incoming_obj,1,recording_url,expert_obj,resolved_time) + elif status == 'failed': + if outgoing_obj: + farmer_number = outgoing_obj.to_number + #send sms to Notify User about Later Call + sms_body = helpline_data['sms_body'] + send_helpline_sms(EXOTEL_HELPLINE_NUMBER,farmer_number,sms_body) + else: + call_detail = get_info_through_api(outgoing_call_id) + if call_detail != '': + farmer_number = call_detail[2] + #send sms to Notify User about Later Call + sms_body = helpline_data['sms_body'] + send_helpline_sms(EXOTEL_HELPLINE_NUMBER,farmer_number,sms_body) + elif status == 'no-answer' or status == 'busy': + call_status = get_status(outgoing_call_id) + if call_status['response_code'] == 200: + # if expert pick call and (not farmer or farmer busy) + if call_status['from_status'] == 'completed': + if outgoing_obj: + farmer_number = outgoing_obj.to_number + else: + farmer_number = call_status['to'] + #send sms to Notify User about Later Call + sms_body = helpline_data['sms_body'] + send_helpline_sms(EXOTEL_HELPLINE_NUMBER,farmer_number,sms_body) + return HttpResponse(status=200) + make_call = 0 + if outgoing_obj: + incoming_obj = outgoing_obj.incoming_call + expert_obj = outgoing_obj.from_number + to_number = outgoing_obj.to_number + make_call = 1 + else: + call_detail = get_info_through_api(outgoing_call_id) + if call_detail != '': + incoming_obj = call_detail[0] + expert_obj = call_detail[1] + to_number = call_detail[2] + make_call = 1 + if make_call == 1: + # Find next expert + expert_numbers = list(HelplineExpert.objects.filter(expert_status=1)) + try: + expert_numbers = expert_numbers[expert_numbers.index(expert_obj)+1:] + except Exception as e: + expert_numbers = [] + pass + # Make a call if next expert found + if len(expert_numbers) > 0: + # if call initiate by queue module or in the chain of call initiate by queue module + if send_acknowledge(incoming_obj) == 0: + make_helpline_call(incoming_obj,expert_numbers[0],to_number) + else: + make_helpline_call(incoming_obj,expert_numbers[0],to_number,1) + # Send greeting and Sms if no expert is available + else: + # if call not initiate by queue module or not in the chain of call initiate by queue module + # then send acknowledgement of future call to user + if send_acknowledge(incoming_obj) == 0: + sms_body = helpline_data['sms_body'] + send_helpline_sms(EXOTEL_HELPLINE_NUMBER,to_number,sms_body) + # Send greeting to user for notify about no expert available at this time. + connect_to_app(to_number,NO_EXPERT_GREETING_APP_ID) + else: + #For other conditions write Logs + module = 'helpline_call_response' + log = 'Status: %s (outgoing_call_id: %s)'%(str(status),str(outgoing_call_id)) + write_log(HELPLINE_LOG_FILE,module,log) + return HttpResponse(status=200) + else: + return HttpResponse(status=403) + +def helpline_offline(request): + if request.method == 'GET': + call_id,farmer_number,dg_number,incoming_time = fetch_info_of_incoming_call(request) + save_call_log(call_id,farmer_number,dg_number,0,incoming_time) + incoming_call_obj = HelplineIncoming.objects.filter(from_number=farmer_number,call_status=0).order_by('-id') + if len(incoming_call_obj) == 0: + incoming_call_obj = HelplineIncoming(call_id=call_id, from_number=farmer_number, to_number=dg_number, incoming_time=incoming_time, last_incoming_time=incoming_time) + try: + incoming_call_obj.save() + except Exception as e: + # Write Exception to Log file + module = 'helpline_offline' + write_log(HELPLINE_LOG_FILE,module,str(e)) + return HttpResponse(status=500) + else: + # Update last incoming time for this pending call + incoming_call_obj = incoming_call_obj[0] + incoming_call_obj.last_incoming_time = incoming_time + try: + incoming_call_obj.save() + except Exception as e: + # Write Exception to Log file + module = 'helpline_offline' + write_log(HELPLINE_LOG_FILE,module,str(e)) + # Create thread for Notify User about off hours and record voicemail. + Thread(target=send_voicemail,args=[farmer_number,OFF_HOURS_VOICEMAIL_APP_ID]).start() + return HttpResponse(status=200) + else: + return HttpResponse(status=403)