modules/i18n.py
def fourteen_week_reminder_seven_days(language):
    if language == "English":
        return "Thank you for being a responsible mother. {name} is due for their important vaccinations in 7 days. Please do so then."
    elif language == "Hindi":
        return u'{name} \u0915\u0947 \u091f\u0940\u0915\u093e\u0915\u0930\u0923 7 \u0926\u093f\u0928\u094b\u0902 \u092e\u0947\u0902 \u0905\u0935\u0936\u094d\u092f \u0915\u0930\u0935\u093e\u090f\u0901.'
    elif language == "Gujarati":
        return u'\u0a8f\u0a95 \u0a9c\u0ab5 \u0aac\u0abf \u0ab0 \u0aae \u0aa4 \u0ab9\u0acb\u0ab5 \u0aac\u0abf\u0ab2 \u0aa7\u0aa8\u0acd\u0aaf\u0ab5 \u0abf. {name} \u0aa8 \u0ac1\u0a82\u0aed \u0aa6\u0abf\u0ab5\u0ab8\u0aae \u0ac1\u0a82\u0aae\u0ab9\u0aa4\u0acd\u0ab5\u0aaa\u0ac2\u0aa3\u0aa3\u0ab0\u0ab8\u0ac0\u0a95\u0ab0\u0aa3 \u0aa6\u0aa8\u0aaf\u0aa4 \u0a9b\u0ac7. \u0aa4\u0acb \u0a95\u0ac3\u0aaa \u0a95\u0ab0\u0ac0\u0ac1\u0a82\u0aa8\u0ac7\u0aaa\u0a9b\u0ac0 \u0a8f \u0a95\u0ab0\u0a9c\u0acb.'
management/models.py
class Contact(models.Model):
    # Vitals
    name = models.CharField(max_length=50)
    phone_regex = RegexValidator(regex=r'^\+?91?\d{9,15}$',
        message="Phone number must be entered in the format: '+9199999999'. Up to 15 digits allowed.",
        code="Invalid phone_number")
    phone_number = models.CharField(validators=[phone_regex], blank=False,
        max_length=20, default="012345") # validators should be a list
    alt_phone_number = models.CharField(validators=[phone_regex], blank=False,
        max_length=20, default="012345")
    date_of_birth = models.DateField(auto_now=False, auto_now_add=False,
        default=datetime.date.today)
    date_of_sign_up = models.DateField(auto_now=False, auto_now_add=False,
        default=datetime.date.today)
    delay_in_days = models.SmallIntegerField(default=0, blank=True)
    functional_date_of_birth = models.DateField(blank=True,auto_now=False,
        auto_now_add=False, default=datetime.date.today)
    cancelled = models.BooleanField(default=False, blank=False)

    # Personal Info
    gender = models.CharField(max_length=6, blank=True)
    mother_tongue = models.CharField(max_length=50, blank=True)
    state = models.CharField(max_length=50, blank=True)
    division = models.CharField(max_length=50, blank=True)
    district = models.CharField(max_length=50, blank=True)
    city = models.CharField(max_length=50, blank=True)
    monthly_income_rupees = models.IntegerField(blank=True, default=999999)
    religion = models.CharField(max_length=50, blank=True)
    children_previously_vaccinated = models.NullBooleanField()
    not_vaccinated_why = models.CharField(max_length=500, blank=True)
    mother_first_name = models.CharField(max_length=50, blank=True)
    mother_last_name = models.CharField(max_length=50, blank=True)
    
    # Type of Sign Up
    method_of_sign_up = models.CharField(max_length=50, blank=True)
    org_sign_up = models.CharField(max_length=40, blank=True)
    hospital_name = models.CharField(max_length=50, blank=True)
    doctor_name = models.CharField(max_length=30, blank=True)
    preg_signup = models.NullBooleanField(default=False)
    preg_update = models.NullBooleanField(default=False)

    def has_been_born(self):
        today = datetime.date.today()
        diff = today - self.date_of_birth
        return  diff >= datetime.timedelta(0)


    # System Identification
    telerivet_contact_id = models.CharField(max_length=50, blank=True)
    trial_id = models.CharField(max_length=40, blank=True)
    trial_group = models.CharField(max_length=40, blank=True)    


    language_preference = models.CharField(max_length=20,
        default="English", blank=False, null=False)

    # Message References
    preferred_time = models.CharField(max_length=50, blank=True)
    script_selection = models.CharField(max_length=20, blank=True)
    telerivet_sender_phone = models.CharField(max_length=100, blank=True)
    time_created = models.DateField(auto_now=False, auto_now_add=False,
        default=datetime.date.today)
    last_heard_from = models.DateTimeField(auto_now=False, auto_now_add=False, blank=True,
        null=True)
    last_contacted = models.DateTimeField(auto_now=False, auto_now_add=False, blank=True,
        null=True)

    def __str__(self):
        return "%s, %s, %s" % (self.name, self.phone_number, self.date_of_birth)

    class Meta:
        ordering = ('name',)
