<s> import json_field <EOL> import logging <EOL> import uuid <EOL> import uuidfield <EOL> from dimagi . ext . couchdbkit import * <EOL> from datetime import datetime , timedelta <EOL> from django . db import models , transaction <EOL> from collections import namedtuple <EOL> from corehq . apps . app_manager . dbaccessors import get_app <EOL> from corehq . apps . app_manager . models import Form <EOL> from corehq . util . mixin import UUIDGeneratorMixin <EOL> from corehq . apps . users . models import CouchUser <EOL> from casexml . apps . case . models import CommCareCase <EOL> from dimagi . utils . couch . migration import SyncSQLToCouchMixin <EOL> from dimagi . utils . mixins import UnicodeMixIn <EOL> from dimagi . utils . parsing import json_format_datetime <EOL> from corehq . apps . sms . mixin import ( CommCareMobileContactMixin , <EOL> PhoneNumberInUseException , InvalidFormatException , VerifiedNumber , <EOL> apply_leniency , BadSMSConfigException ) <EOL> from corehq . apps . sms import util as smsutil <EOL> from corehq . apps . sms . messages import ( MSG_MOBILE_WORKER_INVITATION_START , <EOL> MSG_MOBILE_WORKER_ANDROID_INVITATION , MSG_MOBILE_WORKER_JAVA_INVITATION , <EOL> get_message ) <EOL> from corehq . util . quickcache import quickcache <EOL> from corehq . util . view_utils import absolute_reverse <EOL> from dimagi . utils . couch . undo import DELETED_SUFFIX <EOL> from dimagi . utils . couch import CouchDocLockableMixIn <EOL> from dimagi . utils . load_balance import load_balance <EOL> from dimagi . utils . logging import notify_exception <EOL> from django . utils . translation import ugettext_noop , ugettext_lazy <EOL> INCOMING = "<STR_LIT:I>" <EOL> OUTGOING = "<STR_LIT:O>" <EOL> CALLBACK_PENDING = "<STR_LIT>" <EOL> CALLBACK_RECEIVED = "<STR_LIT>" <EOL> CALLBACK_MISSED = "<STR_LIT>" <EOL> FORWARD_ALL = "<STR_LIT>" <EOL> FORWARD_BY_KEYWORD = "<STR_LIT>" <EOL> FORWARDING_CHOICES = [ FORWARD_ALL , FORWARD_BY_KEYWORD ] <EOL> WORKFLOW_CALLBACK = "<STR_LIT>" <EOL> WORKFLOW_REMINDER = "<STR_LIT>" <EOL> WORKFLOW_KEYWORD = "<STR_LIT>" <EOL> WORKFLOW_FORWARD = "<STR_LIT>" <EOL> WORKFLOW_BROADCAST = "<STR_LIT>" <EOL> WORKFLOW_PERFORMANCE = "<STR_LIT>" <EOL> WORKFLOW_DEFAULT = '<STR_LIT:default>' <EOL> DIRECTION_CHOICES = ( <EOL> ( INCOMING , "<STR_LIT>" ) , <EOL> ( OUTGOING , "<STR_LIT>" ) ) <EOL> class MessageLog ( SafeSaveDocument , UnicodeMixIn ) : <EOL> base_doc = "<STR_LIT>" <EOL> couch_recipient_doc_type = StringProperty ( ) <EOL> couch_recipient = StringProperty ( ) <EOL> phone_number = StringProperty ( ) <EOL> direction = StringProperty ( ) <EOL> date = DateTimeProperty ( ) <EOL> domain = StringProperty ( ) <EOL> backend_api = StringProperty ( ) <EOL> backend_id = StringProperty ( ) <EOL> billed = BooleanProperty ( default = False ) <EOL> billing_errors = ListProperty ( ) <EOL> chat_user_id = StringProperty ( ) <EOL> workflow = StringProperty ( ) <EOL> xforms_session_couch_id = StringProperty ( ) <EOL> reminder_id = StringProperty ( ) <EOL> processed = BooleanProperty ( default = True ) <EOL> datetime_to_process = DateTimeProperty ( ) <EOL> num_processing_attempts = IntegerProperty ( default = <NUM_LIT:0> ) <EOL> error = BooleanProperty ( default = False ) <EOL> system_error_message = StringProperty ( ) <EOL> domain_scope = StringProperty ( ) <EOL> queued_timestamp = DateTimeProperty ( ) <EOL> processed_timestamp = DateTimeProperty ( ) <EOL> in_reply_to = StringProperty ( ) <EOL> system_phone_number = StringProperty ( ) <EOL> ignore_opt_out = BooleanProperty ( default = False ) <EOL> location_id = StringProperty ( ) <EOL> def __unicode__ ( self ) : <EOL> to_from = ( self . direction == INCOMING ) and "<STR_LIT>" or "<STR_LIT:to>" <EOL> return "<STR_LIT>" % ( to_from , self . phone_number ) <EOL> def set_system_error ( self , message = None ) : <EOL> self . error = True <EOL> self . system_error_message = message <EOL> self . save ( ) <EOL> @ property <EOL> def username ( self ) : <EOL> name = self . phone_number <EOL> if self . couch_recipient : <EOL> try : <EOL> if self . couch_recipient_doc_type == "<STR_LIT>" : <EOL> name = CommCareCase . get ( self . couch_recipient ) . name <EOL> else : <EOL> name = CouchUser . get_by_user_id ( self . couch_recipient ) . username <EOL> except Exception as e : <EOL> pass <EOL> return name <EOL> @ property <EOL> def recipient ( self ) : <EOL> if self . couch_recipient_doc_type == "<STR_LIT>" : <EOL> return CommCareCase . get ( self . couch_recipient ) <EOL> else : <EOL> return CouchUser . get_by_user_id ( self . couch_recipient ) <EOL> @ classmethod <EOL> def by_domain_asc ( cls , domain ) : <EOL> if cls . __name__ == "<STR_LIT>" : <EOL> raise NotImplementedError ( "<STR_LIT>" ) <EOL> return cls . view ( "<STR_LIT>" , <EOL> reduce = False , <EOL> startkey = [ domain , cls . __name__ ] , <EOL> endkey = [ domain , cls . __name__ ] + [ { } ] , <EOL> include_docs = True , <EOL> descending = False ) <EOL> @ classmethod <EOL> def by_domain_dsc ( cls , domain ) : <EOL> if cls . __name__ == "<STR_LIT>" : <EOL> raise NotImplementedError ( "<STR_LIT>" ) <EOL> return cls . view ( "<STR_LIT>" , <EOL> reduce = False , <EOL> startkey = [ domain , cls . __name__ ] + [ { } ] , <EOL> endkey = [ domain , cls . __name__ ] , <EOL> include_docs = True , <EOL> descending = True ) <EOL> @ classmethod <EOL> def count_by_domain ( cls , domain , start_date = None , end_date = { } ) : <EOL> if cls . __name__ == "<STR_LIT>" : <EOL> raise NotImplementedError ( "<STR_LIT>" ) <EOL> if not end_date : <EOL> end_date = { } <EOL> reduced = cls . view ( "<STR_LIT>" , <EOL> startkey = [ domain , cls . __name__ ] + [ start_date ] , <EOL> endkey = [ domain , cls . __name__ ] + [ end_date ] , <EOL> reduce = True ) . all ( ) <EOL> if reduced : <EOL> return reduced [ <NUM_LIT:0> ] [ '<STR_LIT:value>' ] <EOL> return <NUM_LIT:0> <EOL> @ classmethod <EOL> def count_incoming_by_domain ( cls , domain , start_date = None , end_date = { } ) : <EOL> if cls . __name__ == "<STR_LIT>" : <EOL> raise NotImplementedError ( "<STR_LIT>" ) <EOL> if not end_date : <EOL> end_date = { } <EOL> reduced = cls . view ( "<STR_LIT>" , <EOL> startkey = [ domain , cls . __name__ , "<STR_LIT:I>" ] + [ start_date ] , <EOL> endkey = [ domain , cls . __name__ , "<STR_LIT:I>" ] + [ end_date ] , <EOL> reduce = True ) . all ( ) <EOL> if reduced : <EOL> return reduced [ <NUM_LIT:0> ] [ '<STR_LIT:value>' ] <EOL> return <NUM_LIT:0> <EOL> @ classmethod <EOL> def count_outgoing_by_domain ( cls , domain , start_date = None , end_date = { } ) : <EOL> if cls . __name__ == "<STR_LIT>" : <EOL> raise NotImplementedError ( "<STR_LIT>" ) <EOL> if not end_date : <EOL> end_date = { } <EOL> reduced = cls . view ( "<STR_LIT>" , <EOL> startkey = [ domain , cls . __name__ , "<STR_LIT:O>" ] + [ start_date ] , <EOL> endkey = [ domain , cls . __name__ , "<STR_LIT:O>" ] + [ end_date ] , <EOL> reduce = True ) . all ( ) <EOL> if reduced : <EOL> return reduced [ <NUM_LIT:0> ] [ '<STR_LIT:value>' ] <EOL> return <NUM_LIT:0> <EOL> @ classmethod <EOL> def by_domain_date ( cls , domain , start_date = None , end_date = { } ) : <EOL> if cls . __name__ == "<STR_LIT>" : <EOL> raise NotImplementedError ( "<STR_LIT>" ) <EOL> return cls . view ( "<STR_LIT>" , <EOL> reduce = False , <EOL> startkey = [ domain , cls . __name__ ] + [ start_date ] , <EOL> endkey = [ domain , cls . __name__ ] + [ end_date ] , <EOL> include_docs = True ) <EOL> @ classmethod <EOL> def inbound_entry_exists ( cls , contact_doc_type , contact_id , from_timestamp , to_timestamp = None ) : <EOL> """<STR_LIT>""" <EOL> if cls . __name__ == "<STR_LIT>" : <EOL> raise NotImplementedError ( "<STR_LIT>" ) <EOL> from_timestamp_str = json_format_datetime ( from_timestamp ) <EOL> to_timestamp_str = json_format_datetime ( to_timestamp or datetime . utcnow ( ) ) <EOL> reduced = cls . view ( "<STR_LIT>" , <EOL> startkey = [ contact_doc_type , contact_id , cls . __name__ , INCOMING , from_timestamp_str ] , <EOL> endkey = [ contact_doc_type , contact_id , cls . __name__ , INCOMING , to_timestamp_str ] , <EOL> reduce = True ) . all ( ) <EOL> if reduced : <EOL> return ( reduced [ <NUM_LIT:0> ] [ '<STR_LIT:value>' ] > <NUM_LIT:0> ) <EOL> else : <EOL> return False <EOL> class SMSLog ( MessageLog ) : <EOL> text = StringProperty ( ) <EOL> raw_text = StringProperty ( ) <EOL> backend_message_id = StringProperty ( ) <EOL> invalid_survey_response = BooleanProperty ( default = False ) <EOL> messaging_subevent_id = IntegerProperty ( ) <EOL> @ property <EOL> def outbound_backend ( self ) : <EOL> """<STR_LIT>""" <EOL> if self . backend_id : <EOL> return SQLMobileBackend . load ( self . backend_id , is_couch_id = True ) <EOL> else : <EOL> return SQLMobileBackend . load_default_by_phone_and_domain ( <EOL> SQLMobileBackend . SMS , <EOL> smsutil . clean_phone_number ( self . phone_number ) , <EOL> domain = self . domain <EOL> ) <EOL> def __unicode__ ( self ) : <EOL> if len ( self . text ) < <NUM_LIT> : str = self . text <EOL> else : str = "<STR_LIT>" % ( self . text [ <NUM_LIT:0> : <NUM_LIT> ] ) <EOL> to_from = ( self . direction == INCOMING ) and "<STR_LIT>" or "<STR_LIT:to>" <EOL> return "<STR_LIT>" % ( str , to_from , self . phone_number ) <EOL> class Log ( models . Model ) : <EOL> class Meta : <EOL> abstract = True <EOL> app_label = "<STR_LIT>" <EOL> domain = models . CharField ( max_length = <NUM_LIT> , null = True , db_index = True ) <EOL> date = models . DateTimeField ( null = True , db_index = True ) <EOL> couch_recipient_doc_type = models . CharField ( max_length = <NUM_LIT> , null = True , db_index = True ) <EOL> couch_recipient = models . CharField ( max_length = <NUM_LIT> , null = True , db_index = True ) <EOL> phone_number = models . CharField ( max_length = <NUM_LIT> , null = True , db_index = True ) <EOL> direction = models . CharField ( max_length = <NUM_LIT:1> , null = True ) <EOL> error = models . NullBooleanField ( default = False ) <EOL> system_error_message = models . TextField ( null = True ) <EOL> system_phone_number = models . CharField ( max_length = <NUM_LIT> , null = True ) <EOL> backend_api = models . CharField ( max_length = <NUM_LIT> , null = True ) <EOL> backend_id = models . CharField ( max_length = <NUM_LIT> , null = True ) <EOL> billed = models . NullBooleanField ( default = False ) <EOL> workflow = models . CharField ( max_length = <NUM_LIT> , null = True ) <EOL> xforms_session_couch_id = models . CharField ( max_length = <NUM_LIT> , null = True , db_index = True ) <EOL> reminder_id = models . CharField ( max_length = <NUM_LIT> , null = True ) <EOL> location_id = models . CharField ( max_length = <NUM_LIT> , null = True ) <EOL> messaging_subevent = models . ForeignKey ( '<STR_LIT>' , null = True , on_delete = models . PROTECT ) <EOL> def set_system_error ( self , message = None ) : <EOL> self . error = True <EOL> self . system_error_message = message <EOL> self . save ( ) <EOL> @ classmethod <EOL> def by_domain ( cls , domain , start_date = None , end_date = None ) : <EOL> qs = cls . objects . filter ( domain = domain ) <EOL> if start_date : <EOL> qs = qs . filter ( date__gte = start_date ) <EOL> if end_date : <EOL> qs = qs . filter ( date__lte = end_date ) <EOL> return qs <EOL> @ classmethod <EOL> def by_recipient ( cls , contact_doc_type , contact_id ) : <EOL> return cls . objects . filter ( <EOL> couch_recipient_doc_type = contact_doc_type , <EOL> couch_recipient = contact_id , <EOL> ) <EOL> @ classmethod <EOL> def get_last_log_for_recipient ( cls , contact_doc_type , contact_id , direction = None ) : <EOL> qs = cls . by_recipient ( contact_doc_type , contact_id ) <EOL> if direction : <EOL> qs = qs . filter ( direction = direction ) <EOL> qs = qs . order_by ( '<STR_LIT>' ) [ : <NUM_LIT:1> ] <EOL> if qs : <EOL> return qs [ <NUM_LIT:0> ] <EOL> return None <EOL> @ classmethod <EOL> def count_by_domain ( cls , domain , direction = None ) : <EOL> qs = cls . objects . filter ( domain = domain ) <EOL> if direction : <EOL> qs = qs . filter ( direction = direction ) <EOL> return qs . count ( ) <EOL> @ property <EOL> def recipient ( self ) : <EOL> if self . couch_recipient_doc_type == '<STR_LIT>' : <EOL> return CommCareCase . get ( self . couch_recipient ) <EOL> else : <EOL> return CouchUser . get_by_user_id ( self . couch_recipient ) <EOL> @ classmethod <EOL> def inbound_entry_exists ( cls , contact_doc_type , contact_id , from_timestamp , to_timestamp = None ) : <EOL> qs = cls . by_recipient ( <EOL> contact_doc_type , <EOL> contact_id <EOL> ) . filter ( <EOL> direction = INCOMING , <EOL> date__gte = from_timestamp <EOL> ) <EOL> if to_timestamp : <EOL> qs = qs . filter ( <EOL> date__lte = to_timestamp <EOL> ) <EOL> return len ( qs [ : <NUM_LIT:1> ] ) > <NUM_LIT:0> <EOL> class SMSBase ( UUIDGeneratorMixin , Log ) : <EOL> ERROR_TOO_MANY_UNSUCCESSFUL_ATTEMPTS = '<STR_LIT>' <EOL> ERROR_MESSAGE_IS_STALE = '<STR_LIT>' <EOL> ERROR_INVALID_DIRECTION = '<STR_LIT>' <EOL> ERROR_PHONE_NUMBER_OPTED_OUT = '<STR_LIT>' <EOL> ERROR_INVALID_DESTINATION_NUMBER = '<STR_LIT>' <EOL> ERROR_MESSAGE_TOO_LONG = '<STR_LIT>' <EOL> ERROR_MESSAGES = { <EOL> ERROR_TOO_MANY_UNSUCCESSFUL_ATTEMPTS : <EOL> ugettext_noop ( '<STR_LIT>' ) , <EOL> ERROR_MESSAGE_IS_STALE : <EOL> ugettext_noop ( '<STR_LIT>' ) , <EOL> ERROR_INVALID_DIRECTION : <EOL> ugettext_noop ( '<STR_LIT>' ) , <EOL> ERROR_PHONE_NUMBER_OPTED_OUT : <EOL> ugettext_noop ( '<STR_LIT>' ) , <EOL> ERROR_INVALID_DESTINATION_NUMBER : <EOL> ugettext_noop ( "<STR_LIT>" ) , <EOL> ERROR_MESSAGE_TOO_LONG : <EOL> ugettext_noop ( "<STR_LIT>" ) , <EOL> } <EOL> UUIDS_TO_GENERATE = [ '<STR_LIT>' ] <EOL> couch_id = models . CharField ( max_length = <NUM_LIT> , null = True , db_index = True ) <EOL> text = models . TextField ( null = True ) <EOL> raw_text = models . TextField ( null = True ) <EOL> datetime_to_process = models . DateTimeField ( null = True , db_index = True ) <EOL> processed = models . NullBooleanField ( default = True , db_index = True ) <EOL> num_processing_attempts = models . IntegerField ( default = <NUM_LIT:0> , null = True ) <EOL> queued_timestamp = models . DateTimeField ( null = True ) <EOL> processed_timestamp = models . DateTimeField ( null = True ) <EOL> domain_scope = models . CharField ( max_length = <NUM_LIT> , null = True ) <EOL> ignore_opt_out = models . NullBooleanField ( default = False ) <EOL> backend_message_id = models . CharField ( max_length = <NUM_LIT> , null = True ) <EOL> chat_user_id = models . CharField ( max_length = <NUM_LIT> , null = True ) <EOL> invalid_survey_response = models . NullBooleanField ( default = False ) <EOL> """<STR_LIT>""" <EOL> fri_message_bank_lookup_completed = models . NullBooleanField ( default = False ) <EOL> fri_message_bank_message_id = models . CharField ( max_length = <NUM_LIT> , null = True ) <EOL> fri_id = models . CharField ( max_length = <NUM_LIT> , null = True ) <EOL> fri_risk_profile = models . CharField ( max_length = <NUM_LIT:1> , null = True ) <EOL> class Meta : <EOL> abstract = True <EOL> app_label = '<STR_LIT>' <EOL> @ property <EOL> def outbound_backend ( self ) : <EOL> if self . backend_id : <EOL> return SQLMobileBackend . load ( self . backend_id , is_couch_id = True ) <EOL> return SQLMobileBackend . load_default_by_phone_and_domain ( <EOL> SQLMobileBackend . SMS , <EOL> smsutil . clean_phone_number ( self . phone_number ) , <EOL> domain = self . domain <EOL> ) <EOL> class SMS ( SMSBase ) : <EOL> def to_json ( self ) : <EOL> from corehq . apps . sms . serializers import SMSSerializer <EOL> data = SMSSerializer ( self ) . data <EOL> return data <EOL> def publish_change ( self ) : <EOL> from corehq . apps . sms . tasks import publish_sms_change <EOL> publish_sms_change . delay ( self ) <EOL> class QueuedSMS ( SMSBase ) : <EOL> class Meta : <EOL> db_table = '<STR_LIT>' <EOL> @ classmethod <EOL> def get_queued_sms ( cls ) : <EOL> return cls . objects . filter ( <EOL> datetime_to_process__lte = datetime . utcnow ( ) , <EOL> ) <EOL> class LastReadMessage ( Document , CouchDocLockableMixIn ) : <EOL> domain = StringProperty ( ) <EOL> read_by = StringProperty ( ) <EOL> contact_id = StringProperty ( ) <EOL> message_id = StringProperty ( ) <EOL> message_timestamp = DateTimeProperty ( ) <EOL> @ classmethod <EOL> def get_obj ( cls , domain , read_by , contact_id , * args , ** kwargs ) : <EOL> return LastReadMessage . view ( <EOL> "<STR_LIT>" , <EOL> key = [ "<STR_LIT>" , domain , read_by , contact_id ] , <EOL> include_docs = True <EOL> ) . one ( ) <EOL> @ classmethod <EOL> def create_obj ( cls , domain , read_by , contact_id , * args , ** kwargs ) : <EOL> obj = LastReadMessage ( <EOL> domain = domain , <EOL> read_by = read_by , <EOL> contact_id = contact_id <EOL> ) <EOL> obj . save ( ) <EOL> return obj <EOL> @ classmethod <EOL> def by_user ( cls , domain , user_id , contact_id ) : <EOL> return cls . get_obj ( domain , user_id , contact_id ) <EOL> @ classmethod <EOL> def by_anyone ( cls , domain , contact_id ) : <EOL> return LastReadMessage . view ( <EOL> "<STR_LIT>" , <EOL> startkey = [ "<STR_LIT>" , domain , contact_id , { } ] , <EOL> endkey = [ "<STR_LIT>" , domain , contact_id ] , <EOL> descending = True , <EOL> include_docs = True <EOL> ) . first ( ) <EOL> class SQLLastReadMessage ( UUIDGeneratorMixin , models . Model ) : <EOL> class Meta : <EOL> db_table = '<STR_LIT>' <EOL> app_label = '<STR_LIT>' <EOL> index_together = [ <EOL> [ '<STR_LIT>' , '<STR_LIT>' , '<STR_LIT>' ] , <EOL> [ '<STR_LIT>' , '<STR_LIT>' ] , <EOL> ] <EOL> UUIDS_TO_GENERATE = [ '<STR_LIT>' ] <EOL> couch_id = models . CharField ( max_length = <NUM_LIT> , null = True , db_index = True ) <EOL> domain = models . CharField ( max_length = <NUM_LIT> , null = True ) <EOL> read_by = models . CharField ( max_length = <NUM_LIT> , null = True ) <EOL> contact_id = models . CharField ( max_length = <NUM_LIT> , null = True ) <EOL> message_id = models . CharField ( max_length = <NUM_LIT> , null = True ) <EOL> message_timestamp = models . DateTimeField ( null = True ) <EOL> @ classmethod <EOL> def by_anyone ( cls , domain , contact_id ) : <EOL> """<STR_LIT>""" <EOL> result = cls . objects . filter ( <EOL> domain = domain , <EOL> contact_id = contact_id <EOL> ) . order_by ( '<STR_LIT>' ) <EOL> result = result [ : <NUM_LIT:1> ] <EOL> if len ( result ) > <NUM_LIT:0> : <EOL> return result [ <NUM_LIT:0> ] <EOL> return None <EOL> @ classmethod <EOL> def by_user ( cls , domain , user_id , contact_id ) : <EOL> """<STR_LIT>""" <EOL> try : <EOL> return cls . objects . get ( <EOL> domain = domain , <EOL> read_by = user_id , <EOL> contact_id = contact_id <EOL> ) <EOL> except cls . DoesNotExist : <EOL> return None <EOL> class CallLog ( MessageLog ) : <EOL> form_unique_id = StringProperty ( ) <EOL> answered = BooleanProperty ( default = False ) <EOL> duration = IntegerProperty ( ) <EOL> gateway_session_id = StringProperty ( ) <EOL> xforms_session_id = StringProperty ( ) <EOL> error_message = StringProperty ( ) <EOL> submit_partial_form = BooleanProperty ( default = False ) <EOL> include_case_side_effects = BooleanProperty ( default = False ) <EOL> max_question_retries = IntegerProperty ( ) <EOL> current_question_retry_count = IntegerProperty ( default = <NUM_LIT:0> ) <EOL> use_precached_first_response = BooleanProperty ( default = False ) <EOL> first_response = StringProperty ( ) <EOL> case_id = StringProperty ( ) <EOL> case_for_case_submission = BooleanProperty ( default = False ) <EOL> messaging_subevent_id = IntegerProperty ( ) <EOL> def __unicode__ ( self ) : <EOL> to_from = ( self . direction == INCOMING ) and "<STR_LIT>" or "<STR_LIT:to>" <EOL> return "<STR_LIT>" % ( to_from , self . phone_number ) <EOL> @ classmethod <EOL> def answered_call_exists ( cls , caller_doc_type , caller_id , after_timestamp , <EOL> end_timestamp = None ) : <EOL> """<STR_LIT>""" <EOL> start_timestamp = json_format_datetime ( after_timestamp ) <EOL> end_timestamp = json_format_datetime ( end_timestamp or datetime . utcnow ( ) ) <EOL> calls = cls . view ( "<STR_LIT>" , <EOL> startkey = [ caller_doc_type , caller_id , "<STR_LIT>" , OUTGOING , start_timestamp ] , <EOL> endkey = [ caller_doc_type , caller_id , "<STR_LIT>" , OUTGOING , end_timestamp ] , <EOL> reduce = False , <EOL> include_docs = True ) . all ( ) <EOL> result = False <EOL> for call in calls : <EOL> if call . answered : <EOL> result = True <EOL> break <EOL> return result <EOL> @ classmethod <EOL> def get_call_by_gateway_session_id ( cls , gateway_session_id ) : <EOL> """<STR_LIT>""" <EOL> return CallLog . view ( '<STR_LIT>' , <EOL> startkey = [ gateway_session_id , { } ] , <EOL> endkey = [ gateway_session_id ] , <EOL> descending = True , <EOL> include_docs = True , <EOL> limit = <NUM_LIT:1> ) . one ( ) <EOL> class EventLog ( SafeSaveDocument ) : <EOL> base_doc = "<STR_LIT>" <EOL> domain = StringProperty ( ) <EOL> date = DateTimeProperty ( ) <EOL> couch_recipient_doc_type = StringProperty ( ) <EOL> couch_recipient = StringProperty ( ) <EOL> class ExpectedCallbackEventLog ( EventLog ) : <EOL> status = StringProperty ( choices = [ CALLBACK_PENDING , CALLBACK_RECEIVED , CALLBACK_MISSED ] ) <EOL> @ classmethod <EOL> def by_domain ( cls , domain , start_date = None , end_date = { } ) : <EOL> """<STR_LIT>""" <EOL> return cls . view ( "<STR_LIT>" , <EOL> startkey = [ domain , start_date ] , <EOL> endkey = [ domain , end_date ] , <EOL> include_docs = True ) . all ( ) <EOL> class ExpectedCallback ( UUIDGeneratorMixin , models . Model ) : <EOL> class Meta : <EOL> app_label = '<STR_LIT>' <EOL> index_together = [ <EOL> [ '<STR_LIT>' , '<STR_LIT:date>' ] , <EOL> ] <EOL> STATUS_CHOICES = ( <EOL> ( CALLBACK_PENDING , ugettext_lazy ( "<STR_LIT>" ) ) , <EOL> ( CALLBACK_RECEIVED , ugettext_lazy ( "<STR_LIT>" ) ) , <EOL> ( CALLBACK_MISSED , ugettext_lazy ( "<STR_LIT>" ) ) , <EOL> ) <EOL> UUIDS_TO_GENERATE = [ '<STR_LIT>' ] <EOL> couch_id = models . CharField ( max_length = <NUM_LIT> , null = True , db_index = True ) <EOL> domain = models . CharField ( max_length = <NUM_LIT> , null = True , db_index = True ) <EOL> date = models . DateTimeField ( null = True ) <EOL> couch_recipient_doc_type = models . CharField ( max_length = <NUM_LIT> , null = True ) <EOL> couch_recipient = models . CharField ( max_length = <NUM_LIT> , null = True , db_index = True ) <EOL> status = models . CharField ( max_length = <NUM_LIT> , null = True ) <EOL> @ classmethod <EOL> def by_domain ( cls , domain , start_date = None , end_date = None ) : <EOL> qs = cls . objects . filter ( domain = domain ) <EOL> if start_date : <EOL> qs = qs . filter ( date__gte = start_date ) <EOL> if end_date : <EOL> qs = qs . filter ( date__lte = end_date ) <EOL> return qs <EOL> @ classmethod <EOL> def by_domain_recipient_date ( cls , domain , recipient_id , date ) : <EOL> try : <EOL> return cls . objects . get ( <EOL> domain = domain , <EOL> couch_recipient = recipient_id , <EOL> date = date <EOL> ) <EOL> except cls . DoesNotExist : <EOL> return None <EOL> class ForwardingRule ( Document ) : <EOL> domain = StringProperty ( ) <EOL> forward_type = StringProperty ( choices = FORWARDING_CHOICES ) <EOL> keyword = StringProperty ( ) <EOL> backend_id = StringProperty ( ) <EOL> def retire ( self ) : <EOL> self . doc_type += "<STR_LIT>" <EOL> self . save ( ) <EOL> class PhoneBlacklist ( models . Model ) : <EOL> """<STR_LIT>""" <EOL> phone_number = models . CharField ( max_length = <NUM_LIT:30> , unique = True , null = False , db_index = True ) <EOL> send_sms = models . BooleanField ( null = False , default = True ) <EOL> send_ivr = models . BooleanField ( null = False , default = True ) <EOL> can_opt_in = models . BooleanField ( null = False , default = True ) <EOL> class Meta : <EOL> app_label = '<STR_LIT>' <EOL> @ classmethod <EOL> def get_by_phone_number ( cls , phone_number ) : <EOL> phone_number = smsutil . strip_plus ( phone_number ) <EOL> return cls . objects . get ( phone_number = phone_number ) <EOL> @ classmethod <EOL> def get_by_phone_number_or_none ( cls , phone_number ) : <EOL> try : <EOL> return cls . get_by_phone_number ( phone_number ) <EOL> except cls . DoesNotExist : <EOL> return None <EOL> @ classmethod <EOL> def get_or_create ( cls , phone_number ) : <EOL> """<STR_LIT>""" <EOL> phone_number = smsutil . strip_plus ( phone_number ) <EOL> if not phone_number : <EOL> return ( None , False ) <EOL> return cls . objects . get_or_create ( phone_number = phone_number ) <EOL> @ classmethod <EOL> def can_receive_sms ( cls , phone_number ) : <EOL> try : <EOL> phone_obj = cls . get_by_phone_number ( phone_number ) <EOL> return phone_obj . send_sms <EOL> except cls . DoesNotExist : <EOL> return True <EOL> @ classmethod <EOL> def opt_in_sms ( cls , phone_number ) : <EOL> """<STR_LIT>""" <EOL> try : <EOL> phone_obj = cls . get_by_phone_number ( phone_number ) <EOL> if phone_obj . can_opt_in : <EOL> phone_obj . send_sms = True <EOL> phone_obj . save ( ) <EOL> return True <EOL> except cls . DoesNotExist : <EOL> pass <EOL> return False <EOL> @ classmethod <EOL> def opt_out_sms ( cls , phone_number ) : <EOL> """<STR_LIT>""" <EOL> phone_obj = cls . get_or_create ( phone_number ) [ <NUM_LIT:0> ] <EOL> if phone_obj : <EOL> phone_obj . send_sms = False <EOL> phone_obj . save ( ) <EOL> return True <EOL> return False <EOL> class PhoneNumber ( SyncSQLToCouchMixin , models . Model ) : <EOL> couch_id = models . CharField ( max_length = <NUM_LIT> , db_index = True , null = True ) <EOL> domain = models . CharField ( max_length = <NUM_LIT> , db_index = True , null = True ) <EOL> owner_doc_type = models . CharField ( max_length = <NUM_LIT> , null = True ) <EOL> owner_id = models . CharField ( max_length = <NUM_LIT> , db_index = True , null = True ) <EOL> phone_number = models . CharField ( max_length = <NUM_LIT> , db_index = True , null = True ) <EOL> backend_id = models . CharField ( max_length = <NUM_LIT> , null = True ) <EOL> ivr_backend_id = models . CharField ( max_length = <NUM_LIT> , null = True ) <EOL> verified = models . NullBooleanField ( default = False ) <EOL> contact_last_modified = models . DateTimeField ( null = True ) <EOL> @ classmethod <EOL> def _migration_get_fields ( cls ) : <EOL> return [ <EOL> '<STR_LIT>' , <EOL> '<STR_LIT>' , <EOL> '<STR_LIT>' , <EOL> '<STR_LIT>' , <EOL> '<STR_LIT>' , <EOL> '<STR_LIT>' , <EOL> '<STR_LIT>' , <EOL> '<STR_LIT>' <EOL> ] <EOL> @ classmethod <EOL> def _migration_get_couch_model_class ( cls ) : <EOL> return VerifiedNumber <EOL> class MessagingStatusMixin ( object ) : <EOL> def refresh ( self ) : <EOL> return self . __class__ . objects . get ( pk = self . pk ) <EOL> def error ( self , error_code , additional_error_text = None ) : <EOL> self . status = MessagingEvent . STATUS_ERROR <EOL> self . error_code = error_code <EOL> self . additional_error_text = additional_error_text <EOL> self . save ( ) <EOL> def completed ( self ) : <EOL> obj = self . refresh ( ) <EOL> if obj . status != MessagingEvent . STATUS_ERROR : <EOL> obj . status = MessagingEvent . STATUS_COMPLETED <EOL> obj . save ( ) <EOL> return obj <EOL> class MessagingEvent ( models . Model , MessagingStatusMixin ) : <EOL> """<STR_LIT>""" <EOL> STATUS_IN_PROGRESS = '<STR_LIT>' <EOL> STATUS_COMPLETED = '<STR_LIT>' <EOL> STATUS_NOT_COMPLETED = '<STR_LIT>' <EOL> STATUS_ERROR = '<STR_LIT>' <EOL> STATUS_CHOICES = ( <EOL> ( STATUS_IN_PROGRESS , ugettext_noop ( '<STR_LIT>' ) ) , <EOL> ( STATUS_COMPLETED , ugettext_noop ( '<STR_LIT>' ) ) , <EOL> ( STATUS_NOT_COMPLETED , ugettext_noop ( '<STR_LIT>' ) ) , <EOL> ( STATUS_ERROR , ugettext_noop ( '<STR_LIT>' ) ) , <EOL> ) <EOL> SOURCE_BROADCAST = '<STR_LIT>' <EOL> SOURCE_KEYWORD = '<STR_LIT>' <EOL> SOURCE_REMINDER = '<STR_LIT>' <EOL> SOURCE_UNRECOGNIZED = '<STR_LIT>' <EOL> SOURCE_FORWARDED = '<STR_LIT>' <EOL> SOURCE_OTHER = '<STR_LIT>' <EOL> SOURCE_CHOICES = ( <EOL> ( SOURCE_BROADCAST , ugettext_noop ( '<STR_LIT>' ) ) , <EOL> ( SOURCE_KEYWORD , ugettext_noop ( '<STR_LIT>' ) ) , <EOL> ( SOURCE_REMINDER , ugettext_noop ( '<STR_LIT>' ) ) , <EOL> ( SOURCE_UNRECOGNIZED , ugettext_noop ( '<STR_LIT>' ) ) , <EOL> ( SOURCE_FORWARDED , ugettext_noop ( '<STR_LIT>' ) ) , <EOL> ( SOURCE_OTHER , ugettext_noop ( '<STR_LIT>' ) ) , <EOL> ) <EOL> CONTENT_NONE = '<STR_LIT>' <EOL> CONTENT_SMS = '<STR_LIT>' <EOL> CONTENT_SMS_CALLBACK = '<STR_LIT>' <EOL> CONTENT_SMS_SURVEY = '<STR_LIT>' <EOL> CONTENT_IVR_SURVEY = '<STR_LIT>' <EOL> CONTENT_PHONE_VERIFICATION = '<STR_LIT>' <EOL> CONTENT_ADHOC_SMS = '<STR_LIT>' <EOL> CONTENT_API_SMS = '<STR_LIT>' <EOL> CONTENT_CHAT_SMS = '<STR_LIT>' <EOL> CONTENT_EMAIL = '<STR_LIT>' <EOL> CONTENT_CHOICES = ( <EOL> ( CONTENT_NONE , ugettext_noop ( '<STR_LIT:None>' ) ) , <EOL> ( CONTENT_SMS , ugettext_noop ( '<STR_LIT>' ) ) , <EOL> ( CONTENT_SMS_CALLBACK , ugettext_noop ( '<STR_LIT>' ) ) , <EOL> ( CONTENT_SMS_SURVEY , ugettext_noop ( '<STR_LIT>' ) ) , <EOL> ( CONTENT_IVR_SURVEY , ugettext_noop ( '<STR_LIT>' ) ) , <EOL> ( CONTENT_PHONE_VERIFICATION , ugettext_noop ( '<STR_LIT>' ) ) , <EOL> ( CONTENT_ADHOC_SMS , ugettext_noop ( '<STR_LIT>' ) ) , <EOL> ( CONTENT_API_SMS , ugettext_noop ( '<STR_LIT>' ) ) , <EOL> ( CONTENT_CHAT_SMS , ugettext_noop ( '<STR_LIT>' ) ) , <EOL> ( CONTENT_EMAIL , ugettext_noop ( '<STR_LIT>' ) ) , <EOL> ) <EOL> RECIPIENT_CASE = '<STR_LIT>' <EOL> RECIPIENT_MOBILE_WORKER = '<STR_LIT>' <EOL> RECIPIENT_WEB_USER = '<STR_LIT>' <EOL> RECIPIENT_USER_GROUP = '<STR_LIT>' <EOL> RECIPIENT_CASE_GROUP = '<STR_LIT>' <EOL> RECIPIENT_VARIOUS = '<STR_LIT>' <EOL> RECIPIENT_LOCATION = '<STR_LIT>' <EOL> RECIPIENT_LOCATION_PLUS_DESCENDANTS = '<STR_LIT>' <EOL> RECIPIENT_VARIOUS_LOCATIONS = '<STR_LIT>' <EOL> RECIPIENT_VARIOUS_LOCATIONS_PLUS_DESCENDANTS = '<STR_LIT>' <EOL> RECIPIENT_UNKNOWN = '<STR_LIT>' <EOL> RECIPIENT_CHOICES = ( <EOL> ( RECIPIENT_CASE , ugettext_noop ( '<STR_LIT>' ) ) , <EOL> ( RECIPIENT_MOBILE_WORKER , ugettext_noop ( '<STR_LIT>' ) ) , <EOL> ( RECIPIENT_WEB_USER , ugettext_noop ( '<STR_LIT>' ) ) , <EOL> ( RECIPIENT_USER_GROUP , ugettext_noop ( '<STR_LIT>' ) ) , <EOL> ( RECIPIENT_CASE_GROUP , ugettext_noop ( '<STR_LIT>' ) ) , <EOL> ( RECIPIENT_VARIOUS , ugettext_noop ( '<STR_LIT>' ) ) , <EOL> ( RECIPIENT_LOCATION , ugettext_noop ( '<STR_LIT>' ) ) , <EOL> ( RECIPIENT_LOCATION_PLUS_DESCENDANTS , <EOL> ugettext_noop ( '<STR_LIT>' ) ) , <EOL> ( RECIPIENT_VARIOUS_LOCATIONS , ugettext_noop ( '<STR_LIT>' ) ) , <EOL> ( RECIPIENT_VARIOUS_LOCATIONS_PLUS_DESCENDANTS , <EOL> ugettext_noop ( '<STR_LIT>' ) ) , <EOL> ( RECIPIENT_UNKNOWN , ugettext_noop ( '<STR_LIT>' ) ) , <EOL> ) <EOL> ERROR_NO_RECIPIENT = '<STR_LIT>' <EOL> ERROR_CANNOT_RENDER_MESSAGE = '<STR_LIT>' <EOL> ERROR_UNSUPPORTED_COUNTRY = '<STR_LIT>' <EOL> ERROR_NO_PHONE_NUMBER = '<STR_LIT>' <EOL> ERROR_NO_TWO_WAY_PHONE_NUMBER = '<STR_LIT>' <EOL> ERROR_INVALID_CUSTOM_CONTENT_HANDLER = '<STR_LIT>' <EOL> ERROR_CANNOT_LOAD_CUSTOM_CONTENT_HANDLER = '<STR_LIT>' <EOL> ERROR_CANNOT_FIND_FORM = '<STR_LIT>' <EOL> ERROR_FORM_HAS_NO_QUESTIONS = '<STR_LIT>' <EOL> ERROR_CASE_EXTERNAL_ID_NOT_FOUND = '<STR_LIT>' <EOL> ERROR_MULTIPLE_CASES_WITH_EXTERNAL_ID_FOUND = '<STR_LIT>' <EOL> ERROR_NO_CASE_GIVEN = '<STR_LIT>' <EOL> ERROR_NO_EXTERNAL_ID_GIVEN = '<STR_LIT>' <EOL> ERROR_COULD_NOT_PROCESS_STRUCTURED_SMS = '<STR_LIT>' <EOL> ERROR_SUBEVENT_ERROR = '<STR_LIT>' <EOL> ERROR_TOUCHFORMS_ERROR = '<STR_LIT>' <EOL> ERROR_INTERNAL_SERVER_ERROR = '<STR_LIT>' <EOL> ERROR_GATEWAY_ERROR = '<STR_LIT>' <EOL> ERROR_NO_SUITABLE_GATEWAY = '<STR_LIT>' <EOL> ERROR_GATEWAY_NOT_FOUND = '<STR_LIT>' <EOL> ERROR_NO_EMAIL_ADDRESS = '<STR_LIT>' <EOL> ERROR_TRIAL_EMAIL_LIMIT_REACHED = '<STR_LIT>' <EOL> ERROR_MESSAGES = { <EOL> ERROR_NO_RECIPIENT : <EOL> ugettext_noop ( '<STR_LIT>' ) , <EOL> ERROR_CANNOT_RENDER_MESSAGE : <EOL> ugettext_noop ( '<STR_LIT>' ) , <EOL> ERROR_UNSUPPORTED_COUNTRY : <EOL> ugettext_noop ( '<STR_LIT>' ) , <EOL> ERROR_NO_PHONE_NUMBER : <EOL> ugettext_noop ( '<STR_LIT>' ) , <EOL> ERROR_NO_TWO_WAY_PHONE_NUMBER : <EOL> ugettext_noop ( '<STR_LIT>' ) , <EOL> ERROR_INVALID_CUSTOM_CONTENT_HANDLER : <EOL> ugettext_noop ( '<STR_LIT>' ) , <EOL> ERROR_CANNOT_LOAD_CUSTOM_CONTENT_HANDLER : <EOL> ugettext_noop ( '<STR_LIT>' ) , <EOL> ERROR_CANNOT_FIND_FORM : <EOL> ugettext_noop ( '<STR_LIT>' ) , <EOL> ERROR_FORM_HAS_NO_QUESTIONS : <EOL> ugettext_noop ( '<STR_LIT>' <EOL> '<STR_LIT>' <EOL> '<STR_LIT>' ) , <EOL> ERROR_CASE_EXTERNAL_ID_NOT_FOUND : <EOL> ugettext_noop ( '<STR_LIT>' ) , <EOL> ERROR_MULTIPLE_CASES_WITH_EXTERNAL_ID_FOUND : <EOL> ugettext_noop ( '<STR_LIT>' ) , <EOL> ERROR_NO_CASE_GIVEN : <EOL> ugettext_noop ( '<STR_LIT>' ) , <EOL> ERROR_NO_EXTERNAL_ID_GIVEN : <EOL> ugettext_noop ( '<STR_LIT>' ) , <EOL> ERROR_COULD_NOT_PROCESS_STRUCTURED_SMS : <EOL> ugettext_noop ( '<STR_LIT>' ) , <EOL> ERROR_SUBEVENT_ERROR : <EOL> ugettext_noop ( '<STR_LIT>' ) , <EOL> ERROR_TOUCHFORMS_ERROR : <EOL> ugettext_noop ( '<STR_LIT>' ) , <EOL> ERROR_INTERNAL_SERVER_ERROR : <EOL> ugettext_noop ( '<STR_LIT>' ) , <EOL> ERROR_GATEWAY_ERROR : <EOL> ugettext_noop ( '<STR_LIT>' ) , <EOL> ERROR_NO_SUITABLE_GATEWAY : <EOL> ugettext_noop ( '<STR_LIT>' ) , <EOL> ERROR_GATEWAY_NOT_FOUND : <EOL> ugettext_noop ( '<STR_LIT>' ) , <EOL> ERROR_NO_EMAIL_ADDRESS : <EOL> ugettext_noop ( '<STR_LIT>' ) , <EOL> ERROR_TRIAL_EMAIL_LIMIT_REACHED : <EOL> ugettext_noop ( "<STR_LIT>" <EOL> "<STR_LIT>" ) , <EOL> } <EOL> domain = models . CharField ( max_length = <NUM_LIT> , null = False , db_index = True ) <EOL> date = models . DateTimeField ( null = False , db_index = True ) <EOL> source = models . CharField ( max_length = <NUM_LIT:3> , choices = SOURCE_CHOICES , null = False ) <EOL> source_id = models . CharField ( max_length = <NUM_LIT> , null = True ) <EOL> content_type = models . CharField ( max_length = <NUM_LIT:3> , choices = CONTENT_CHOICES , null = False ) <EOL> form_unique_id = models . CharField ( max_length = <NUM_LIT> , null = True ) <EOL> form_name = models . TextField ( null = True ) <EOL> status = models . CharField ( max_length = <NUM_LIT:3> , choices = STATUS_CHOICES , null = False ) <EOL> error_code = models . CharField ( max_length = <NUM_LIT> , null = True ) <EOL> additional_error_text = models . TextField ( null = True ) <EOL> recipient_type = models . CharField ( max_length = <NUM_LIT:3> , choices = RECIPIENT_CHOICES , null = True , db_index = True ) <EOL> recipient_id = models . CharField ( max_length = <NUM_LIT> , null = True , db_index = True ) <EOL> class Meta : <EOL> app_label = '<STR_LIT>' <EOL> @ classmethod <EOL> def get_recipient_type_from_doc_type ( cls , recipient_doc_type ) : <EOL> return { <EOL> '<STR_LIT>' : cls . RECIPIENT_MOBILE_WORKER , <EOL> '<STR_LIT>' : cls . RECIPIENT_WEB_USER , <EOL> '<STR_LIT>' : cls . RECIPIENT_CASE , <EOL> '<STR_LIT>' : cls . RECIPIENT_USER_GROUP , <EOL> '<STR_LIT>' : cls . RECIPIENT_CASE_GROUP , <EOL> } . get ( recipient_doc_type , cls . RECIPIENT_UNKNOWN ) <EOL> @ classmethod <EOL> def get_recipient_type ( cls , recipient ) : <EOL> return cls . get_recipient_type_from_doc_type ( recipient . doc_type ) <EOL> @ classmethod <EOL> def _get_recipient_doc_type ( cls , recipient_type ) : <EOL> return { <EOL> MessagingEvent . RECIPIENT_MOBILE_WORKER : '<STR_LIT>' , <EOL> MessagingEvent . RECIPIENT_WEB_USER : '<STR_LIT>' , <EOL> MessagingEvent . RECIPIENT_CASE : '<STR_LIT>' , <EOL> MessagingEvent . RECIPIENT_USER_GROUP : '<STR_LIT>' , <EOL> MessagingEvent . RECIPIENT_CASE_GROUP : '<STR_LIT>' , <EOL> MessagingEvent . RECIPIENT_LOCATION : '<STR_LIT>' , <EOL> MessagingEvent . RECIPIENT_LOCATION_PLUS_DESCENDANTS : '<STR_LIT>' , <EOL> } . get ( recipient_type , None ) <EOL> def get_recipient_doc_type ( self ) : <EOL> return MessagingEvent . _get_recipient_doc_type ( self . recipient_type ) <EOL> def create_subevent ( self , reminder_definition , reminder , recipient ) : <EOL> recipient_type = MessagingEvent . get_recipient_type ( recipient ) <EOL> content_type , form_unique_id , form_name = self . get_content_info_from_reminder ( <EOL> reminder_definition , reminder , parent = self ) <EOL> obj = MessagingSubEvent . objects . create ( <EOL> parent = self , <EOL> date = datetime . utcnow ( ) , <EOL> recipient_type = recipient_type , <EOL> recipient_id = recipient . get_id if recipient_type else None , <EOL> content_type = content_type , <EOL> form_unique_id = form_unique_id , <EOL> form_name = form_name , <EOL> case_id = reminder . case_id , <EOL> status = MessagingEvent . STATUS_IN_PROGRESS , <EOL> ) <EOL> return obj <EOL> def create_ivr_subevent ( self , recipient , form_unique_id , case_id = None ) : <EOL> recipient_type = MessagingEvent . get_recipient_type ( recipient ) <EOL> obj = MessagingSubEvent . objects . create ( <EOL> parent = self , <EOL> date = datetime . utcnow ( ) , <EOL> recipient_type = recipient_type , <EOL> recipient_id = recipient . get_id if recipient_type else None , <EOL> content_type = MessagingEvent . CONTENT_IVR_SURVEY , <EOL> form_unique_id = form_unique_id , <EOL> form_name = MessagingEvent . get_form_name_or_none ( form_unique_id ) , <EOL> case_id = case_id , <EOL> status = MessagingEvent . STATUS_IN_PROGRESS , <EOL> ) <EOL> return obj <EOL> @ classmethod <EOL> def create_event_for_adhoc_sms ( cls , domain , recipient = None , <EOL> content_type = CONTENT_ADHOC_SMS , source = SOURCE_OTHER ) : <EOL> if recipient : <EOL> recipient_type = cls . get_recipient_type ( recipient ) <EOL> recipient_id = recipient . get_id <EOL> else : <EOL> recipient_type = cls . RECIPIENT_UNKNOWN <EOL> recipient_id = None <EOL> obj = cls . objects . create ( <EOL> domain = domain , <EOL> date = datetime . utcnow ( ) , <EOL> source = source , <EOL> content_type = content_type , <EOL> status = cls . STATUS_IN_PROGRESS , <EOL> recipient_type = recipient_type , <EOL> recipient_id = recipient_id , <EOL> ) <EOL> return obj <EOL> def create_structured_sms_subevent ( self , case_id ) : <EOL> obj = MessagingSubEvent . objects . create ( <EOL> parent = self , <EOL> date = datetime . utcnow ( ) , <EOL> recipient_type = self . recipient_type , <EOL> recipient_id = self . recipient_id , <EOL> content_type = MessagingEvent . CONTENT_SMS_SURVEY , <EOL> form_unique_id = self . form_unique_id , <EOL> form_name = self . form_name , <EOL> case_id = case_id , <EOL> status = MessagingEvent . STATUS_IN_PROGRESS , <EOL> ) <EOL> return obj <EOL> def create_subevent_for_single_sms ( self , recipient_doc_type = None , <EOL> recipient_id = None , case = None , completed = False ) : <EOL> obj = MessagingSubEvent . objects . create ( <EOL> parent = self , <EOL> date = datetime . utcnow ( ) , <EOL> recipient_type = MessagingEvent . get_recipient_type_from_doc_type ( recipient_doc_type ) , <EOL> recipient_id = recipient_id , <EOL> content_type = MessagingEvent . CONTENT_SMS , <EOL> case_id = case . get_id if case else None , <EOL> status = ( MessagingEvent . STATUS_COMPLETED <EOL> if completed <EOL> else MessagingEvent . STATUS_IN_PROGRESS ) , <EOL> ) <EOL> return obj <EOL> @ property <EOL> def subevents ( self ) : <EOL> return self . messagingsubevent_set . all ( ) <EOL> @ classmethod <EOL> def get_source_from_reminder ( cls , reminder_definition ) : <EOL> from corehq . apps . reminders . models import ( REMINDER_TYPE_ONE_TIME , <EOL> REMINDER_TYPE_DEFAULT ) <EOL> default = ( cls . SOURCE_OTHER , None ) <EOL> return { <EOL> REMINDER_TYPE_ONE_TIME : <EOL> ( cls . SOURCE_BROADCAST , reminder_definition . get_id ) , <EOL> REMINDER_TYPE_DEFAULT : <EOL> ( cls . SOURCE_REMINDER , reminder_definition . get_id ) , <EOL> } . get ( reminder_definition . reminder_type , default ) <EOL> @ classmethod <EOL> def get_form_name_or_none ( cls , form_unique_id ) : <EOL> try : <EOL> form = Form . get_form ( form_unique_id ) <EOL> return form . full_path_name <EOL> except : <EOL> return None <EOL> @ classmethod <EOL> def get_content_info_from_reminder ( cls , reminder_definition , reminder , parent = None ) : <EOL> from corehq . apps . reminders . models import ( METHOD_SMS , METHOD_SMS_CALLBACK , <EOL> METHOD_SMS_SURVEY , METHOD_IVR_SURVEY , METHOD_EMAIL ) <EOL> content_type = { <EOL> METHOD_SMS : cls . CONTENT_SMS , <EOL> METHOD_SMS_CALLBACK : cls . CONTENT_SMS_CALLBACK , <EOL> METHOD_SMS_SURVEY : cls . CONTENT_SMS_SURVEY , <EOL> METHOD_IVR_SURVEY : cls . CONTENT_IVR_SURVEY , <EOL> METHOD_EMAIL : cls . CONTENT_EMAIL , <EOL> } . get ( reminder_definition . method , cls . CONTENT_SMS ) <EOL> form_unique_id = reminder . current_event . form_unique_id <EOL> if parent and parent . form_unique_id == form_unique_id : <EOL> form_name = parent . form_name <EOL> else : <EOL> form_name = ( cls . get_form_name_or_none ( form_unique_id ) <EOL> if form_unique_id else None ) <EOL> return ( content_type , form_unique_id , form_name ) <EOL> @ classmethod <EOL> def get_content_info_from_keyword ( cls , keyword ) : <EOL> from corehq . apps . reminders . models import ( METHOD_SMS , METHOD_SMS_SURVEY , <EOL> METHOD_STRUCTURED_SMS , RECIPIENT_SENDER ) <EOL> content_type = cls . CONTENT_NONE <EOL> form_unique_id = None <EOL> form_name = None <EOL> for action in keyword . actions : <EOL> if action . recipient == RECIPIENT_SENDER : <EOL> if action . action in ( METHOD_SMS_SURVEY , METHOD_STRUCTURED_SMS ) : <EOL> content_type = cls . CONTENT_SMS_SURVEY <EOL> form_unique_id = action . form_unique_id <EOL> form_name = cls . get_form_name_or_none ( action . form_unique_id ) <EOL> elif action . action == METHOD_SMS : <EOL> content_type = cls . CONTENT_SMS <EOL> return ( content_type , form_unique_id , form_name ) <EOL> @ classmethod <EOL> def create_from_reminder ( cls , reminder_definition , reminder , recipient = None ) : <EOL> if reminder_definition . messaging_event_id : <EOL> return cls . objects . get ( pk = reminder_definition . messaging_event_id ) <EOL> source , source_id = cls . get_source_from_reminder ( reminder_definition ) <EOL> content_type , form_unique_id , form_name = cls . get_content_info_from_reminder ( <EOL> reminder_definition , reminder ) <EOL> if recipient and reminder_definition . recipient_is_list_of_locations ( recipient ) : <EOL> if len ( recipient ) == <NUM_LIT:1> : <EOL> recipient_type = ( cls . RECIPIENT_LOCATION_PLUS_DESCENDANTS <EOL> if reminder_definition . include_child_locations <EOL> else cls . RECIPIENT_LOCATION ) <EOL> recipient_id = recipient [ <NUM_LIT:0> ] . location_id <EOL> elif len ( recipient ) > <NUM_LIT:1> : <EOL> recipient_type = ( cls . RECIPIENT_VARIOUS_LOCATIONS_PLUS_DESCENDANTS <EOL> if reminder_definition . include_child_locations <EOL> else cls . RECIPIENT_VARIOUS_LOCATIONS ) <EOL> recipient_id = None <EOL> else : <EOL> recipient_type = cls . RECIPIENT_UNKNOWN <EOL> recipient_id = None <EOL> elif isinstance ( recipient , list ) : <EOL> recipient_type = cls . RECIPIENT_VARIOUS <EOL> recipient_id = None <EOL> elif recipient is None : <EOL> recipient_type = cls . RECIPIENT_UNKNOWN <EOL> recipient_id = None <EOL> else : <EOL> recipient_type = cls . get_recipient_type ( recipient ) <EOL> recipient_id = recipient . get_id if recipient_type else None <EOL> return cls . objects . create ( <EOL> domain = reminder_definition . domain , <EOL> date = datetime . utcnow ( ) , <EOL> source = source , <EOL> source_id = source_id , <EOL> content_type = content_type , <EOL> form_unique_id = form_unique_id , <EOL> form_name = form_name , <EOL> status = cls . STATUS_IN_PROGRESS , <EOL> recipient_type = recipient_type , <EOL> recipient_id = recipient_id <EOL> ) <EOL> @ classmethod <EOL> def create_from_keyword ( cls , keyword , contact ) : <EOL> """<STR_LIT>""" <EOL> content_type , form_unique_id , form_name = cls . get_content_info_from_keyword ( <EOL> keyword ) <EOL> recipient_type = cls . get_recipient_type ( contact ) <EOL> return cls . objects . create ( <EOL> domain = keyword . domain , <EOL> date = datetime . utcnow ( ) , <EOL> source = cls . SOURCE_KEYWORD , <EOL> source_id = keyword . get_id , <EOL> content_type = content_type , <EOL> form_unique_id = form_unique_id , <EOL> form_name = form_name , <EOL> status = cls . STATUS_IN_PROGRESS , <EOL> recipient_type = recipient_type , <EOL> recipient_id = contact . get_id if recipient_type else None <EOL> ) <EOL> @ classmethod <EOL> def create_verification_event ( cls , domain , contact ) : <EOL> recipient_type = cls . get_recipient_type ( contact ) <EOL> return cls . objects . create ( <EOL> domain = domain , <EOL> date = datetime . utcnow ( ) , <EOL> source = cls . SOURCE_OTHER , <EOL> content_type = cls . CONTENT_PHONE_VERIFICATION , <EOL> status = cls . STATUS_IN_PROGRESS , <EOL> recipient_type = recipient_type , <EOL> recipient_id = contact . get_id if recipient_type else None <EOL> ) <EOL> @ classmethod <EOL> def get_current_verification_event ( cls , domain , contact_id , phone_number ) : <EOL> """<STR_LIT>""" <EOL> qs = cls . objects . filter ( <EOL> domain = domain , <EOL> recipient_id = contact_id , <EOL> messagingsubevent__sms__phone_number = smsutil . clean_phone_number ( phone_number ) , <EOL> content_type = cls . CONTENT_PHONE_VERIFICATION , <EOL> status = cls . STATUS_IN_PROGRESS <EOL> ) <EOL> return qs . order_by ( '<STR_LIT>' ) [ <NUM_LIT:0> ] if qs . count ( ) > <NUM_LIT:0> else None <EOL> class MessagingSubEvent ( models . Model , MessagingStatusMixin ) : <EOL> """<STR_LIT>""" <EOL> RECIPIENT_CHOICES = ( <EOL> ( MessagingEvent . RECIPIENT_CASE , ugettext_noop ( '<STR_LIT>' ) ) , <EOL> ( MessagingEvent . RECIPIENT_MOBILE_WORKER , ugettext_noop ( '<STR_LIT>' ) ) , <EOL> ( MessagingEvent . RECIPIENT_WEB_USER , ugettext_noop ( '<STR_LIT>' ) ) , <EOL> ) <EOL> parent = models . ForeignKey ( '<STR_LIT>' ) <EOL> date = models . DateTimeField ( null = False , db_index = True ) <EOL> recipient_type = models . CharField ( max_length = <NUM_LIT:3> , choices = RECIPIENT_CHOICES , null = False ) <EOL> recipient_id = models . CharField ( max_length = <NUM_LIT> , null = True ) <EOL> content_type = models . CharField ( max_length = <NUM_LIT:3> , choices = MessagingEvent . CONTENT_CHOICES , null = False ) <EOL> form_unique_id = models . CharField ( max_length = <NUM_LIT> , null = True ) <EOL> form_name = models . TextField ( null = True ) <EOL> xforms_session = models . ForeignKey ( '<STR_LIT>' , null = True , on_delete = models . PROTECT ) <EOL> case_id = models . CharField ( max_length = <NUM_LIT> , null = True ) <EOL> status = models . CharField ( max_length = <NUM_LIT:3> , choices = MessagingEvent . STATUS_CHOICES , null = False ) <EOL> error_code = models . CharField ( max_length = <NUM_LIT> , null = True ) <EOL> additional_error_text = models . TextField ( null = True ) <EOL> class Meta : <EOL> app_label = '<STR_LIT>' <EOL> def save ( self , * args , ** kwargs ) : <EOL> super ( MessagingSubEvent , self ) . save ( * args , ** kwargs ) <EOL> parent = self . parent <EOL> if self . status == MessagingEvent . STATUS_ERROR : <EOL> parent . status = MessagingEvent . STATUS_ERROR <EOL> parent . save ( ) <EOL> if ( parent . source != MessagingEvent . SOURCE_KEYWORD and <EOL> ( parent . recipient_id != self . recipient_id or self . recipient_id is None ) and <EOL> parent . recipient_type not in ( <EOL> MessagingEvent . RECIPIENT_USER_GROUP , <EOL> MessagingEvent . RECIPIENT_CASE_GROUP , <EOL> MessagingEvent . RECIPIENT_VARIOUS , <EOL> MessagingEvent . RECIPIENT_LOCATION , <EOL> MessagingEvent . RECIPIENT_LOCATION_PLUS_DESCENDANTS , <EOL> MessagingEvent . RECIPIENT_VARIOUS_LOCATIONS , <EOL> MessagingEvent . RECIPIENT_VARIOUS_LOCATIONS_PLUS_DESCENDANTS , <EOL> ) and len ( parent . subevents ) > <NUM_LIT:1> ) : <EOL> parent . recipient_type = MessagingEvent . RECIPIENT_VARIOUS <EOL> parent . recipient_id = None <EOL> parent . save ( ) <EOL> def get_recipient_doc_type ( self ) : <EOL> return MessagingEvent . _get_recipient_doc_type ( self . recipient_type ) <EOL> class SelfRegistrationInvitation ( models . Model ) : <EOL> PHONE_TYPE_ANDROID = '<STR_LIT>' <EOL> PHONE_TYPE_OTHER = '<STR_LIT>' <EOL> PHONE_TYPE_CHOICES = ( <EOL> ( PHONE_TYPE_ANDROID , ugettext_lazy ( '<STR_LIT>' ) ) , <EOL> ( PHONE_TYPE_OTHER , ugettext_lazy ( '<STR_LIT>' ) ) , <EOL> ) <EOL> STATUS_PENDING = '<STR_LIT>' <EOL> STATUS_REGISTERED = '<STR_LIT>' <EOL> STATUS_EXPIRED = '<STR_LIT>' <EOL> domain = models . CharField ( max_length = <NUM_LIT> , null = False , db_index = True ) <EOL> phone_number = models . CharField ( max_length = <NUM_LIT:30> , null = False , db_index = True ) <EOL> token = models . CharField ( max_length = <NUM_LIT> , null = False , unique = True , db_index = True ) <EOL> app_id = models . CharField ( max_length = <NUM_LIT> , null = True ) <EOL> expiration_date = models . DateField ( null = False ) <EOL> created_date = models . DateTimeField ( null = False ) <EOL> odk_url = models . CharField ( max_length = <NUM_LIT> , null = True ) <EOL> phone_type = models . CharField ( max_length = <NUM_LIT:20> , null = True , choices = PHONE_TYPE_CHOICES ) <EOL> registered_date = models . DateTimeField ( null = True ) <EOL> class Meta : <EOL> app_label = '<STR_LIT>' <EOL> @ property <EOL> def already_registered ( self ) : <EOL> return self . registered_date is not None <EOL> @ property <EOL> def expired ( self ) : <EOL> """<STR_LIT>""" <EOL> return datetime . utcnow ( ) . date ( ) > self . expiration_date <EOL> @ property <EOL> def status ( self ) : <EOL> if self . already_registered : <EOL> return self . STATUS_REGISTERED <EOL> elif self . expired : <EOL> return self . STATUS_EXPIRED <EOL> else : <EOL> return self . STATUS_PENDING <EOL> def completed ( self ) : <EOL> self . registered_date = datetime . utcnow ( ) <EOL> self . save ( ) <EOL> def send_step1_sms ( self ) : <EOL> from corehq . apps . sms . api import send_sms <EOL> send_sms ( <EOL> self . domain , <EOL> None , <EOL> self . phone_number , <EOL> get_message ( MSG_MOBILE_WORKER_INVITATION_START , domain = self . domain ) <EOL> ) <EOL> def send_step2_java_sms ( self ) : <EOL> from corehq . apps . sms . api import send_sms <EOL> send_sms ( <EOL> self . domain , <EOL> None , <EOL> self . phone_number , <EOL> get_message ( MSG_MOBILE_WORKER_JAVA_INVITATION , context = ( self . domain , ) , domain = self . domain ) <EOL> ) <EOL> def send_step2_android_sms ( self ) : <EOL> from corehq . apps . sms . api import send_sms <EOL> from corehq . apps . sms . views import InvitationAppInfoView <EOL> from corehq . apps . users . views . mobile . users import CommCareUserSelfRegistrationView <EOL> registration_url = absolute_reverse ( CommCareUserSelfRegistrationView . urlname , <EOL> args = [ self . domain , self . token ] ) <EOL> send_sms ( <EOL> self . domain , <EOL> None , <EOL> self . phone_number , <EOL> get_message ( MSG_MOBILE_WORKER_ANDROID_INVITATION , context = ( registration_url , ) , domain = self . domain ) <EOL> ) <EOL> """<STR_LIT>""" <EOL> def expire ( self ) : <EOL> self . expiration_date = datetime . utcnow ( ) . date ( ) - timedelta ( days = <NUM_LIT:1> ) <EOL> self . save ( ) <EOL> @ classmethod <EOL> def get_unexpired_invitations ( cls , phone_number ) : <EOL> current_date = datetime . utcnow ( ) . date ( ) <EOL> return cls . objects . filter ( <EOL> phone_number = phone_number , <EOL> expiration_date__gte = current_date , <EOL> registered_date__isnull = True <EOL> ) <EOL> @ classmethod <EOL> def expire_invitations ( cls , phone_number ) : <EOL> """<STR_LIT>""" <EOL> for invitation in cls . get_unexpired_invitations ( phone_number ) : <EOL> invitation . expire ( ) <EOL> @ classmethod <EOL> def by_token ( cls , token ) : <EOL> try : <EOL> return cls . objects . get ( token = token ) <EOL> except cls . DoesNotExist : <EOL> return None <EOL> @ classmethod <EOL> def by_phone ( cls , phone_number , expire_duplicates = True ) : <EOL> """<STR_LIT>""" <EOL> phone_number = apply_leniency ( phone_number ) <EOL> result = cls . get_unexpired_invitations ( phone_number ) . order_by ( '<STR_LIT>' ) <EOL> if len ( result ) == <NUM_LIT:0> : <EOL> return None <EOL> invitation = result [ <NUM_LIT:0> ] <EOL> if expire_duplicates and len ( result ) > <NUM_LIT:1> : <EOL> for i in result [ <NUM_LIT:1> : ] : <EOL> i . expire ( ) <EOL> return invitation <EOL> @ classmethod <EOL> def get_app_odk_url ( cls , domain , app_id ) : <EOL> """<STR_LIT>""" <EOL> app = get_app ( domain , app_id , latest = True ) <EOL> if not app . copy_of : <EOL> app = get_app ( domain , app_id , latest = True , target = '<STR_LIT>' ) <EOL> if not app . copy_of : <EOL> return None <EOL> return app . get_short_odk_url ( with_media = True ) <EOL> @ classmethod <EOL> def initiate_workflow ( cls , domain , phone_numbers , app_id = None , <EOL> days_until_expiration = <NUM_LIT:30> ) : <EOL> """<STR_LIT>""" <EOL> success_numbers = [ ] <EOL> invalid_format_numbers = [ ] <EOL> numbers_in_use = [ ] <EOL> odk_url = None <EOL> if app_id : <EOL> odk_url = cls . get_app_odk_url ( domain , app_id ) <EOL> for phone_number in phone_numbers : <EOL> phone_number = apply_leniency ( phone_number ) <EOL> try : <EOL> CommCareMobileContactMixin . validate_number_format ( phone_number ) <EOL> except InvalidFormatException : <EOL> invalid_format_numbers . append ( phone_number ) <EOL> continue <EOL> if VerifiedNumber . by_phone ( phone_number , include_pending = True ) : <EOL> numbers_in_use . append ( phone_number ) <EOL> continue <EOL> cls . expire_invitations ( phone_number ) <EOL> expiration_date = ( datetime . utcnow ( ) . date ( ) + <EOL> timedelta ( days = days_until_expiration ) ) <EOL> invitation = cls . objects . create ( <EOL> domain = domain , <EOL> phone_number = phone_number , <EOL> token = uuid . uuid4 ( ) . hex , <EOL> app_id = app_id , <EOL> expiration_date = expiration_date , <EOL> created_date = datetime . utcnow ( ) , <EOL> odk_url = odk_url , <EOL> ) <EOL> invitation . send_step1_sms ( ) <EOL> success_numbers . append ( phone_number ) <EOL> return ( success_numbers , invalid_format_numbers , numbers_in_use ) <EOL> class ActiveMobileBackendManager ( models . Manager ) : <EOL> def get_queryset ( self ) : <EOL> return super ( ActiveMobileBackendManager , self ) . get_queryset ( ) . filter ( deleted = False ) <EOL> class SQLMobileBackend ( UUIDGeneratorMixin , models . Model ) : <EOL> SMS = '<STR_LIT>' <EOL> IVR = '<STR_LIT>' <EOL> TYPE_CHOICES = ( <EOL> ( SMS , ugettext_lazy ( '<STR_LIT>' ) ) , <EOL> ( IVR , ugettext_lazy ( '<STR_LIT>' ) ) , <EOL> ) <EOL> UUIDS_TO_GENERATE = [ '<STR_LIT>' , '<STR_LIT>' ] <EOL> objects = models . Manager ( ) <EOL> active_objects = ActiveMobileBackendManager ( ) <EOL> couch_id = models . CharField ( max_length = <NUM_LIT> , db_index = True , unique = True ) <EOL> backend_type = models . CharField ( max_length = <NUM_LIT:3> , choices = TYPE_CHOICES , default = SMS ) <EOL> inbound_api_key = models . CharField ( max_length = <NUM_LIT> , unique = True , db_index = True ) <EOL> hq_api_id = models . CharField ( max_length = <NUM_LIT> , null = True ) <EOL> is_global = models . BooleanField ( default = False ) <EOL> domain = models . CharField ( max_length = <NUM_LIT> , null = True , db_index = True ) <EOL> name = models . CharField ( max_length = <NUM_LIT> ) <EOL> display_name = models . CharField ( max_length = <NUM_LIT> , null = True ) <EOL> description = models . TextField ( null = True ) <EOL> supported_countries = json_field . JSONField ( default = [ ] ) <EOL> extra_fields = json_field . JSONField ( default = { } ) <EOL> deleted = models . BooleanField ( default = False ) <EOL> load_balancing_numbers = json_field . JSONField ( default = [ ] ) <EOL> reply_to_phone_number = models . CharField ( max_length = <NUM_LIT> , null = True ) <EOL> class Meta : <EOL> db_table = '<STR_LIT>' <EOL> app_label = '<STR_LIT>' <EOL> @ quickcache ( [ '<STR_LIT>' , '<STR_LIT>' ] , timeout = <NUM_LIT:5> * <NUM_LIT> ) <EOL> def domain_is_shared ( self , domain ) : <EOL> """<STR_LIT>""" <EOL> count = self . mobilebackendinvitation_set . filter ( domain = domain , accepted = True ) . count ( ) <EOL> return count > <NUM_LIT:0> <EOL> def domain_is_authorized ( self , domain ) : <EOL> """<STR_LIT>""" <EOL> return ( self . is_global or <EOL> domain == self . domain or <EOL> self . domain_is_shared ( domain ) ) <EOL> @ classmethod <EOL> def name_is_unique ( cls , name , domain = None , backend_id = None ) : <EOL> if domain : <EOL> result = cls . objects . filter ( <EOL> is_global = False , <EOL> domain = domain , <EOL> name = name , <EOL> deleted = False <EOL> ) <EOL> else : <EOL> result = cls . objects . filter ( <EOL> is_global = True , <EOL> name = name , <EOL> deleted = False <EOL> ) <EOL> result = result . values_list ( '<STR_LIT:id>' , flat = True ) <EOL> if len ( result ) == <NUM_LIT:0> : <EOL> return True <EOL> if len ( result ) == <NUM_LIT:1> : <EOL> return result [ <NUM_LIT:0> ] == backend_id <EOL> return False <EOL> def get_authorized_domain_list ( self ) : <EOL> return ( self . mobilebackendinvitation_set . filter ( accepted = True ) <EOL> . order_by ( '<STR_LIT>' ) . values_list ( '<STR_LIT>' , flat = True ) ) <EOL> @ classmethod <EOL> def get_domain_backends ( cls , backend_type , domain , count_only = False , offset = None , limit = None ) : <EOL> """<STR_LIT>""" <EOL> domain_owned_backends = models . Q ( is_global = False , domain = domain ) <EOL> domain_shared_backends = models . Q ( <EOL> is_global = False , <EOL> mobilebackendinvitation__domain = domain , <EOL> mobilebackendinvitation__accepted = True <EOL> ) <EOL> global_backends = models . Q ( is_global = True ) <EOL> result = SQLMobileBackend . objects . filter ( <EOL> ( domain_owned_backends | domain_shared_backends | global_backends ) , <EOL> deleted = False , <EOL> backend_type = backend_type <EOL> ) . distinct ( ) <EOL> if count_only : <EOL> return result . count ( ) <EOL> result = result . order_by ( '<STR_LIT:name>' ) . values_list ( '<STR_LIT:id>' , flat = True ) <EOL> if offset is not None and limit is not None : <EOL> result = result [ offset : offset + limit ] <EOL> return [ cls . load ( pk ) for pk in result ] <EOL> @ classmethod <EOL> def get_global_backends_for_this_class ( cls , backend_type ) : <EOL> return cls . objects . filter ( <EOL> is_global = True , <EOL> deleted = False , <EOL> backend_type = backend_type , <EOL> hq_api_id = cls . get_api_id ( ) <EOL> ) . all ( ) <EOL> @ classmethod <EOL> def get_global_backend_ids ( cls , backend_type , couch_id = False ) : <EOL> id_field = '<STR_LIT>' if couch_id else '<STR_LIT:id>' <EOL> return SQLMobileBackend . active_objects . filter ( <EOL> backend_type = backend_type , <EOL> is_global = True <EOL> ) . values_list ( id_field , flat = True ) <EOL> @ classmethod <EOL> def get_global_backends ( cls , backend_type , count_only = False , offset = None , limit = None ) : <EOL> result = SQLMobileBackend . objects . filter ( <EOL> is_global = True , <EOL> deleted = False , <EOL> backend_type = backend_type <EOL> ) <EOL> if count_only : <EOL> return result . count ( ) <EOL> result = result . order_by ( '<STR_LIT:name>' ) . values_list ( '<STR_LIT:id>' , flat = True ) <EOL> if offset is not None and limit is not None : <EOL> result = result [ offset : offset + limit ] <EOL> return [ cls . load ( pk ) for pk in result ] <EOL> @ classmethod <EOL> def get_domain_default_backend ( cls , backend_type , domain , id_only = False ) : <EOL> result = SQLMobileBackendMapping . objects . filter ( <EOL> is_global = False , <EOL> domain = domain , <EOL> backend_type = backend_type , <EOL> prefix = '<STR_LIT:*>' <EOL> ) . values_list ( '<STR_LIT>' , flat = True ) <EOL> if len ( result ) > <NUM_LIT:1> : <EOL> raise cls . MultipleObjectsReturned ( <EOL> "<STR_LIT>" <EOL> "<STR_LIT>" % ( backend_type , domain ) <EOL> ) <EOL> elif len ( result ) == <NUM_LIT:1> : <EOL> if id_only : <EOL> return result [ <NUM_LIT:0> ] <EOL> else : <EOL> return cls . load ( result [ <NUM_LIT:0> ] ) <EOL> else : <EOL> return None <EOL> @ classmethod <EOL> def load_default_backend ( cls , backend_type , phone_number , domain = None ) : <EOL> """<STR_LIT>""" <EOL> backend_map = SQLMobileBackendMapping . get_prefix_to_backend_map ( <EOL> backend_type , domain = domain ) <EOL> backend_id = backend_map . get_backend_id_by_prefix ( phone_number ) <EOL> if backend_id : <EOL> return cls . load ( backend_id ) <EOL> return None <EOL> @ classmethod <EOL> def load_default_by_phone_and_domain ( cls , backend_type , phone_number , domain = None ) : <EOL> """<STR_LIT>""" <EOL> backend = None <EOL> if domain : <EOL> backend = cls . load_default_backend ( backend_type , phone_number , domain = domain ) <EOL> if not backend : <EOL> backend = cls . load_default_backend ( backend_type , phone_number ) <EOL> if not backend : <EOL> raise BadSMSConfigException ( "<STR_LIT>" <EOL> "<STR_LIT>" % <EOL> ( phone_number , domain ) ) <EOL> return backend <EOL> @ classmethod <EOL> @ quickcache ( [ '<STR_LIT>' , '<STR_LIT>' ] , timeout = <NUM_LIT> * <NUM_LIT> ) <EOL> def get_backend_info_by_api_key ( cls , hq_api_id , inbound_api_key ) : <EOL> """<STR_LIT>""" <EOL> result = ( cls . active_objects <EOL> . filter ( hq_api_id = hq_api_id , inbound_api_key = inbound_api_key ) <EOL> . values_list ( '<STR_LIT>' , '<STR_LIT>' ) ) <EOL> if len ( result ) == <NUM_LIT:0> : <EOL> raise cls . DoesNotExist <EOL> return result [ <NUM_LIT:0> ] <EOL> @ classmethod <EOL> @ quickcache ( [ '<STR_LIT>' , '<STR_LIT>' ] , timeout = <NUM_LIT> * <NUM_LIT> ) <EOL> def get_backend_api_id ( cls , backend_id , is_couch_id = False ) : <EOL> filter_args = { '<STR_LIT>' : backend_id } if is_couch_id else { '<STR_LIT>' : backend_id } <EOL> result = ( cls . active_objects <EOL> . filter ( ** filter_args ) <EOL> . values_list ( '<STR_LIT>' , flat = True ) ) <EOL> if len ( result ) == <NUM_LIT:0> : <EOL> raise cls . DoesNotExist <EOL> return result [ <NUM_LIT:0> ] <EOL> @ classmethod <EOL> @ quickcache ( [ '<STR_LIT>' , '<STR_LIT>' , '<STR_LIT>' ] , timeout = <NUM_LIT:5> * <NUM_LIT> ) <EOL> def load ( cls , backend_id , api_id = None , is_couch_id = False , include_deleted = False ) : <EOL> """<STR_LIT>""" <EOL> backend_classes = smsutil . get_backend_classes ( ) <EOL> api_id = api_id or cls . get_backend_api_id ( backend_id , is_couch_id = is_couch_id ) <EOL> if api_id not in backend_classes : <EOL> raise BadSMSConfigException ( "<STR_LIT>" <EOL> "<STR_LIT>" % ( api_id , backend_id ) ) <EOL> klass = backend_classes [ api_id ] <EOL> if include_deleted : <EOL> result = klass . objects <EOL> else : <EOL> result = klass . active_objects <EOL> if is_couch_id : <EOL> return result . get ( couch_id = backend_id ) <EOL> else : <EOL> return result . get ( pk = backend_id ) <EOL> @ classmethod <EOL> def get_backend_from_id_and_api_id_result ( cls , result ) : <EOL> if len ( result ) > <NUM_LIT:0> : <EOL> return cls . load ( result [ <NUM_LIT:0> ] [ '<STR_LIT:id>' ] , api_id = result [ <NUM_LIT:0> ] [ '<STR_LIT>' ] ) <EOL> return None <EOL> @ classmethod <EOL> def get_owned_backend_by_name ( cls , backend_type , domain , name ) : <EOL> name = name . strip ( ) . upper ( ) <EOL> result = cls . active_objects . filter ( <EOL> is_global = False , <EOL> backend_type = backend_type , <EOL> domain = domain , <EOL> name = name <EOL> ) . values ( '<STR_LIT:id>' , '<STR_LIT>' ) <EOL> return cls . get_backend_from_id_and_api_id_result ( result ) <EOL> @ classmethod <EOL> def get_shared_backend_by_name ( cls , backend_type , domain , name ) : <EOL> name = name . strip ( ) . upper ( ) <EOL> result = cls . active_objects . filter ( <EOL> is_global = False , <EOL> backend_type = backend_type , <EOL> mobilebackendinvitation__domain = domain , <EOL> mobilebackendinvitation__accepted = True , <EOL> name = name <EOL> ) . values ( '<STR_LIT:id>' , '<STR_LIT>' ) . order_by ( '<STR_LIT>' ) <EOL> return cls . get_backend_from_id_and_api_id_result ( result ) <EOL> @ classmethod <EOL> def get_global_backend_by_name ( cls , backend_type , name ) : <EOL> name = name . strip ( ) . upper ( ) <EOL> result = cls . active_objects . filter ( <EOL> is_global = True , <EOL> backend_type = backend_type , <EOL> name = name <EOL> ) . values ( '<STR_LIT:id>' , '<STR_LIT>' ) <EOL> return cls . get_backend_from_id_and_api_id_result ( result ) <EOL> @ classmethod <EOL> def load_by_name ( cls , backend_type , domain , name ) : <EOL> """<STR_LIT>""" <EOL> backend = cls . get_owned_backend_by_name ( backend_type , domain , name ) <EOL> if not backend : <EOL> backend = cls . get_shared_backend_by_name ( backend_type , domain , name ) <EOL> if not backend : <EOL> backend = cls . get_global_backend_by_name ( backend_type , name ) <EOL> if not backend : <EOL> raise BadSMSConfigException ( "<STR_LIT>" <EOL> "<STR_LIT>" % ( backend_type , name , domain ) ) <EOL> return backend <EOL> @ classmethod <EOL> def get_api_id ( cls ) : <EOL> """<STR_LIT>""" <EOL> raise NotImplementedError ( "<STR_LIT>" ) <EOL> @ classmethod <EOL> def get_generic_name ( cls ) : <EOL> """<STR_LIT>""" <EOL> raise NotImplementedError ( "<STR_LIT>" ) <EOL> @ classmethod <EOL> def get_form_class ( cls ) : <EOL> """<STR_LIT>""" <EOL> raise NotImplementedError ( "<STR_LIT>" ) <EOL> @ classmethod <EOL> def get_available_extra_fields ( cls ) : <EOL> """<STR_LIT>""" <EOL> raise NotImplementedError ( "<STR_LIT>" ) <EOL> @ property <EOL> def config ( self ) : <EOL> """<STR_LIT>""" <EOL> BackendConfig = namedtuple ( '<STR_LIT>' , self . get_available_extra_fields ( ) ) <EOL> return BackendConfig ( ** self . get_extra_fields ( ) ) <EOL> def get_extra_fields ( self ) : <EOL> result = { field : None for field in self . get_available_extra_fields ( ) } <EOL> result . update ( self . extra_fields ) <EOL> return result <EOL> def set_extra_fields ( self , ** kwargs ) : <EOL> """<STR_LIT>""" <EOL> result = self . get_extra_fields ( ) <EOL> for k , v in kwargs . iteritems ( ) : <EOL> if k not in self . get_available_extra_fields ( ) : <EOL> raise Exception ( "<STR_LIT>" <EOL> % ( k , self . __class__ . __name__ ) ) <EOL> result [ k ] = v <EOL> self . extra_fields = result <EOL> def __clear_shared_domain_cache ( self , new_domains ) : <EOL> current_domains = self . mobilebackendinvitation_set . values_list ( '<STR_LIT>' , flat = True ) <EOL> for domain in set ( current_domains ) ^ set ( new_domains ) : <EOL> self . domain_is_shared . clear ( self , domain ) <EOL> def set_shared_domains ( self , domains ) : <EOL> if self . id is None : <EOL> raise Exception ( "<STR_LIT>" <EOL> "<STR_LIT>" ) <EOL> with transaction . atomic ( ) : <EOL> self . __clear_shared_domain_cache ( domains ) <EOL> self . mobilebackendinvitation_set . all ( ) . delete ( ) <EOL> self . mobilebackendinvitation_set = [ <EOL> MobileBackendInvitation ( <EOL> domain = domain , <EOL> accepted = True , <EOL> ) for domain in domains <EOL> ] <EOL> def soft_delete ( self ) : <EOL> with transaction . atomic ( ) : <EOL> self . deleted = True <EOL> self . __clear_shared_domain_cache ( [ ] ) <EOL> self . mobilebackendinvitation_set . all ( ) . delete ( ) <EOL> for mapping in self . sqlmobilebackendmapping_set . all ( ) : <EOL> mapping . delete ( ) <EOL> self . save ( ) <EOL> def __clear_caches ( self ) : <EOL> if self . pk : <EOL> self . load . clear ( SQLMobileBackend , self . pk , is_couch_id = False ) <EOL> self . get_backend_api_id . clear ( SQLMobileBackend , self . pk , is_couch_id = False ) <EOL> if self . couch_id : <EOL> self . load . clear ( SQLMobileBackend , self . couch_id , is_couch_id = True ) <EOL> self . get_backend_api_id . clear ( SQLMobileBackend , self . couch_id , is_couch_id = True ) <EOL> def save ( self , * args , ** kwargs ) : <EOL> self . __clear_caches ( ) <EOL> return super ( SQLMobileBackend , self ) . save ( * args , ** kwargs ) <EOL> def delete ( self , * args , ** kwargs ) : <EOL> self . __clear_caches ( ) <EOL> return super ( SQLMobileBackend , self ) . delete ( * args , ** kwargs ) <EOL> class SQLSMSBackend ( SQLMobileBackend ) : <EOL> class Meta : <EOL> proxy = True <EOL> app_label = '<STR_LIT>' <EOL> def get_sms_rate_limit ( self ) : <EOL> """<STR_LIT>""" <EOL> return None <EOL> def send ( self , msg , * args , ** kwargs ) : <EOL> raise NotImplementedError ( "<STR_LIT>" ) <EOL> @ classmethod <EOL> def get_opt_in_keywords ( cls ) : <EOL> """<STR_LIT>""" <EOL> return [ ] <EOL> @ classmethod <EOL> def get_opt_out_keywords ( cls ) : <EOL> """<STR_LIT>""" <EOL> return [ ] <EOL> class PhoneLoadBalancingMixin ( object ) : <EOL> """<STR_LIT>""" <EOL> def get_load_balance_redis_key ( self ) : <EOL> return '<STR_LIT>' % self . pk <EOL> def get_next_phone_number ( self ) : <EOL> if ( <EOL> not isinstance ( self . load_balancing_numbers , list ) or <EOL> len ( self . load_balancing_numbers ) == <NUM_LIT:0> <EOL> ) : <EOL> raise Exception ( "<STR_LIT>" <EOL> "<STR_LIT>" % self . pk ) <EOL> if len ( self . load_balancing_numbers ) == <NUM_LIT:1> : <EOL> return self . load_balancing_numbers [ <NUM_LIT:0> ] <EOL> redis_key = self . get_load_balance_redis_key ( ) <EOL> return load_balance ( redis_key , self . load_balancing_numbers ) <EOL> class BackendMap ( object ) : <EOL> def __init__ ( self , catchall_backend_id , backend_map ) : <EOL> """<STR_LIT>""" <EOL> self . catchall_backend_id = catchall_backend_id <EOL> self . backend_map_dict = backend_map <EOL> self . backend_map_tuples = backend_map . items ( ) <EOL> self . backend_map_tuples . sort ( key = lambda x : len ( x [ <NUM_LIT:0> ] ) , reverse = True ) <EOL> def get_backend_id_by_prefix ( self , phone_number ) : <EOL> phone_number = smsutil . strip_plus ( phone_number ) <EOL> for prefix , backend_id in self . backend_map_tuples : <EOL> if phone_number . startswith ( prefix ) : <EOL> return backend_id <EOL> return self . catchall_backend_id <EOL> class SQLMobileBackendMapping ( models . Model ) : <EOL> """<STR_LIT>""" <EOL> class Meta : <EOL> db_table = '<STR_LIT>' <EOL> app_label = '<STR_LIT>' <EOL> unique_together = ( '<STR_LIT>' , '<STR_LIT>' , '<STR_LIT>' ) <EOL> couch_id = models . CharField ( max_length = <NUM_LIT> , null = True , db_index = True ) <EOL> is_global = models . BooleanField ( default = False ) <EOL> domain = models . CharField ( max_length = <NUM_LIT> , null = True ) <EOL> backend_type = models . CharField ( max_length = <NUM_LIT:3> , choices = SQLMobileBackend . TYPE_CHOICES ) <EOL> prefix = models . CharField ( max_length = <NUM_LIT> ) <EOL> backend = models . ForeignKey ( '<STR_LIT>' ) <EOL> @ classmethod <EOL> def __set_default_domain_backend ( cls , domain , backend_type , backend = None ) : <EOL> fields = dict ( <EOL> is_global = False , <EOL> domain = domain , <EOL> backend_type = backend_type , <EOL> prefix = '<STR_LIT:*>' <EOL> ) <EOL> obj = None <EOL> try : <EOL> obj = cls . objects . get ( ** fields ) <EOL> except cls . DoesNotExist : <EOL> pass <EOL> if not backend : <EOL> if obj : <EOL> obj . delete ( ) <EOL> return <EOL> if not obj : <EOL> obj = cls ( ** fields ) <EOL> obj . backend = backend <EOL> obj . save ( ) <EOL> @ classmethod <EOL> def set_default_domain_backend ( cls , domain , backend , backend_type = SQLMobileBackend . SMS ) : <EOL> cls . __set_default_domain_backend ( domain , backend_type , backend = backend ) <EOL> @ classmethod <EOL> def unset_default_domain_backend ( cls , domain , backend_type = SQLMobileBackend . SMS ) : <EOL> cls . __set_default_domain_backend ( domain , backend_type ) <EOL> @ classmethod <EOL> @ quickcache ( [ '<STR_LIT>' , '<STR_LIT>' ] , timeout = <NUM_LIT:5> * <NUM_LIT> ) <EOL> def get_prefix_to_backend_map ( cls , backend_type , domain = None ) : <EOL> """<STR_LIT>""" <EOL> if domain : <EOL> filter_args = { '<STR_LIT>' : backend_type , '<STR_LIT>' : False , '<STR_LIT>' : domain } <EOL> else : <EOL> filter_args = { '<STR_LIT>' : backend_type , '<STR_LIT>' : True } <EOL> catchall_backend_id = None <EOL> backend_map = { } <EOL> for instance in cls . objects . filter ( ** filter_args ) : <EOL> if instance . prefix == '<STR_LIT:*>' : <EOL> catchall_backend_id = instance . backend_id <EOL> else : <EOL> backend_map [ instance . prefix ] = instance . backend_id <EOL> return BackendMap ( catchall_backend_id , backend_map ) <EOL> def __clear_prefix_to_backend_map_cache ( self ) : <EOL> self . get_prefix_to_backend_map . clear ( self . __class__ , self . backend_type , domain = self . domain ) <EOL> def save ( self , * args , ** kwargs ) : <EOL> self . __clear_prefix_to_backend_map_cache ( ) <EOL> return super ( SQLMobileBackendMapping , self ) . save ( * args , ** kwargs ) <EOL> def delete ( self , * args , ** kwargs ) : <EOL> self . __clear_prefix_to_backend_map_cache ( ) <EOL> return super ( SQLMobileBackendMapping , self ) . delete ( * args , ** kwargs ) <EOL> class MobileBackendInvitation ( models . Model ) : <EOL> class Meta : <EOL> db_table = '<STR_LIT>' <EOL> app_label = '<STR_LIT>' <EOL> unique_together = ( '<STR_LIT>' , '<STR_LIT>' ) <EOL> domain = models . CharField ( max_length = <NUM_LIT> , null = True , db_index = True ) <EOL> backend = models . ForeignKey ( '<STR_LIT>' ) <EOL> accepted = models . BooleanField ( default = False ) <EOL> class MigrationStatus ( models . Model ) : <EOL> """<STR_LIT>""" <EOL> MIGRATION_BACKEND = '<STR_LIT>' <EOL> MIGRATION_BACKEND_MAP = '<STR_LIT>' <EOL> MIGRATION_DOMAIN_DEFAULT_BACKEND = '<STR_LIT>' <EOL> MIGRATION_LOGS = '<STR_LIT>' <EOL> class Meta : <EOL> db_table = '<STR_LIT>' <EOL> app_label = "<STR_LIT>" <EOL> name = models . CharField ( max_length = <NUM_LIT> ) <EOL> timestamp = models . DateTimeField ( null = True ) <EOL> @ classmethod <EOL> def set_migration_completed ( cls , name ) : <EOL> obj , created = cls . objects . get_or_create ( name = name ) <EOL> obj . timestamp = datetime . utcnow ( ) <EOL> obj . save ( ) <EOL> @ classmethod <EOL> def has_migration_completed ( cls , name ) : <EOL> try : <EOL> cls . objects . get ( name = name ) <EOL> return True <EOL> except cls . DoesNotExist : <EOL> return False <EOL> from corehq . apps . sms import signals </s>
