modules/utils.py
def keywords_without_word(language, word):
	new_keys = subscribe_keywords(language)
	if word in new_keys:
		new_keys.remove(word)
	return new_keys
modules/i18n.py
def msg_already_sub(language):
    if language == "English":
        return "You are already registered to receive CSH health reminders."
    elif language == "Hindi":
        return u'\u0906\u092a \u092a\u0939\u0932\u0947 \u0938\u0947 \u0939\u0940 \u0938\u0940.\u090f\u0938.\u091a \u0938\u094d\u0935\u093e\u0938\u094d\u0925\u094d\u092f \u0938\u094d\u092e\u0930\u0923 \u092a\u094d\u0930\u093e\u092a\u094d\u0924 \u0915\u0930\u0928\u0947 \u0915\u0947 \u0932\u093f\u090f \u092a\u0902\u091c\u0940\u0915\u0943\u0924 \u0939\u0948\u0902.'
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/i18n.py
def msg_failed_date(language):
    if language == "English":
        return "Sorry, the date format was incorrect. An example message is 'Remind Sai 14-01-17' where 'Sai' is your child's first name and '14-01-17'' is their birthday."
    elif language == "Hindi":
        return u'\u0915\u094d\u0937\u092e\u093e \u0915\u0940\u091c\u093f\u092f\u0947, \u0924\u093e\u0930\u0940\u0916 \u0915\u093e \u092a\u094d\u0930\u093e\u0930\u0942\u092a \u0917\u0932\u0924 \u0939\u0948.'
modules/utils.py
def quote(word):
    return u"`{}`".format(word)
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.'
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/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/utils.py
def add_contact_to_group(contact, group_name):
    group, _ = Group.objects.get_or_create(name=group_name)
    group.contacts.add(contact)
    group.save()
    return group
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/date_helper.py
def date_string_to_date(date_string):
    sep = "-" if "-" in date_string else "/"
    if len(date_string.split(sep)) < 3:
        raise ValueError()
    year = "%Y" if len(date_string.split(sep)[2]) == 4 else "%y"
    pattern = "%d" + sep + "%m" + sep + year
    return datetime.strptime(date_string, pattern).date()
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/i18n.py
def msg_failure(language):
    if language == "English":
        return "Sorry, we didn't understand that message. Text END to " + TEXTLOCAL_PHONENUMBER + " to unsubscribe."
    elif language == "Hindi":
        return u'\u0915\u094d\u0937\u092e\u093e \u0915\u0930\u0947\u0902, \u0939\u092e\u0928\u0947 \u0909\u0938 \u0938\u0902\u0926\u0947\u0936 \u0915\u094b \u0928\u0939\u0940\u0902 \u0938\u092e\u091d\u093e. \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 + ' \u092a\u0930.'
modules/i18n.py
def hindi_born():
    return u'\u091c\u0928\u094d\u092e'
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/date_helper.py
def date_is_valid(date_string):
    try:
        date = date_string_to_date(date_string)
        too_old = date.year < datetime.now().year - 18
        too_young = date.year > datetime.now().year + 2
        return (not too_old and not too_young)
    except ValueError:
        return False
modules/i18n.py
def subscribe_keywords(language):
    if language == "English":
        return ["remind", "join", "born"]
    elif language == "Hindi":
        return [hindi_remind(), hindi_information(), hindi_born()]
    else:
        return []


Question: import logging
import string
from datetime import datetime
from django.utils import timezone
from django.core.exceptions import MultipleObjectsReturned
from management.models import Contact, Group, Message
from modules.texter import Texter
from modules.utils import quote, add_contact_to_group, keywords_without_word
from modules.date_helper import date_is_valid, date_string_to_date
from modules.i18n import msg_subscribe, msg_unsubscribe, msg_placeholder_child, msg_failure, \
                         msg_failed_date, subscribe_keywords, msg_already_sub, hindi_born



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):

Answer:         self.contacts = Contact.objects.filter(phone_number=self.phone_number)