modules/i18n.py
def six_week_reminder_seven_days(language):
    if language == "English":
        return "{name} has their scheduled vaccination in 7 days. Without this vaccination your child will be vulnerable to deadly diseases."
    elif language == "Hindi":
        return u'7 \u0926\u093f\u0928\u094b\u0902 \u092e\u0947\u0902 {name} \u0915\u093e \u091f\u0940\u0915\u093e\u0915\u0930\u0923 \u0915\u0930\u0935\u093e\u090f\u0901 \u090f\u0935\u0902 \u0916\u093c\u0924\u0930\u0928\u093e\u0915 \u092c\u0940\u092e\u093e\u0930\u093f\u0913\u0902 \u0938\u0947 \u092c\u091a\u093e\u090f\u0901.'
    elif language == "Gujarati":
        return u'{name} \u0aa8 \u0ac1\u0a82\u0aed \u0aa6\u0abf\u0ab5\u0ab8 \u0aae \u0ac1\u0aa8 \u0ac1\u0a82\u0aed \u0aa6\u0abf\u0ab5\u0ab8 \u0aae \u0ac1\u0a82\u0ab0\u0ab8\u0ac0\u0a95\u0ab0\u0aa3 \u0ab8 \u0aa6\u0aa8\u0aa6\u0abf\u0aa4 \u0a9b\u0ac7. \u0a86 \u0ab0\u0ab8\u0ac0\u0a95\u0ab0\u0aa3 \u0ab5\u0a97\u0ab0 \u0aa4\u0aae \u0ab0\u0ac1\u0a82 \u0aac \u0ab3\u0a95 \u0a9c\u0ac0\u0ab5\u0ab2\u0ac7\u0aa3 \u0ab0\u0acb\u0a97\u0acb \u0aae \u0a9f\u0ac7\u0ab8\u0ac1\u0a82\u0ab5\u0ac7\u0abf\u0aa8\u0ab6\u0ac0\u0ab2 \u0ab0\u0ab9\u0ac7\u0ab6\u0ac7.'
modules/i18n.py
def ten_week_reminder_one_day(language):
    if language == "English":
        return "{name} is due for their important vaccinations tomorrow. Please do so then."
    elif language == "Hindi":
        return u'\u091c\u093c\u093f\u092e\u094d\u092e\u0947\u0926\u093e\u0930 \u092e\u093e\u0924\u093e \u0939\u094b\u0928\u0947 \u0915\u0947 \u0932\u093f\u090f \u0906\u092a\u0915\u094b \u092c\u0927\u093e\u0908. 1 \u0926\u093f\u0928 \u092e\u0947\u0902 {name} \u0915\u0947 \u091c\u093c\u0930\u0942\u0930\u0940 \u091f\u0940\u0915\u093e\u0915\u0930\u0923 \u0915\u0947 \u0932\u093f\u090f \u0906\u092a \u0938\u0947 \u092e\u0941\u0932\u093e\u0915\u093c\u093e\u0924 \u0939\u094b\u0917\u0940.'
    elif language == "Gujarati":
        return u'{name} \u0aa8 \u0ac1\u0a82\u0ae7 \u0aa6\u0abf\u0ab5\u0ab8\u0aae \u0ac1\u0a82\u0aae\u0ab9\u0aa4\u0acd\u0ab5\u0aaa\u0ac2\u0aa3\u0aa3\u0ab0\u0ab8\u0ac0\u0a95\u0ab0\u0aa3 \u0aa6\u0aa8\u0aaf\u0aa4 \u0a9b\u0ac7. \u0aa4\u0acb \u0a95\u0ac3\u0aaa \u0a95\u0ab0\u0ac0\u0ac1\u0a82\u0aa8\u0ac7\u0aaa\u0a9b\u0ac0 \u0a8f \u0a95\u0ab0\u0a9c\u0acb.'
modules/i18n.py
def msg_unsubscribe(language):
    if language == "English":
        return "You have been unsubscribed from CSH health reminders."
    elif language == "Hindi":
        return u'\u0906\u092a\u0915\u0940 \u0938\u0926\u0938\u094d\u092f\u0924\u093e \u0938\u092e\u093e\u092a\u094d\u0924 \u0915\u0930 \u0926\u0940 \u0917\u092f\u0940 \u0939\u0948.'
