Пример #1
0
    def get_context_data(self, **kwargs):
        data = super(WizardChatView, self).get_context_data(**kwargs)
        wizard_obj = Wizard.objects.get(id=self.request.GET['wizard_id'])
        campaign_obj = wizard_obj.campaign
        raw_phone_num = wizard_obj.recipient.username
        phone_num = format_to_phone_num(raw_phone_num)

        if 'show_all_history' in self.request.GET:
            records = MessageRecord.objects.filter(
                Q(receiver_num=raw_phone_num) | Q(sender_num=raw_phone_num))
            data['show_all_history'] = 1
        else:
            records = MessageRecord.objects.filter(
                campaign=campaign_obj).filter(
                    Q(receiver_num=raw_phone_num)
                    | Q(sender_num=raw_phone_num))

        data['records'] = records
        data['phone_number'] = phone_num
        data['campaign_title'] = campaign_obj.title
        data['campaign_id'] = campaign_obj.id
        data['wizard_obj'] = wizard_obj
        return data
Пример #2
0
 def get_formatted_receiver_number(self):
     return format_to_phone_num(self.receiver_num)
Пример #3
0
 def get_formatted_sender_number(self):
     return format_to_phone_num(self.sender_num)
Пример #4
0
    def handle(self, *args, **options):
        std_out = True  #False
        result = [
            "Group Name", "From", "To", "Time Sent or Received",
            "Message Content", "Conversation Title"
        ]
        for group_name in args:

            with open(group_name + '.csv', 'w') as csvfile:
                csvwiter = csv.writer(csvfile,
                                      delimiter=',',
                                      lineterminator='\n')

                if std_out: print(group_name + "\n====================")
                group_obj = Group.objects.filter(name=group_name).first()
                students = Student.objects.filter(groups=group_obj)

                csvwiter.writerow([unicode(s).encode("utf-8") for s in result
                                   ])  # some lines are non-ASCII
                if std_out: print(result)
                if std_out:
                    print(
                        "===================================================================================================="
                    )

                for student in students:
                    #school = student.school.name
                    group = group_name
                    phone_num = student.user.username
                    records = MessageRecord.objects.filter(
                        Q(receiver_num=phone_num) | Q(sender_num=phone_num)
                    )  # all messages sent/received by student

                    if not records:  # empty list
                        result.append('')
                    else:
                        for record in records:
                            result = [group]
                            sender = utils.format_to_phone_num(
                                record.sender_num)
                            receiver = utils.format_to_phone_num(
                                record.receiver_num)
                            msg_content = "Message Failed To Be Sent" if record.status == "give up" else unicode(
                                record.content).encode("utf-8")

                            try:
                                campaign_title = unicode(
                                    record.campaign.title).encode("utf-8")
                            except:
                                campaign_title = "N/A"

                            result.extend([
                                sender, receiver, record.created_at,
                                msg_content, campaign_title
                            ])

                            csvwiter.writerow(result)
                            if std_out: print(result)

                    csvwiter.writerow(
                        [])  # write an empty row between each student
                    if std_out: print("*****\n\n")