management/models.py
class Group(models.Model):
    """
    List of Pre-existing groups in Telerivet
    -----------------------
    Telerivet Blocked
    Cancelled Contacts
    Cancelled Contacts - English
    Cancelled Contacts - Hindi
    Cancelled Contacts - Text Sign Ups
    Cancelled Contacts - Text Sign Ups - English
    Cancelled Contacts - Text Sign Ups - Hindi
    Cancelled Contacts - Text Sign Ups - undefined
    Cancelled Contacts - undefined
    Everyone - English
    Everyone - Gujarati    Everyone - Hindi
    Everyone - Online Form
    Everyone - Text Default Time
    One Time Sign Up Message 06-02-17
    Online Form
    Online Form - English
    Online Form - English - ENG
    Online Form - English - ENG - Text Default Time
    Online Form - Gujarati
    Online Form - Gujarati - GUJ
    Online Form - Gujarati - GUJ - Text Default Time
    Online Form - Hindi
    Online Form - Hindi - HND
    Online Form - Hindi - HND - Text Default Time
    Sample Contacts
    Text Sign Ups
    Text Sign Ups - English
    Text Sign Ups - English - ENG
    Text Sign Ups - English - ENG - Text Default Time
    Text Sign Ups - Hindi
    """
    name = models.CharField(max_length=100, unique=True)
    contacts = models.ManyToManyField(Contact)

    def __str__(self):
        return self.name

    class Meta:
        ordering = ('name',)
modules/i18n.py
def msg_subscribe(language):
    if language == "English":
        return "{name} has been subscribed to CSH health reminders. Text END to " + TEXTLOCAL_PHONENUMBER + " to unsubscribe."
    elif language == "Hindi":
        return u'{name} \u0938\u0940 \u090f\u0938 \u090f\u091a \u0939\u0947\u0932\u094d\u0925 \u0905\u0928\u0941\u0938\u094d\u092e\u0930\u0928 \u0915\u0947 \u0938\u0926\u0938\u094d\u092f \u0939\u0948\u0902. \u0938\u0926\u0938\u094d\u092f\u0924\u093e \u0930\u0926\u094d\u0926 \u0915\u0930\u0928\u0947 \u0915\u0947 \u0932\u093f\u090f \u0932\u093f\u0916\u0947\u0902 "END" \u0914\u0930 \u092d\u0947\u0902\u091c \u0926\u0947 ' + TEXTLOCAL_PHONENUMBER + u' \u092a\u0930.'
modules/date_helper.py
def date_to_date_string(date):
    return '{d}/{m}/{y}'.format(d = date.day,
                                m = date.month,
                                y = date.year)
modules/text_reminder.py
class TextReminder(object):
    def __init__(self, contact):
        self.contact = contact
        self.child_name = contact.name
        self.date_of_birth = contact.date_of_birth
        self.phone_number = contact.phone_number
        self.language = contact.language_preference
        self.preg_signup = contact.preg_signup
        self.preg_update = contact.preg_update

    # self.get_contact() is preferred to self.contact due to triggering a Django DB refresh.
    def get_contact(self):
        if self.contact:
            self.contact.refresh_from_db()
        return self.contact

    def correct_date_for_reminder(self, years_after_birth=0, months_after_birth=0,
                                  weeks_after_birth=0, days_before_appointment=0):
        time_after_dob = relativedelta(years=years_after_birth,
                                       months=months_after_birth,
                                       weeks=weeks_after_birth)
        time_before_appointment = relativedelta(days=days_before_appointment)
        target_date = (datetime.now() - time_after_dob + time_before_appointment).date()
        return self.date_of_birth == target_date

    def get_reminder_msg(self):
        if self.correct_date_for_reminder(weeks_after_birth=6, days_before_appointment=7):
            reminder = six_week_reminder_seven_days
        elif self.correct_date_for_reminder(weeks_after_birth=6, days_before_appointment=1):
            reminder = six_week_reminder_one_day
        elif self.correct_date_for_reminder(weeks_after_birth=10, days_before_appointment=7):
            reminder = ten_week_reminder_seven_days
        elif self.correct_date_for_reminder(weeks_after_birth=10, days_before_appointment=1):
            reminder = ten_week_reminder_one_day
        elif self.correct_date_for_reminder(weeks_after_birth=14, days_before_appointment=7):
            reminder = fourteen_week_reminder_seven_days
        elif self.correct_date_for_reminder(weeks_after_birth=14, days_before_appointment=1):
            reminder = fourteen_week_reminder_one_day
        elif self.correct_date_for_reminder(months_after_birth=9, days_before_appointment=7):
            reminder = nine_month_reminder_seven_days
        elif self.correct_date_for_reminder(months_after_birth=9, days_before_appointment=1):
            reminder = nine_month_reminder_one_day
        elif self.correct_date_for_reminder(months_after_birth=16, days_before_appointment=7):
            reminder = sixteen_month_reminder_seven_days
        elif self.correct_date_for_reminder(months_after_birth=16, days_before_appointment=1):
            reminder = sixteen_month_reminder_one_day
        elif self.correct_date_for_reminder(years_after_birth=5, days_before_appointment=7):
            reminder = five_year_reminder_seven_days
        elif self.correct_date_for_reminder(years_after_birth=5, days_before_appointment=1):
            reminder = five_year_reminder_one_day
        elif self.preg_signup_check():
            if self.correct_date_for_reminder(weeks_after_birth=2, days_before_appointment=0):
                reminder = verify_pregnant_signup_birthdate
            elif self.correct_date_for_reminder(weeks_after_birth=4, days_before_appointment=0):
                reminder = verify_pregnant_signup_birthdate
            else:
                reminder = None
        else:
            reminder = None
        return reminder(self.language).format(name=self.child_name) if reminder else None

    def why_not_remind_reasons(self):
        reasons = []
        if self.get_contact().cancelled:
            reasons.append("Contact is cancelled.")
        if self.get_reminder_msg() is None:
            reasons.append("Contact has no reminders for today's date.")
        return reasons
        
    def should_remind_today(self):
        return len(self.why_not_remind_reasons()) == 0

    def remind(self):
        if self.should_remind_today():
            logging.info("Sent reminder to " + quote(self.phone_number))
            contact = self.get_contact()
            outgoing_message = Message.objects.create(contact=contact, direction="Outgoing",
                body=self.get_reminder_msg())
            contact.last_contacted = outgoing_message.created_at
            contact.save()
            Texter().send(message=self.get_reminder_msg(),
                          phone_number=self.phone_number)
            outgoing_message.sent_at = datetime.now().replace(tzinfo=timezone.get_default_timezone())
            outgoing_message.save()
            return True
        else:
            return False

    def preg_signup_check(self):
        return True if self.preg_signup and not self.preg_update else False
modules/textlocalwrapper.py
class TextLocal(object):
    def __init__(self, apikey, primary_id, sendername):
        self.apikey = apikey
        self.primary_id = primary_id
        self.sendername = sendername


    def get_all_inboxes(self):
        params = {'apikey': self.apikey}
        inboxes_url = 'https://api.textlocal.in/get_inboxes/?'
        return self.get_url_response(request_url=inboxes_url, params=params)


    def get_primary_inbox(self):
        params = {'apikey': self.apikey, 'inbox_id': self.primary_id}
        messages_url = 'https://api.textlocal.in/get_messages/?'
        return self.get_url_response(request_url=messages_url, params=params)

    def get_api_send_history(self):
        params = {'apikey': self.apikey}
        api_send_history_url = 'https://api.textlocal.in/get_history_api/?'
        return self.get_url_response(request_url=api_send_history_url, params=params)


    def get_url_response(self, request_url, params):
        f = request.urlopen(request_url + parse.urlencode(params))
        return json.loads(f.read().decode('latin1'))


    def get_primary_inbox_messages(self):
        return self.get_primary_inbox()['messages']

    def get_api_send_history_messages(self):
        return self.get_api_send_history()['messages']

    def correct_unicode(self, messages, key_name):
        for message in messages:
            message[key_name] = self.correct_corrupted_unicode_matches(message[key_name])
        return messages

    def correct_corrupted_unicode_matches(self, message):
        fixed_message = []
        for message_part in message.split(' '):
            if all(c in string.hexdigits for c in message_part) and '09' in message_part and len(message_part) >= 4:
                fixed_message.append(''.join([unichr(int(message_part[i:i + 4], 16)) for i in range(0, len(message_part), 4)]))
            else:
                fixed_message.append(message_part)
        return ' '.join(fixed_message)

    def response_unicode_encoder(self, message):
        removed_at = message.replace("@U", "\\u")
        front_substring = removed_at[:6] + "\\u"
        back_substring = removed_at[6:]
        back_substring = "\\u".join(re.findall("....", back_substring))
        return (front_substring + back_substring).encode("utf-8").decode("unicode-escape")
         

    def is_message_new(self, message, date_key_name):
        date_of_message = datetime_string_ymd_to_datetime(message[date_key_name])
        margin = timedelta(hours=24)
        return True if datetime.now().replace(tzinfo=timezone.get_default_timezone()) - margin <= date_of_message else False

    def add_to_num_message_dict(self, num_message_dict, message, message_key_name, date_key_name):
        date_of_message = datetime_string_ymd_to_datetime(message[date_key_name])
        num_message_dict.setdefault(str(message['number']), []).append((message[message_key_name], date_of_message))
        return num_message_dict

    def new_messages_by_number(self):
        all_messages = self.get_primary_inbox_messages()
        corrected_messages = self.correct_unicode(messages=all_messages, key_name="message")
        num_message_dict = {}
        for message in corrected_messages:
            if self.is_message_new(message=message, date_key_name="date"):
                num_message_dict = self.add_to_num_message_dict(num_message_dict=num_message_dict,
                                                                message=message,
                                                                message_key_name="message",
                                                                date_key_name="date")
        return num_message_dict

    def new_api_send_messages_by_number(self):
        all_messages = self.get_api_send_history_messages()
        corrected_messages = self.correct_unicode(messages=all_messages, key_name="content")
        num_message_dict = {}
        for message in corrected_messages:
            if self.is_message_new(message=message, date_key_name="datetime"):
                num_message_dict = self.add_to_num_message_dict(num_message_dict=num_message_dict,
                                                                message=message,
                                                                message_key_name="content",
                                                                date_key_name="datetime")
        return num_message_dict

    def send_message(self, message, phone_numbers):
        send_url = "https://api.textlocal.in/send/?"
        unicode_used = 'false'
        if not isinstance(message, str):
            message = message.encode('utf-8')
        if is_not_ascii(message):
            unicode_used = 'true'
        data = parse.urlencode({'numbers': phone_numbers,
                                'message': message,
                                'sender': self.sendername,
                                'apikey': self.apikey,
                                'unicode': unicode_used})
        data = data.encode('utf-8')
        # Avoid triggering bot errors by setting a user agent
        user_agent = {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.95 Safari/537.36'}
        requester = request.Request(send_url, headers=user_agent)
        f = request.urlopen(requester, data)
        return json.loads(f.read().decode('latin1'))
modules/i18n.py
def six_week_reminder_one_day(language):
    if language == "English":
        return "{name} is due for their important vaccinations tomorrow. Please do so then."
    elif language == "Hindi":
        return u'\u0905\u0917\u0932\u0947 1 \u0926\u093f\u0928 \u092e\u0947\u0902 {name} \u0915\u0940 \u091c\u093c\u0930\u0942\u0930\u0940 \u091f\u0940\u0915\u093e\u0915\u0930\u0923 \u0905\u0935\u0936\u094d\u092f \u0915\u0930\u0935\u093e\u090f\u0901.'
    elif language == "Gujarati":
        return u'{name} \u0aa8 \u0ac1\u0a82\u0ae7 \u0aa6\u0abf\u0ab5\u0ab8\u0aae \u0ac1\u0a82\u0aae\u0ab9\u0aa4\u0acd\u0ab5\u0aaa\u0ac2\u0aa3\u0aa3\u0ab0\u0ab8\u0ac0\u0a95\u0ab0\u0aa3 \u0aa6\u0aa8\u0aaf\u0aa4 \u0a9b\u0ac7. \u0aa4\u0acb \u0a95\u0ac3\u0aaa \u0a95\u0ab0\u0ac0\u0ac1\u0a82\u0aa8\u0ac7\u0aaa\u0a9b\u0ac0 \u0a8f \u0a95\u0ab0\u0a9c\u0acb.'
modules/i18n.py
def verify_pregnant_signup_birthdate(language):
    if language == "English":
        return "You signed up for health reminders while pregnant. If your child has been born, text 'BORN Name DD-MM-YY' to "+ TEXTLOCAL_PHONENUMBER + " to register for  English. An example message is 'BORN Aarav 14-01-17' where 'Aarav' is your child's first name and '14-01-17' is your child's birthday."
    elif language == "Hindi":
        return u'\u0905\u092a\u0928\u0940 \u0917\u0930\u094d\u092d\u093e\u0935\u0938\u094d\u0920\u093e \u0915\u0947 \u0926\u094c\u0930\u093e\u0928 \u0906\u092a\u0928\u0947 \u0938\u094d\u0935\u093e\u0938\u094d\u0925\u094d\u092f \u0938\u094d\u092e\u0930\u0923 \u0915\u0940 \u0938\u0926\u0938\u094d\u092f\u0924\u093e \u0932\u0940 \u0925\u0940. \u092f\u0926\u093f \u0906\u092a\u0915\u093e \u092c\u091a\u094d\u091a\u093e \u091c\u0928\u094d\u092e \u0932\u0947 \u091a\u0942\u0915\u093e \u0939\u0948 \u0924\u094b \u0932\u093f\u0916\u0947 \u201c \u091c\u0928\u094d\u092e \u0928\u093e\u092e DD-MM-YY \u201d \u0914\u0930 \u092d\u0947\u0902\u091c \u0926\u0947 \u201c ' + TEXTLOCAL_PHONENUMBER + u' \u201d \u092a\u0930 \u0939\u093f\u0902\u0926\u0940 \u092e\u0947\u0902 \u092a\u0902\u091c\u0940\u0915\u0930\u0923 \u0915\u0947 \u0932\u093f\u090f.'
modules/i18n.py
def hindi_born():
    return u'\u091c\u0928\u094d\u092e'
modules/texter.py
class Texter(object):
    def read_inbox(self):
        textlocal = TextLocal(apikey=TEXTLOCAL_API, primary_id=TEXTLOCAL_PRIMARY_ID, sendername=TEXTLOCAL_SENDERNAME)
        num_message_dict = textlocal.new_messages_by_number()
        return num_message_dict

    def send(self, message, phone_number):
        textlocal = TextLocal(apikey=TEXTLOCAL_API,
                        primary_id=TEXTLOCAL_PRIMARY_ID,
                        sendername=TEXTLOCAL_SENDERNAME)
        send_status = textlocal.send_message(message=message,
                                            phone_numbers=phone_number)
        return send_status

    def read_api_outbox(self):
        textlocal = TextLocal(apikey=TEXTLOCAL_API, primary_id=TEXTLOCAL_PRIMARY_ID, sendername=TEXTLOCAL_SENDERNAME)
        num_message_dict = textlocal.new_api_send_messages_by_number()
        return num_message_dict
modules/i18n.py
def msg_placeholder_child(language):
    if language == "English":
        return "Your child"
    elif language == "Hindi":
        return hindi_placeholder_name()
    elif language == "Gujarati":
        return gujarati_placeholder_name()
modules/i18n.py
def hindi_remind():
    return u'\u0938\u094d\u092e\u0930\u0923'
modules/i18n.py
def fourteen_week_reminder_one_day(language):
    if language == "English":
        return "Your child is eligible to receive a free course of vaccines. {name} has their scheduled vaccination tomorrow."
    elif language == "Hindi":
        return u'1 \u0926\u093f\u0928 \u092e\u0947\u0902 {name} \u0915\u0940 \u091f\u0940\u0915\u093e\u0915\u0930\u0923 \u0915\u0930\u0935\u093e\u090f\u0901 \u090f\u0935\u0902 \u0916\u093c\u0924\u0930\u0928\u093e\u0915 \u092c\u0940\u092e\u093e\u0930\u093f\u0913\u0902 \u0938\u0947 \u092c\u091a\u093e\u090f\u0901.'
    elif language == "Gujarati":
        return u'\u0aa1\u0ac9\u0a95\u0acd\u0a9f\u0ab0\u0ac9 \u0a8f \u0ae7 \u0aa6\u0abf\u0ab5\u0ab8\u0aae\u0abe\u0a82  {name} \u0aa8\u0ac7 \u0ab0\u0ab8\u0ac0\u0a95\u0ab0\u0aa3 \u0aae\u0abe\u0a9f\u0ac7 \u0ab8\u0ac2\u0a9a\u0ab5\u0acd\u0aaf\u0ac1 \u0a9b\u0ac7.'
management/models.py
class Message(models.Model):
    contact = models.ForeignKey(Contact, on_delete=models.CASCADE, null=True)
    body = models.CharField(max_length=300)

    # Message direction is Incoming or Outgoing
    direction = models.CharField(max_length=10)
    
    is_processed = models.BooleanField(default=False, blank=False)
    created_at = models.DateTimeField(auto_now_add=True)
    received_at = models.DateTimeField(auto_now=False,
                                        auto_now_add=False,
                                        blank=True,
                                        null=True)
    sent_at = models.DateTimeField(auto_now=False,
                                    auto_now_add=False,
                                    blank=True,
                                    null=True)

    def __str__(self):
        return self.body
modules/text_processor.py
class TextProcessor(object):
    def __init__(self, phone_number):
        self.phone_number = phone_number
        self.set_language(default=None)

    def set_language(self, default):
        if self.get_contacts().exists():
            self.language = self.contacts.first().language_preference or default
        else:
            self.language = None

    def get_language(self, language, inferred_language, keyword):
        # Returns inferred language if the keyword is in the subscribe keywords of the inferred langauge,
        #  ignoring "born"
        subscribe_keys_without_born = keywords_without_word(language=inferred_language, word="born")
        if keyword in subscribe_keys_without_born:
            return inferred_language
        return language

    # self.get_contacts() is preferred to self.contact due to triggering a Django DB reload.
    def get_contacts(self):
        self.contacts = Contact.objects.filter(phone_number=self.phone_number)
        return self.contacts


    def create_contact(self, child_name, phone_number, date_of_birth, language, preg_update=False):
        contact = Contact.objects.filter(name=child_name, phone_number=self.phone_number).first()
        if contact:
            if contact.cancelled or preg_update:
                # Update and resubscribe
                contact.cancelled = False
                contact.language_preference = language
                contact.date_of_birth = date_of_birth
                contact.functional_date_of_birth = date_of_birth
                contact.preg_update = preg_update
                contact.save()
                return True
            elif Message.objects.filter(contact=contact,
                                        direction="Outgoing",
                                        body=msg_subscribe(language).format(name=contact.name)).exists():
                # Already exists (error)
                logging.error("Contact for {name} at {phone} was subscribed but already exists!".format(name=child_name, phone=self.phone_number))
                return False

        # Otherwise, create
        update_dict = {"delay_in_days": 0,
                       "language_preference": self.language,
                       "date_of_birth": date_of_birth,
                       "functional_date_of_birth": date_of_birth,
                       "method_of_sign_up": "Text"}
        contact, _ = Contact.objects.update_or_create(name=child_name,
                                                      phone_number=phone_number,
                                                      defaults=update_dict)
        for group_name in ["Text Sign Ups",
                           "Text Sign Ups - " + self.language.title(),
                           "Everyone - " + self.language.title()]:
            add_contact_to_group(contact, group_name)
        self.get_contacts()
        return True


    def cancel_contacts(self):
        for contact in self.contacts:
            contact.cancelled = True
            contact.save()
        return True


    def process_subscribe(self, child_name, date_of_birth, preg_update):        
        if self.create_contact(child_name=child_name,
                               phone_number=self.phone_number,
                               date_of_birth=date_of_birth,
                               language=self.language,
                               preg_update=preg_update):
            return msg_subscribe(self.language).format(name=child_name)
        else:
            return msg_already_sub(self.language)


    def process_unsubscribe(self, child_name, date_of_birth, preg_update=False):
        if self.contacts.exists():
            contact = self.get_contacts().first()
            if contact.name is None or contact.date_of_birth is None or contact.language_preference is None:
                logging.error(quote(self.phone_number) + " asked to be unsubscribed but some data is missing on the existing contact object.")
            self.cancel_contacts()
        else:
            logging.error(quote(self.phone_number) + " asked to be unsubscribed but does not exist.")
        return msg_unsubscribe(self.language or "English")


    def process_failure(self, child_name, date_of_birth, preg_update=False):
        return msg_failure(self.language)


    def process_failed_date(self, child_name, date_of_birth, preg_update=False):
        return msg_failed_date(self.language)


    def get_data_from_message(self, message):
        """Get the keyword, child name, and the date from the message.
            A text will look like `<KEYWORD> <CHILD> <DATE OF BIRTH>`, like
            `REMIND NATHAN 25/11/2015`. Sometimes the child name is omitted."""
        message = message.lower().split(" ")
        if len(message) == 1:
            keyword = message[0]
            date = None
            child_name = None
        elif len(message) == 2:
            keyword, date = message
            child_name = None
        else:
            keyword, child_name, date = message[0:3]
        date = date_string_to_date(date) if date and date_is_valid(date) else None
        return (keyword, child_name, date)

    def write_to_database(self, message, date):
        keyword, child_name, date_entered = self.get_data_from_message(message)
        inferred_language = "Hindi" if keyword and keyword[0] not in string.ascii_lowercase else "English"
        language = self.language or inferred_language

        if language != inferred_language:
            language = self.get_language(language=language,
                                            inferred_language=inferred_language,
                                            keyword=keyword)

        if not child_name and self.get_contacts():
            contact = self.get_contacts().first()
            child_name = contact.name

        if child_name:
            child_name = child_name.title()

        incoming = self.create_message_object(child_name=child_name,
                                              phone_number=self.phone_number,
                                              language=language,
                                              body=message,
                                              direction="Incoming")

        contact = Contact.objects.get(pk=incoming.contact.id)
        contact.last_heard_from = incoming.created_at
        incoming.received_at = date
        incoming.save()
        contact.save()
        self.get_contacts()
        return incoming

    def process(self, message):
        """This is the main function that is run on an message to process it."""
        contact = Contact.objects.get(pk=message.contact.id)
        keyword, child_name, date = self.get_data_from_message(message.body)
        preg_update = False
        if keyword in subscribe_keywords("English"):
            self.set_language(default="English")
            if keyword == "born":
                preg_update = True
            action = self.process_subscribe
        elif keyword in subscribe_keywords("Hindi"):
            self.set_language(default="Hindi")
            if keyword == hindi_born():
                preg_update = True
            action = self.process_subscribe
        elif keyword == "end":
            self.set_language(default="English")
            action = self.process_unsubscribe
        else:
            self.set_language(default="English")
            logging.error("Keyword " + quote(keyword) + " in message " + quote(message.body) +
                          " was not understood by the system.")
            action = self.process_failure

        if action == self.process_subscribe:
            if child_name is None:
                # If a child name is not found, we call them "your child".
                child_name = msg_placeholder_child(self.language)
            else:
                child_name = child_name.title()

            if len(child_name) > 50:
                action = self.process_failure

            if date is None:
                logging.error("Date in message " + quote(message.body) + " is invalid.")
                action = self.process_failed_date

        if action == self.process_subscribe:
            logging.info("Subscribing " + quote(message.body) + "...")
        elif action == self.process_unsubscribe:
            logging.info("Unsubscribing " + quote(contact.phone_number) + "...")

        response_text_message = action(child_name=child_name,
                                       date_of_birth=date,
                                       preg_update=preg_update)

        outgoing = self.create_message_object(child_name=contact.name,
                                              phone_number=contact.phone_number,
                                              language=self.language,
                                              body=response_text_message,
                                              direction="Outgoing")
        contact = Contact.objects.get(pk=outgoing.contact.id)
        contact.last_contacted = outgoing.created_at
        contact.save()
        self.get_contacts()
        Texter().send(message=response_text_message,
                      phone_number=self.phone_number)
        outgoing.is_processed = True
        outgoing.sent_at = datetime.now().replace(tzinfo=timezone.get_default_timezone())
        outgoing.save()
        message.is_processed = True
        message.save()
        return response_text_message

    def create_message_object(self, child_name, phone_number, language, body, direction):
        if not child_name or len(child_name) > 50:
            if not language:
                language = "English"
            child_name = msg_placeholder_child(language)
        try:
            contact, _ = Contact.objects.get_or_create(name=child_name,
                                                       phone_number=phone_number)
        except MultipleObjectsReturned:
            contact = Contact.objects.filter(name=child_name,
                                             phone_number=phone_number).first()

        contact.language_preference = language
        contact.save()
        return Message.objects.create(contact=contact, direction=direction, body=body)
cshsms/settings.py
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
DEBUG = False
ALLOWED_HOSTS = [REMOTE['host']]
INSTALLED_APPS = [
    'management.apps.ManagementConfig',
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'django_crontab'
]
MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
ROOT_URLCONF = 'cshsms.urls'
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]
WSGI_APPLICATION = 'cshsms.wsgi.application'
AUTH_PASSWORD_VALIDATORS = [
    {
        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
    },
]
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'Asia/Kolkata'
USE_I18N = True
USE_L10N = True
USE_TZ = True
STATIC_URL = '/static/'
TEST_RUNNER = 'rainbowtests.test.runner.RainbowDiscoverRunner'
CRONJOBS = [
    ('*/10 * * * *', 'jobs.text_processor_job.check_and_process_registrations'),  # Check for new registrations every 10 min
    ('0 16 * * *', 'jobs.text_reminder_job.remind_all')                           # Remind people daily at 4pm
]
modules/i18n.py
def ten_week_reminder_seven_days(language):
    if language == "English":
        return "{name} is eligible for a free vaccination in 7 days. Without this vaccination your child will be vulnerable to deadly diseases."
    elif language == "Hindi":
        return u'7 \u0926\u093f\u0928\u094b\u0902 \u092e\u0947\u0902 {name} \u0915\u0940 \u0928\u093f\u0903\u0936\u0941\u0932\u094d\u0915 \u091f\u0940\u0915\u093e\u0915\u0930\u0923 \u0915\u0930\u0935\u093e\u090f\u0901 \u090f\u0935\u0902 \u0916\u093c\u0924\u0930\u0928\u093e\u0915 \u092c\u0940\u092e\u093e\u0930\u093f\u0913\u0902 \u0938\u0947 \u092c\u091a\u093e\u090f\u0901.'
    elif language == "Gujarati":
        return u'{name} \u0aed \u0aa6\u0abf\u0ab5\u0ab8\u0aae \u0ac1\u0a82\u0aae\u0aab\u0aa4 \u0ab0\u0ab8\u0ac0\u0a95\u0ab0\u0aa3 \u0aae \u0a9f\u0ac7\u0aaa \u0aa4\u0acd\u0ab0 \u0a9b\u0ac7. \u0a86 \u0ab0\u0ab8\u0ac0\u0a95\u0ab0\u0aa3 \u0ab5\u0a97\u0ab0 \u0aa4\u0aae \u0ab0\u0ac2 \u0aac \u0ab3\u0a95 \u0a9c\u0ac0\u0ab5\u0ab2\u0ac7\u0aa3 \u0ab0\u0acb\u0a97\u0acb \u0aae \u0a9f\u0ac7\u0ab8\u0ac1\u0a82\u0ab5\u0ac7\u0abf\u0aa8\u0ab6\u0ac0\u0ab2 \u0ab0\u0ab9\u0ac7\u0ab6\u0ac7.'