Пример #5
0
    def get(self, request, *args, **kwargs):

        # Twilio's GET request for incoming SMS looks like this:
        # "GET /smsmessages/receive?AccountSid=AC14415ab0be51ba4da097dd51da5a7d8f
        # &MessageSid=SMacc66709b7ba53144cd632b6ee17c0f0&Body=Ahhhhh&ToZip=14603
        # &ToCity=ROCHESTER&FromState=NY&ToState=NY&SmsSid=SMacc66709b7ba53144cd632b6ee17c0f0
        # &To=%2B15853600116&ToCountry=US&FromCountry=US&SmsMessageSid=SMacc66709b7ba53144cd632b6ee17c0f0
        # &ApiVersion=2010-04-01&FromCity=&SmsStatus=received&NumMedia=0&From=%2B15856268095&FromZip= HTTP/1.1" 404 2862

        # TODO ***: Refactor this and figure out a neater way to separate between country code and core phone num
        # IMPORTANT: must get rid of the '+1' too

        # TODO: This is a temporary solution; We need to decide on how to handle different country codes more elegantly
        if ((request.GET['From'][:2] == "+1")
                and (len(request.GET['From'][2:]) == 10)):  # means US number
            caller_num = request.GET['From'][
                2:]  # "5856261222" from "+15856261222"
        else:
            caller_num = request.GET['From'][
                1:]  #  "8613910988979" from "+8613910988979"

        our_twilio_num = request.GET['To'][
            2:]  # our_twilio number WITH country code and '+'

        msg_content = request.GET['Body']
        sms_sid = request.GET['SmsSid']

        last_msg_record = MessageRecord.objects.filter(
            message__isnull=False, receiver_num=caller_num).last()

        if last_msg_record is None:  # we never sent/received message from this number, so welcome them and alert our team via email
            client = TwilioRestClient(T4S_TWILIO_SID, T4S_TWILIO_TOKEN)
            send_sms(client,
                     FIRST_TIMER_WELCOME,
                     caller_num,
                     our_twilio_num,
                     campaign=None,
                     task_queue=None,
                     root_msg=None)
            subject = "Unknown user (" + caller_num + ") sent text to the system"
            msg_body = "Someone with phone number: " + caller_num + " texted our system.\n" + \
                        "The text message content was as follow:\n" + msg_content + "\n" + \
                        "Please follow up with him as necessary."
            send_email_notification(subject, msg_body)
            return HttpResponse('OK')
        else:
            last_msg_sent = last_msg_record.message

        task_queue = last_msg_record.task_queue
        campaign = last_msg_record.campaign  # For trigger keyword if user type 'A' vs. 'a', we need to cover this

        # Note: we cannot search twilio num by TwilioAccount.objects.get(number=our_twilio_num)
        # because our_twilio_num has country code and '+' in it
        if task_queue:  # if this reply is in response to an earlier message from us, it has task_queue
            twilio = task_queue.twilio
            client = TwilioRestClient(
                twilio.sid, twilio.token
            )  #(settings.T4S_TWILIO_SID, settings.T4S_TWILIO_TOKEN)

        #our_twilio_num = twilio.number # because URL's "To=" number has "+1"/country code, we'll use it for now to prepare for i18n
        recipient = User.objects.get(username=caller_num)

        # Step 1: Save incoming msg first
        record = MessageRecord(content=msg_content,
                               sender_num=caller_num,
                               receiver_num=our_twilio_num,
                               twilio_msg_sid=sms_sid,
                               campaign=campaign,
                               task_queue=task_queue,
                               prompting_msg=last_msg_sent,
                               is_wizard=False)
        record.save()

        # Step 2: Check if student replied HALT
        matched_obj = re.match('halt.*', msg_content, re.I | re.S)
        user = get_user_role(recipient)
        if matched_obj:
            user.halt = True
            user.save()

            send_sms(client,
                     HALT_REPLY,
                     caller_num,
                     our_twilio_num,
                     campaign=campaign,
                     task_queue=task_queue,
                     root_msg=last_msg_sent)
            return HttpResponse('OK')

        # Step 3: Check if student replied RESUME
        matched_obj = re.match('resume.*', msg_content, re.I | re.S)
        if matched_obj:
            user.halt = False
            user.save()

            send_sms(client,
                     RESUME_REPLY,
                     caller_num,
                     our_twilio_num,
                     campaign=campaign,
                     task_queue=task_queue,
                     root_msg=last_msg_sent)
            return HttpResponse('OK')

        # Step 4: Check if student replied TUTORIAL
        matched_obj = re.match('tutorial.*', msg_content, re.I | re.S)
        if matched_obj:
            tutorial_campaign = Campaign.objects.filter(tutorial=True)
            response = tutorial_campaign.last().root_message.get_full_content()

            send_sms(client,
                     response,
                     caller_num,
                     our_twilio_num,
                     campaign=tutorial_campaign.last(),
                     task_queue=task_queue,
                     root_msg=tutorial_campaign.last().root_message)
            return HttpResponse('OK')

        # Step 5: Check if student replied AID
        matched_obj = re.match('aid.*', msg_content, re.I | re.S)
        if matched_obj:
            try:
                school = User.objects.get(username=caller_num).student.school
            except:
                school = User.objects.get(username=caller_num).advisor.school

            response_msg = school.help_reply

            # IMPORTANT: we must set root_msg so that if they type HELPINFO in the middle of a conversation, we still don't lose track of where they are
            send_sms(client,
                     response_msg,
                     caller_num,
                     our_twilio_num,
                     campaign=campaign,
                     task_queue=task_queue,
                     root_msg=last_msg_sent)
            return HttpResponse('OK')

        # Step 6: Check if student reply CONTACT
        matched_obj = re.match('contact(.*)', msg_content, re.I | re.S)
        if matched_obj:
            try:
                school = User.objects.get(username=caller_num).student.school
            except:
                school = User.objects.get(username=caller_num).advisor.school

            for contact in school.contacts.split(','):
                response_msg = CONTACT_TEXT + caller_num
                send_sms(
                    client,
                    response_msg,
                    contact,
                    our_twilio_num,
                    root_msg=last_msg_sent
                )  # IMPORTANT: we must set root_msg so that if they type HELPINFO in the middle of a conversation, we still don't lose track of where they are
            return HttpResponse('OK')

        # Step 7: Check if student replied BACK
        matched_obj = re.match('back(.*)', msg_content, re.I | re.S)
        is_back_step = False
        if matched_obj and matched_obj.groups():
            is_back_step = True
            try:
                steps = int(matched_obj.group(1).strip())
            except:  # 'back', 'back random 4', etc.
                steps = 1

            for step in range(steps):  # rewind
                option = MessageOption.objects.filter(
                    child_msg=last_msg_sent).first()
                if option:
                    last_msg_sent = option.parent_msg
                else:
                    break

            sent_msgs = MessageRecord.objects.filter(
                message__isnull=False,
                task_queue=last_msg_record.task_queue,
                receiver_num=caller_num).order_by('-id')
            if sent_msgs.count() > steps:
                last_msg_record = sent_msgs[steps]
            else:
                last_msg_record = sent_msgs.last()

        # This used to be here; but I moved it to Step#1 above. See what the side effects are after 8/20/2015
        # record = MessageRecord(content=msg_content, sender_num=caller_num, receiver_num=our_twilio_num, twilio_msg_sid=sms_sid,
        #                    campaign=campaign, task_queue=task_queue, prompting_msg=last_msg_sent, is_wizard=False)
        # record.save()

        # If student replied BACK, handle it
        if is_back_step:
            # send the msg, donot try to match the options
            response = last_msg_sent.get_full_content()
            send_sms(client,
                     response,
                     caller_num,
                     our_twilio_num,
                     campaign=campaign,
                     task_queue=task_queue,
                     root_msg=last_msg_sent)
        else:
            # helpinfo, HELPINFO, Helpinfo (we respond to them say "Here's the number for suicide prevention center: "; QUIT, quit, Quit (then we unsubscribe them from our message stream)
            # BACK, Back, back (will rewind the message to an earlier one)

            # Handle the end of the conversation
            if not last_msg_sent.options.exists():
                send_sms(client,
                         THANKYOU,
                         caller_num,
                         our_twilio_num,
                         campaign=campaign,
                         task_queue=task_queue,
                         root_msg=last_msg_sent)

            else:
                # Check if this user and this campaign has been queued in wizard (i.e. if not wizard.status:); if YES, return OK
                wizard_table_entry = Wizard.objects.filter(campaign=campaign,
                                                           recipient=recipient)
                if wizard_table_entry:
                    wizard = wizard_table_entry.last()
                    if not wizard.status:  # if case is NOT closed
                        # We'll notify researcher (this is such a wasteful use of Twilio texting
                        # and could potentially overwhelm/annoy whoever guards the system; but Tony wants it)
                        notify_msg = "New text in Wizard: participant (" + format_to_phone_num(caller_num) + \
                                     ") in wizard queue replied '" + msg_content + "' for " + \
                                "conversation titled: " + campaign.title
                        to_groups = last_msg_sent.groups.all()
                        send_sms_notification(client,
                                              notify_msg,
                                              to_groups,
                                              our_twilio_num,
                                              campaign=campaign,
                                              task_queue=task_queue,
                                              root_msg=last_msg_sent)

                        return HttpResponse('OK')

                # checking if trigger keyword is nonexistent
                matched_msg_option = []  #None
                user_response = msg_content.strip()

                for option in last_msg_sent.options.all():
                    trigger_keyword = option.trigger_keyword
                    # We will match "YEs" as well as "Yes!!" when researcher only defined "Yes" as keyword
                    # CAUTION: The first clause of checking if trigger != FREERESPONSE is important and must come first as a short-circuit
                    if (trigger_keyword != FREERESPONSE) and (re.match(
                            '^\s*%s.*$' % trigger_keyword, user_response,
                            re.I)):
                        # matched_msg_option = option
                        # we cannot break here because if we have "1" and "10" as keyword,
                        # the latter option will never be detected
                        matched_msg_option.append(option)

                # can't find in regular list of expected options, so trying to match and see if '*' is there
                if not matched_msg_option:
                    for option in last_msg_sent.options.all():
                        if option.trigger_keyword == FREERESPONSE:
                            matched_msg_option.append(option)

                # this means we have more than one match in the regex above; choose the one with longer option
                if len(matched_msg_option) > 1:
                    max_len = max(
                        len(x.trigger_keyword) for x in matched_msg_option)
                    for o in matched_msg_option:
                        if len(o.trigger_keyword) == max_len:
                            matched_msg_option = [o]
                            break

                # If we found a match in keyword, we respond
                if matched_msg_option:
                    matched_msg_option = matched_msg_option[0]
                    # send planned response to user here
                    response = matched_msg_option.child_msg.get_full_content()
                    # TODO: decide if root_msg=matched_msg_option.parent_msg is correct because we are going to be using root_msg as pretty much prompting_msg
                    # TODO: also decide if renaming 'root_msg' to 'prompting_msg' might make sense in smsmessages/utils.py's send_sms method
                    send_sms(client,
                             response,
                             caller_num,
                             our_twilio_num,
                             campaign=campaign,
                             task_queue=task_queue,
                             root_msg=matched_msg_option.child_msg)

                    # Send notifications to advisors/researchers/anyone if it is set up
                    # TODO: refactor
                    notify_msg = "T4S Notification: a participant (" + format_to_phone_num(caller_num) + \
                                 ") replied '" + user_response + "' CORRECTLY to a prompt which expects this keyword: '" + \
                                 trigger_keyword +"' in conversation titled '" + campaign.title + "'. "
                    wizard_msg = "Queued in wizard per your request anyway."
                    if matched_msg_option.wizard:  # this is when researcher wants to queue participant in wizard regardless if his/her response is correct
                        notify_msg += " " + wizard_msg  # step 1. extend the notification message
                        wizard_table_entry = Wizard.objects.filter(
                            campaign=campaign, recipient=recipient)

                        # Since the user explicitly said queue in wizard regardless of response correctness, we do it
                        if not wizard_table_entry:  # step 2. register in the wizard table
                            wizard = Wizard(campaign=campaign,
                                            recipient=recipient,
                                            message=last_msg_sent)
                            wizard.save()
                        else:  # there's an entry in the table for this campaign for this recipient, so make sure we set its case status to "open"
                            wizard = wizard_table_entry.last()
                            wizard.status = False
                            wizard.deleted = False  # if deleted, show it in the table again
                            wizard.save()

                        # step 3. update the 'is_wizard' flag to 'True' in message log
                        record.is_wizard = True
                        record.save()

                    # after taking care of wizard business, send notification to respective folks
                    if matched_msg_option.notify:
                        # I considered using TaskQueue to send messages; But it requires us to
                        # associate a campaign_obj, plus it doesn't allow us to avoid recording
                        # these sent notification in log. Archiving these notification messages will
                        # just add noise in our DB when time comes for data analysis.
                        # So I'm avoiding that and choosing a simpler--albeit a little bit
                        # time consuming--approach that gives us a bit more freedom
                        to_groups = last_msg_sent.groups.all()
                        root_msg = matched_msg_option.parent_msg
                        send_sms_notification(client,
                                              notify_msg,
                                              to_groups,
                                              our_twilio_num,
                                              campaign=campaign,
                                              task_queue=task_queue,
                                              root_msg=None)

                else:  # No match in option/trigger keyword, we tell them we don't understand
                    dont_understand = DONT_UNDERSTAND + "Please reply with "
                    for option in last_msg_sent.options.all():
                        trigger_keyword = option.trigger_keyword
                        if (trigger_keyword != FREERESPONSE):  # not asterik
                            dont_understand += trigger_keyword + " or "

                    dont_understand += "type AID if you need more help."  #dont_understand[:-3] # remove trailing "or "

                    # Step 1: Reply to participant first
                    send_sms(client,
                             dont_understand,
                             caller_num,
                             our_twilio_num,
                             campaign=campaign,
                             task_queue=task_queue,
                             root_msg=last_msg_sent)

                    # Step 2: Make sure to register this conversation instance in messagerecord regardless
                    # (set is_wizard=True for now; not very sure if we need to set it differently in the future)
                    record.is_wizard = True
                    record.save()

                    # Potential TODO based on Tony's new new request
                    # remove Step 3 and (possibly) step 4 below if he agrees; if he still wants notification,
                    # we can change "NOT queued in wizard"

                    # # Step 3: Mandatory queuing of this instance in wizard table because Tony requested it.
                    # # First search participant with specific campaign in the wizard table
                    # wizard_table_entry = Wizard.objects.filter(campaign=campaign, recipient=recipient)
                    #
                    # if wizard_table_entry:          # s/he is already registered in the table for this campaign
                    #     wizard = wizard_table_entry.last()
                    #
                    #     # reopen the case
                    #     if wizard.status:    # if status is True, case closed
                    #         wizard.status = False
                    #         wizard_table_entry.deleted = False      # if deleted, show it in the table again
                    #         wizard.save()
                    #
                    # else: # don't have old wizard table entry; create a new one
                    #     wizard = Wizard(campaign=campaign, recipient=recipient, message=last_msg_sent)
                    #     wizard.save()
                    #
                    # # Step 4: We'll notify researcher (this is such a wasteful use of Twilio texting
                    # # and could potentially annoy whoever guards the system; but Tony wants it)
                    # notify_msg = "T4S Notification: participant (" + format_to_phone_num(caller_num) + \
                    #              ") replied '" + user_response + "', which seems like an unexpected option for " + \
                    #         "conversation titled '" + campaign.title + "'. Queued in wizard."
                    #
                    # to_groups = last_msg_sent.groups.all()
                    # send_sms_notification(client, notify_msg, to_groups, our_twilio_num,
                    #                       campaign=campaign, task_queue=task_queue, root_msg=last_msg_sent)

        # need to know the last message they received
        return HttpResponse('OK')
Пример #6
0
 def get_formatted_phone_number(self):
     return format_to_phone_num(self.number)
Пример #7
0
 def get_formatted_phone_number(self):
     return format_to_phone_num(self.user.username)