Question: import logging
import time
from datetime import datetime
from dateutil.relativedelta import relativedelta
from django.test import TestCase
from django.utils import timezone
from freezegun import freeze_time
from cshsms.settings import TEXTLOCAL_API, TEXTLOCAL_PRIMARY_ID, TEXTLOCAL_SENDERNAME, \
                            TEXTLOCAL_PHONENUMBER
from management.models import Contact, Group, Message
from modules.texter import Texter
from modules.textlocalwrapper import TextLocal
from modules.text_reminder import TextReminder
from modules.text_processor import TextProcessor
from modules.date_helper import date_to_date_string
from modules.i18n import msg_subscribe, msg_unsubscribe, hindi_remind, \
                         six_week_reminder_one_day, six_week_reminder_seven_days, \
                         ten_week_reminder_seven_days, ten_week_reminder_one_day, \
                         fourteen_week_reminder_seven_days, fourteen_week_reminder_one_day, \
                         msg_placeholder_child, hindi_born, verify_pregnant_signup_birthdate




class TexterGetInboxesTests(TestCase):
    def test_read_inbox(self):
        inbox_dict = Texter().read_inbox()
        self.assertIsInstance(inbox_dict, dict)

    def test_read_api_outbox(self):
        inbox_dict = Texter().read_api_outbox()
        self.assertIsInstance(inbox_dict, dict)

    def test_full_e2e_english_signup_and_cancel_flow(self):
        logging.info("running e2e full flow for sign up + cancel in English...")
        self.run_e2e_flow_for_language(language="English",
                                       person_name="Testjohnson",
                                       join_keyword="JOIN")

    def test_full_e2e_hindi_signup_and_cancel_flow(self):
        logging.info("running e2e full flow for sign up + cancel in Hindi...")
        self.run_e2e_flow_for_language(language="Hindi",
                                       person_name=u'\u0906\u0930\u0935',

Answer:                                        join_keyword=hindi_remind())