def global_keyword_start(verified_number, text, msg, text_words, open_sessions): outbound_metadata = MessageMetadata(workflow=WORKFLOW_KEYWORD, ) if len(text_words) > 1: keyword = text_words[1] k = Keyword.get_keyword(verified_number.domain, keyword) if k: if not contact_can_use_keyword(verified_number, k): return False process_survey_keyword_actions(verified_number, k, text[6:].strip(), msg) else: message = get_message(MSG_KEYWORD_NOT_FOUND, verified_number, (keyword, )) send_sms_to_verified_number(verified_number, message, metadata=outbound_metadata) else: message = get_message(MSG_START_KEYWORD_USAGE, verified_number, (text_words[0], )) send_sms_to_verified_number(verified_number, message, metadata=outbound_metadata) return True
def handle_due_survey_action(domain, contact_id, session_id): with critical_section_for_smsforms_sessions(contact_id): session = SQLXFormsSession.by_session_id(session_id) if (not session or not session.session_is_open or session.current_action_due > utcnow()): return if session.current_action_is_a_reminder: # Resend the current question in the open survey to the contact p = PhoneNumber.get_phone_number_for_owner(session.connection_id, session.phone_number) if p: metadata = MessageMetadata( workflow=session.workflow, xforms_session_couch_id=session._id, ) resp = current_question(session.session_id, domain) send_sms_to_verified_number( p, resp.event.text_prompt, metadata, logged_subevent=session.related_subevent) session.move_to_next_action() session.save() else: # Close the session session.close() session.save()
def report_reminder_process_user(user, test=False): now = datetime.datetime.utcnow() date = now - datetime.timedelta(days=7) if not user.location or user.location.location_type.administrative: return sp = SupplyPointCase.get_by_location(user.location) if not sp: return transaction_exists = StockTransaction.objects.filter( case_id=sp._id, type="stockonhand", report__date__gte=date ).exists() if sp and not transaction_exists and user.get_verified_number(): message = REPORT_REMINDER % (user.name, user.location.name) verified_number = user.get_verified_number() if not test: send_sms_to_verified_number(verified_number, message) else: send_test_message(verified_number, message) if can_receive_email(user, verified_number): email = str(user.email) send_mail('REPORT REMINDER', message, '*****@*****.**', [email])
def stock_alerts(transactions, user): report_helper = ProductsReportHelper(user.sql_location, transactions) products_below = report_helper.low_supply() stockouts = report_helper.stockouts() overstocked = report_helper.overstocked() receipts = report_helper.receipts() missings = report_helper.missing_products() message = "" super_message = "" if missings: products_codes_str = ' '.join(sorted([missing.code for missing in missings])) message += " still missing %s. " % products_codes_str if stockouts: products_codes_str = ' '.join([stockout.sql_product.code for stockout in stockouts]) products_names_str = ' '.join([stockout.sql_product.name for stockout in stockouts]) message += " " + STOCKOUTS_MESSAGE % {'products': products_codes_str} super_message = _("stockouts %s; ") % products_names_str if products_below: products_codes_str = ' '.join([product.sql_product.code for product in products_below]) products_names_str = ' '.join([product.sql_product.name for product in products_below]) message += " " + LOW_SUPPLY_MESSAGE % {'low_supply': products_codes_str} super_message += _("below reorder level %s; ") % products_names_str if stockouts or products_below: reorders = [ u'%s %s' % (code, amount) for (code, amount) in report_helper.reorders() if amount ] if reorders: message += " Please order %s." % ' '.join(reorders) if overstocked: if not message: products_codes_str = ' '.join([overstock.sql_product.code for overstock in overstocked]) message += " " + OVERSTOCKED_MESSAGE % {'username': user.username, 'overstocked': products_codes_str} products_names_str = ' '.join([overstock.sql_product.name for overstock in overstocked]) super_message += _("overstocked %s; ") % products_names_str if not message: if not receipts: message = COMPLETE_REPORT % user.username else: products_str = ' '.join( [ "%s %s" % (SQLProduct.objects.get(product_id=receipt.product_id).code, receipt.quantity) for receipt in receipts ] ) message = RECEIPT_MESSAGE % {'username': user.username, 'received': products_str} else: message = (_('Dear %s,') % user.username) + message if super_message: stripped_message = super_message.strip().strip(';') super_message = _('Dear %s, %s is experiencing the following problems: ') + stripped_message send_message_to_admins(user, super_message.rstrip()) send_sms_to_verified_number(user.get_verified_number(), message.rstrip())
def on_going_non_reporting(): now = datetime.datetime.utcnow() date = now - datetime.timedelta(days=21) domains = EWSGhanaConfig.get_all_enabled_domains() for domain in domains: for user in CommCareUser.by_domain(domain): try: user_location = SQLLocation.objects.get(domain=domain, location_id=user.location._id) except AttributeError: continue if user_location: facilities = [] if user_location.location_type == 'district': facilities = user_location.get_children() elif user_location.location_type == 'region': facilities = SQLLocation.objects.filter(domain=domain, parent__parent__location_id=user.location._id) fac = set() for facility in facilities: sp = facility.supply_point_id if sp and not StockTransaction.objects.filter( case_id=sp, type="stockonhand", report__date__gte=date).exists(): fac.add(str(facility.name)) if fac and user.get_verified_number(): message = ONGOING_NON_REPORTING % " \n".join(fac) send_sms_to_verified_number(user.get_verified_number(), message) if user.email: email = str(user.email) send_mail('ONGOING NON REPORTING', message, '*****@*****.**', [email])
def on_going_process_user(user, test=False): now = datetime.datetime.utcnow() date = now - datetime.timedelta(days=21) user_location = user.sql_location if not user_location: return facilities = [] if user_location.location_type.name == 'district': facilities = user_location.get_children() elif user_location.location_type.name == 'region': facilities = SQLLocation.objects.filter(domain=user.domain, parent__parent__location_id=user.location._id) fac = set() for facility in facilities: sp = facility.supply_point_id if not sp: continue transactions_exist = StockTransaction.objects.filter( case_id=sp, type="stockonhand", report__date__gte=date ).exists() if not transactions_exist: fac.add(unicode(facility.name)) verified_number = user.get_verified_number() if fac and verified_number: message = ONGOING_NON_REPORTING % " \n".join(fac) if not test: send_sms_to_verified_number(verified_number, message) else: send_test_message(verified_number, message) if can_receive_email(user, verified_number): email = str(user.email) send_mail('ONGOING NON REPORTING', message, '*****@*****.**', [email])
def rrirv_process_user(user, test=False): if user_has_reporting_location(user) and user_has_role(user, IN_CHARGE_ROLE): if user.get_verified_number(): if not test: send_sms_to_verified_number(user.get_verified_number(), RRIRV_REMINDER % {'name': user.name}) else: send_test_message(user.get_verified_number(), RRIRV_REMINDER % {'name': user.name})
def fallback_handler(verified_number, text, msg): domain_obj = Domain.get_by_name(verified_number.domain, strict=True) logged_event = MessagingEvent.create_event_for_adhoc_sms( verified_number.domain, recipient=verified_number.owner, content_type=MessagingEvent.CONTENT_SMS, source=MessagingEvent.SOURCE_UNRECOGNIZED) inbound_subevent = logged_event.create_subevent_for_single_sms( verified_number.owner_doc_type, verified_number.owner_id) inbound_meta = MessageMetadata(workflow=WORKFLOW_DEFAULT, messaging_subevent_id=inbound_subevent.pk) add_msg_tags(msg, inbound_meta) if domain_obj.use_default_sms_response and domain_obj.default_sms_response: outbound_subevent = logged_event.create_subevent_for_single_sms( verified_number.owner_doc_type, verified_number.owner_id) outbound_meta = MessageMetadata( workflow=WORKFLOW_DEFAULT, location_id=msg.location_id, messaging_subevent_id=outbound_subevent.pk) send_sms_to_verified_number(verified_number, domain_obj.default_sms_response, metadata=outbound_meta) outbound_subevent.completed() inbound_subevent.completed() logged_event.completed() return True
def send_messages_for_config(config, actually_send=True): query_engine = QueryEngine(template_vars=config.template_variables) params = get_parsed_params(config.template) sent_messages = [] for user in config.group.get_users(): phone_number = get_preferred_phone_number_for_recipient(user) if phone_number: query_context = QueryContext( user, config.group, template_vars=config.template_variables) message_context = query_engine.get_context(params, query_context) message = config.template.format(**message_context) if actually_send: metadata = MessageMetadata(workflow=WORKFLOW_PERFORMANCE) if isinstance(phone_number, PhoneNumber): send_sms_to_verified_number(phone_number, message, metadata=metadata) else: send_sms(config.domain, user, phone_number, message, metadata=metadata) sent_messages.append(MessageResult(user, message)) return sent_messages
def stockout_process_user(user, test=False): if user_has_reporting_location(user): location = user.location supply_point = SupplyPointCase.get_by_location(location) if supply_point and user.get_verified_number(): products = [ SQLProduct.objects.get(product_id=state.product_id).name for state in StockState.objects.filter( case_id=supply_point._id, stock_on_hand=0, product_id__in=[product.product_id for product in location.sql_location.products] ) ] if products: if not test: send_sms_to_verified_number( user.get_verified_number(), STOCKOUT_REPORT % { 'name': user.name, 'facility': supply_point.name, 'date': datetime.datetime.utcnow().strftime('%b %d'), 'products': ", ".join(products) } ) else: send_test_message( user.get_verified_number(), STOCKOUT_REPORT % { 'name': user.name, 'facility': supply_point.name, 'date': datetime.datetime.utcnow().strftime('%b %d'), 'products': ", ".join(products) } )
def fallback_handler(v, text, msg): domain_obj = Domain.get_by_name(v.domain, strict=True) logged_event = MessagingEvent.create_event_for_adhoc_sms( v.domain, recipient=v.owner, content_type=MessagingEvent.CONTENT_SMS, source=MessagingEvent.SOURCE_UNRECOGNIZED) inbound_subevent = logged_event.create_subevent_for_single_sms( v.owner_doc_type, v.owner_id) inbound_meta = MessageMetadata(workflow=WORKFLOW_DEFAULT, messaging_subevent_id=inbound_subevent.pk) add_msg_tags(msg, inbound_meta) if domain_obj.use_default_sms_response and domain_obj.default_sms_response: outbound_subevent = logged_event.create_subevent_for_single_sms( v.owner_doc_type, v.owner_id) outbound_meta = MessageMetadata(workflow=WORKFLOW_DEFAULT, location_id=msg.location_id, messaging_subevent_id=outbound_subevent.pk) send_sms_to_verified_number(v, domain_obj.default_sms_response, metadata=outbound_meta) outbound_subevent.completed() inbound_subevent.completed() logged_event.completed() return True
def process_verification(phone_number, msg, backend_id=None): v = VerifiedNumber.by_phone(phone_number, True) if not v: return if not verification_response_ok(msg.text): return msg.domain = v.domain msg.couch_recipient_doc_type = v.owner_doc_type msg.couch_recipient = v.owner_id msg.save() if backend_id: backend = MobileBackend.load(backend_id) else: backend = MobileBackend.auto_load(phone_number, v.domain) # i don't know how to dynamically instantiate this object, which may be any number of doc types... #owner = CommCareMobileContactMixin.get(v.owner_id) assert v.owner_doc_type == 'CommCareUser' owner = CommCareUser.get(v.owner_id) v = owner.save_verified_number(v.domain, phone_number, True, backend.name) with localize(owner.language): send_sms_to_verified_number(v, _(CONFIRM))
def send_translated_message(user, message, **kwargs): verified_number = user.get_verified_number() if not verified_number: return False with localize(user.get_language_code()): send_sms_to_verified_number(verified_number, message % kwargs) return True
def send_translated_message(user, message, **kwargs): verified_number = get_two_way_number_for_recipient(user) if not verified_number: return False with localize(user.get_language_code()): send_sms_to_verified_number(verified_number, message % kwargs) return True
def __test_verified_number_with_map(self): # Test sending to verified number with backend map SQLMobileBackendMapping.unset_default_domain_backend(self.domain) verified_number = self.contact.get_verified_number() self.assertTrue(verified_number is not None) self.assertTrue(verified_number.backend_id is None) self.assertEqual(verified_number.phone_number, '15551234567') with patch( 'corehq.messaging.smsbackends.test.models.SQLTestSMSBackend.send', autospec=True ) as mock_send: self.assertTrue(send_sms_to_verified_number(verified_number, 'Test for BACKEND2')) self.assertEqual(mock_send.call_count, 1) self.assertEqual(mock_send.call_args[0][0].pk, self.backend2.pk) # Test sending to verified number with default domain backend SQLMobileBackendMapping.set_default_domain_backend(self.domain, self.backend5) with patch( 'corehq.messaging.smsbackends.test.models.SQLTestSMSBackend.send', autospec=True ) as mock_send: self.assertTrue(send_sms_to_verified_number(verified_number, 'Test for BACKEND5')) self.assertEqual(mock_send.call_count, 1) self.assertEqual(mock_send.call_args[0][0].pk, self.backend5.pk)
def get_valid_reports(self, data, verified_contact): filtered_transactions = [] excluded_products = [] for product_id, transactions in get_transactions_by_product( data['transactions']).iteritems(): begin_soh = None end_soh = None receipt = 0 for transaction in transactions: if begin_soh is None: sql_location = SQLLocation.objects.get( location_id=transaction.location_id) latest = StockTransaction.latest( sql_location.supply_point_id, SECTION_TYPE_STOCK, transaction.product_id) begin_soh = 0 if latest: begin_soh = float(latest.stock_on_hand) if transaction.action == 'receipts': receipt += float(transaction.quantity) elif not end_soh: end_soh = float(transaction.quantity) if end_soh > begin_soh + receipt: excluded_products.append(transaction.product_id) else: filtered_transactions.append(transaction) if excluded_products: message = ERROR_MESSAGE.format(products_list=', '.join([ SQLProduct.objects.get(product_id=product_id).code for product_id in set(excluded_products) ])) send_sms_to_verified_number(verified_contact, message) return filtered_transactions
def handle(self): verified_contact = self.verified_contact user = verified_contact.owner domain = Domain.get_by_name(verified_contact.domain) splitted_text = self.msg.text.split() if splitted_text[0].lower() == 'soh': text = ' '.join(self.msg.text.split()[1:]) else: text = self.msg.text if not domain.commtrack_enabled: return False try: data = StockAndReceiptParser(domain, verified_contact).parse(text) if not data: return False if EWS_INVALID_REPORT_RESPONSE.enabled(self.domain): filtered_transactions = self.get_valid_reports( data, verified_contact) if not filtered_transactions: return True data['transactions'] = filtered_transactions except NotAUserClassError: return False except Exception, e: # todo: should we only trap SMSErrors? if settings.UNIT_TESTING or settings.DEBUG: raise send_sms_to_verified_number( verified_contact, 'problem with stock report: %s' % str(e)) return True
def _send_delivery_alert_to_facilities(self, sp_name, location): locs = [c._id for c in location.children] users = filter(lambda u: u.location_id in locs, CommCareUser.by_domain(self.domain)) for user in users: if user.get_verified_number(): send_sms_to_verified_number(user.get_verified_number(), DELIVERY_CONFIRM_CHILDREN % {"district_name": sp_name})
def _send_submission_alert_to_msd(self, params): users = filter(lambda u: u.user_data.get('role', None) == 'MSD', CommCareUser.by_domain(self.domain)) for user in users: if not user.get_verified_number(): continue with localize(user.get_language_code()): send_sms_to_verified_number(user.get_verified_number(), SUBMITTED_NOTIFICATION_MSD % params)
def handle(self): verified_contact = self.verified_contact user = verified_contact.owner domain = Domain.get_by_name(verified_contact.domain) splitted_text = self.msg.text.split() if splitted_text[0].lower() == "soh": text = " ".join(self.msg.text.split()[1:]) else: text = self.msg.text if not domain.commtrack_enabled: return False try: data = StockAndReceiptParser(domain, verified_contact).parse(text) if not data: return False if EWS_INVALID_REPORT_RESPONSE.enabled(self.domain): filtered_transactions = self.get_valid_reports(data, verified_contact) if not filtered_transactions: return True data["transactions"] = filtered_transactions except NotAUserClassError: return False except Exception, e: # todo: should we only trap SMSErrors? if settings.UNIT_TESTING or settings.DEBUG: raise send_sms_to_verified_number(verified_contact, "problem with stock report: %s" % str(e)) return True
def handle(self): location = self.user.location domain = self.domain_object location_id = self.location_id if not location_id: return False if location.location_type_name == 'FACILITY': try: data = self.data if not data: return True if not data.get('transactions'): self.on_error(data) return True process(domain.name, data) if not data['errors']: self.on_success() else: self.on_error(data) return True self.respond(self.get_message(data)) except NotAUserClassError: return True except Exception, e: # todo: should we only trap SMSErrors? if settings.UNIT_TESTING or settings.DEBUG: raise send_sms_to_verified_number( self.verified_contact, 'problem with stock report: %s' % str(e))
def visit_website_process_user(user, test=False): date = datetime.datetime.utcnow() - datetime.timedelta(weeks=13) if user.last_login < date and user.get_verified_number(): if not test: send_sms_to_verified_number(user.get_verified_number(), WEB_REMINDER % {'name': user.name}) else: send_test_message(user.get_verified_number(), WEB_REMINDER % {'name': user.name})
def handle(self): verified_contact = self.verified_contact domain = Domain.get_by_name(verified_contact.domain) text = self.msg.text try: data = StockReportParser(domain, verified_contact).parse(text) if not data: return False except NotAUserClassError: return False except Exception as e: if settings.UNIT_TESTING or settings.DEBUG: raise send_sms_to_verified_number( verified_contact, 'problem with stock report: %s' % str(e)) return True transactions = data['transactions'] products = [ SQLProduct.objects.get(product_id=transaction.product_id).code for transaction in transactions ] process(domain.name, data) send_sms_to_verified_number( verified_contact, RECEIPT_CONFIRM % {'products': ' '.join(products)}) return True
def get_valid_reports(self, data, verified_contact): filtered_transactions = [] excluded_products = [] for product_id, transactions in get_transactions_by_product(data["transactions"]).iteritems(): begin_soh = None end_soh = None receipt = 0 for transaction in transactions: if begin_soh is None: sql_location = SQLLocation.objects.get(location_id=transaction.location_id) latest = StockTransaction.latest( sql_location.supply_point_id, SECTION_TYPE_STOCK, transaction.product_id ) begin_soh = 0 if latest: begin_soh = float(latest.stock_on_hand) if transaction.action == "receipts": receipt += float(transaction.quantity) elif not end_soh: end_soh = float(transaction.quantity) if end_soh > begin_soh + receipt: excluded_products.append(transaction.product_id) else: filtered_transactions.append(transaction) if excluded_products: message = ERROR_MESSAGE.format( products_list=", ".join( [SQLProduct.objects.get(product_id=product_id).code for product_id in set(excluded_products)] ) ) send_sms_to_verified_number(verified_contact, message) return filtered_transactions
def handle(verified_contact, text, msg): """top-level handler for incoming stock report messages""" domain_obj = Domain.get_by_name(verified_contact.domain) if not domain_obj.commtrack_enabled: return False try: if toggles.STOCK_AND_RECEIPT_SMS_HANDLER.enabled(domain_obj.name): # handle special stock parser for custom domain logic data = StockAndReceiptParser(domain_obj, verified_contact).parse(text.lower()) else: # default report parser data = StockReportParser(domain_obj, verified_contact).parse(text.lower()) if not data: return False except NotAUserClassError: return False except Exception as e: if settings.UNIT_TESTING or settings.DEBUG: raise send_sms_to_verified_number(verified_contact, 'problem with stock report: %s' % six.text_type(e)) return True process(domain_obj.name, data) send_confirmation(verified_contact, data) return True
def on_going_process_user(user, test=False): now = datetime.datetime.utcnow() date = now - datetime.timedelta(days=21) user_location = user.sql_location if not user_location: return facilities = [] if user_location.location_type.name == 'district': facilities = user_location.get_children() elif user_location.location_type.name == 'region': facilities = SQLLocation.objects.filter( domain=user.domain, parent__parent__location_id=user.location._id) fac = set() for facility in facilities: sp = facility.supply_point_id if not sp: continue transactions_exist = StockTransaction.objects.filter( case_id=sp, type="stockonhand", report__date__gte=date).exists() if not transactions_exist: fac.add(unicode(facility.name)) verified_number = user.get_verified_number() if fac and verified_number: message = ONGOING_NON_REPORTING % " \n".join(fac) if not test: send_sms_to_verified_number(verified_number, message) else: send_test_message(verified_number, message) if can_receive_email(user, verified_number): email = str(user.email) send_mail('ONGOING NON REPORTING', message, '*****@*****.**', [email])
def handle(verified_contact, text, msg): """top-level handler for incoming stock report messages""" domain = Domain.get_by_name(verified_contact.domain) if not domain.commtrack_enabled: return False try: if toggles.STOCK_AND_RECEIPT_SMS_HANDLER.enabled(domain.name): # handle special stock parser for custom domain logic data = StockAndReceiptParser(domain, verified_contact).parse(text.lower()) else: # default report parser data = StockReportParser(domain, verified_contact).parse(text.lower()) if not data: return False except NotAUserClassError: return False except Exception as e: if settings.UNIT_TESTING or settings.DEBUG: raise send_sms_to_verified_number(verified_contact, 'problem with stock report: %s' % str(e)) return True process(domain.name, data) send_confirmation(verified_contact, data) return True
def clean_up_and_send_response(msg, contact, session, error_occurred, error_msg, verified_number=None, send_response=False, logged_event=None, logged_subevent=None): session = refresh_and_close_session(session) metadata = add_keyword_metadata(msg, session) if error_occurred and verified_number and send_response: response_subevent = None if logged_event: response_subevent = logged_event.create_subevent_for_single_sms( contact.doc_type, contact.get_id) metadata.messaging_subevent_id = response_subevent.pk send_sms_to_verified_number(verified_number, error_msg, metadata=metadata) if response_subevent: response_subevent.completed() if logged_subevent: logged_subevent.completed()
def fallback_handler(v, text, msg): domain_obj = Domain.get_by_name(v.domain, strict=True) default_workflow_meta = MessageMetadata(workflow=WORKFLOW_DEFAULT, location_id=msg.location_id) if domain_obj.use_default_sms_response and domain_obj.default_sms_response: send_sms_to_verified_number(v, domain_obj.default_sms_response, metadata=default_workflow_meta) add_msg_tags(msg, default_workflow_meta) return True
def fire_sms_event(reminder, handler, recipients, verified_numbers, logged_event, workflow=None): current_event = reminder.current_event case = reminder.case template_params = get_message_template_params(case) uses_custom_content_handler, content_handler = get_custom_content_handler( handler, logged_event) if uses_custom_content_handler and not content_handler: return domain_obj = Domain.get_by_name(reminder.domain, strict=True) for recipient in recipients: logged_subevent = logged_event.create_subevent(handler, reminder, recipient) try: lang = recipient.get_language_code() except Exception: lang = None if content_handler: message = content_handler(reminder, handler, recipient) else: message = current_event.message.get( lang, current_event.message[handler.default_lang]) try: message = Message.render(message, **template_params) except Exception: logged_subevent.error( MessagingEvent.ERROR_CANNOT_RENDER_MESSAGE) continue verified_number, unverified_number = get_recipient_phone_number( reminder, recipient, verified_numbers) if message: metadata = MessageMetadata( workflow=workflow or get_workflow(handler), reminder_id=reminder._id, messaging_subevent_id=logged_subevent.pk, ) if verified_number is not None: send_sms_to_verified_number(verified_number, message, metadata) elif isinstance(recipient, CouchUser) and unverified_number: send_sms(reminder.domain, recipient, unverified_number, message, metadata) elif (isinstance(recipient, CommCareCase) and unverified_number and domain_obj.send_to_duplicated_case_numbers): send_sms(reminder.domain, recipient, unverified_number, message, metadata) else: logged_subevent.error(MessagingEvent.ERROR_NO_PHONE_NUMBER) continue logged_subevent.completed()
def process_verification(phone_number, msg, backend_id=None): v = VerifiedNumber.by_phone(phone_number, True) if not v: return if not verification_response_ok(msg.text): return msg.domain = v.domain msg.couch_recipient_doc_type = v.owner_doc_type msg.couch_recipient = v.owner_id msg.save() if not domain_has_privilege(msg.domain, privileges.INBOUND_SMS): return if backend_id: backend = MobileBackend.load(backend_id) else: backend = MobileBackend.auto_load(phone_number, v.domain) # i don't know how to dynamically instantiate this object, which may be any number of doc types... #owner = CommCareMobileContactMixin.get(v.owner_id) assert v.owner_doc_type == 'CommCareUser' owner = CommCareUser.get(v.owner_id) v = owner.save_verified_number(v.domain, phone_number, True, backend.name) with localize(owner.language): send_sms_to_verified_number(v, _(CONFIRM))
def answer_next_question(v, text, msg, session): resp = current_question(session.session_id) event = resp.event valid, text, error_msg = validate_answer(event, text, v) # metadata to be applied to the reply message outbound_metadata = MessageMetadata( workflow=session.workflow, reminder_id=session.reminder_id, xforms_session_couch_id=session._id, ) if valid: responses = _get_responses(v.domain, v.owner_id, text, yield_responses=True) if has_invalid_response(responses): mark_as_invalid_response(msg) text_responses = _responses_to_text(responses) if len(text_responses) > 0: response_text = format_message_list(text_responses) send_sms_to_verified_number(v, response_text, metadata=outbound_metadata) else: mark_as_invalid_response(msg) response_text = "%s %s" % (error_msg, event.text_prompt) send_sms_to_verified_number(v, response_text, metadata=outbound_metadata)
def handle(self): location = self.user.location domain = self.domain_object location_id = self.location_id if not location_id: return False if location.location_type_name == 'FACILITY': try: data = self.data if not data: return True if not data.get('transactions'): self.on_error(data) return True process(domain.name, data) if not data['errors']: self.on_success() else: self.on_error(data) return True self.respond(self.get_message(data)) except NotAUserClassError: return True except Exception, e: # todo: should we only trap SMSErrors? if settings.UNIT_TESTING or settings.DEBUG: raise send_sms_to_verified_number(self.verified_contact, 'problem with stock report: %s' % str(e))
def form_session_handler(v, text, msg): """ The form session handler will use the inbound text to answer the next question in the open SQLXformsSession for the associated contact. If no session is open, the handler passes. If multiple sessions are open, they are all closed and an error message is displayed to the user. """ multiple, session = get_single_open_session_or_close_multiple(v.domain, v.owner_id) if multiple: send_sms_to_verified_number(v, get_message(MSG_MULTIPLE_SESSIONS, v)) return True if session: # Metadata to be applied to the inbound message inbound_metadata = MessageMetadata( workflow=session.workflow, reminder_id=session.reminder_id, xforms_session_couch_id=session._id, ) add_msg_tags(msg, inbound_metadata) try: answer_next_question(v, text, msg, session) except Exception: # Catch any touchforms errors log_sms_exception(msg) send_sms_to_verified_number(v, get_message(MSG_TOUCHFORMS_DOWN, v)) return True else: return False
def stockout_process_user(user, test=False): if user_has_reporting_location(user): location = user.location supply_point = SupplyPointCase.get_by_location(location) if supply_point and user.get_verified_number(): products = [ SQLProduct.objects.get(product_id=state.product_id).name for state in StockState.objects.filter( case_id=supply_point._id, stock_on_hand=0, product_id__in=[ product.product_id for product in location.sql_location.products ]) ] if products: if not test: send_sms_to_verified_number( user.get_verified_number(), STOCKOUT_REPORT % { 'name': user.name, 'facility': supply_point.name, 'date': datetime.datetime.utcnow().strftime('%b %d'), 'products': ", ".join(products) }) else: send_test_message( user.get_verified_number(), STOCKOUT_REPORT % { 'name': user.name, 'facility': supply_point.name, 'date': datetime.datetime.utcnow().strftime('%b %d'), 'products': ", ".join(products) })
def answer_next_question(v, text, msg, session): resp = current_question(session.session_id, domain=v.domain) event = resp.event valid, text, error_msg = validate_answer(event, text, v) # metadata to be applied to the reply message outbound_metadata = MessageMetadata( workflow=session.workflow, reminder_id=session.reminder_id, xforms_session_couch_id=session._id, ) if valid: responses = get_responses(v.domain, session.session_id, text) if has_invalid_response(responses): mark_as_invalid_response(msg) text_responses = _responses_to_text(responses) if len(text_responses) > 0: response_text = format_message_list(text_responses) send_sms_to_verified_number(v, response_text, metadata=outbound_metadata) else: mark_as_invalid_response(msg) response_text = "%s %s" % (error_msg, event.text_prompt) send_sms_to_verified_number(v, response_text, metadata=outbound_metadata)
def handle(verified_contact, text, msg=None): user = verified_contact.owner if verified_contact else None domain = user.domain if not domain: return False if not EWSGhanaConfig.for_domain(domain): return False args = text.split() if not args: send_sms_to_verified_number(verified_contact, unicode(INVALID_MESSAGE)) return True keyword = args[0] args = args[1:] params = { 'user': user, 'domain': domain, 'args': args, 'msg': msg, 'verified_contact': verified_contact } def not_function(word): if args and re.match("del", word): return NotDeliveredHandler elif args and re.match("sub", word): return NotSubmittedHandler return None handlers = { ('help', ): HelpHandler, ('stop', ): StopHandler, ('start', ): StartHandler, ('language', 'lang', 'lugha'): LanguageHandler, ('yes', 'no', 'y', 'n'): RequisitionHandler, ('undo', 'replace', 'revoke'): UndoHandler, ('soh',): AlertsHandler, ('not',): not_function(args[0]) if args else None, ('rec', 'receipts', 'received'): ReceiptsHandler } def choose_handler(keyword): for k, v in handlers.iteritems(): if keyword.lower() in k: return v return None handler_class = choose_handler(keyword) handler = handler_class(**params) if handler_class else None if handler: if args: return handler.handle() else: handler.help() return True else: return AlertsHandler(**params).handle()
def handle(verified_contact, text, msg=None): user = verified_contact.owner if verified_contact else None domain = user.domain if not domain: return False if not EWSGhanaConfig.for_domain(domain): return False args = text.split() if not args: send_sms_to_verified_number(verified_contact, unicode(INVALID_MESSAGE)) return True keyword = args[0] args = args[1:] params = { 'user': user, 'domain': domain, 'args': args, 'msg': msg, 'verified_contact': verified_contact } def not_function(word): if args and re.match("del", word): return NotDeliveredHandler elif args and re.match("sub", word): return NotSubmittedHandler return None handlers = { ('help', ): HelpHandler, ('stop', ): StopHandler, ('start', ): StartHandler, ('language', 'lang', 'lugha'): LanguageHandler, ('yes', 'no', 'y', 'n'): RequisitionHandler, ('undo', 'replace', 'revoke'): UndoHandler, ('soh', ): SOHHandler, ('not', ): not_function(args[0]) if args else None, ('rec', 'receipts', 'received'): ReceiptsHandler } def choose_handler(keyword): for k, v in handlers.iteritems(): if keyword.lower() in k: return v return None handler_class = choose_handler(keyword) handler = handler_class(**params) if handler_class else None if handler: if args: return handler.handle() else: handler.help() return True else: return SOHHandler(**params).handle()
def send_message(self, sql_location, message, **kwargs): for user in get_users_by_location_id(self.domain, sql_location.location_id): verified_number = user.get_verified_number() if verified_number: with localize(user.get_language_code()): send_sms_to_verified_number(verified_number, message % kwargs)
def respond(self, message, **kwargs): if self.verified_contact: with localize(self.user.get_language_code()): send_sms_to_verified_number(self.verified_contact, str(message % kwargs)) else: send_sms(self.domain, None, self.msg.phone_number, str(message % kwargs))
def form_session_handler(v, text, msg): """ The form session handler will use the inbound text to answer the next question in the open SQLXformsSession for the associated contact. If no session is open, the handler passes. If multiple sessions are open, they are all closed and an error message is displayed to the user. """ with critical_section_for_smsforms_sessions(v.owner_id): if toggles.ONE_PHONE_NUMBER_MULTIPLE_CONTACTS.enabled(v.domain): channel = get_channel_for_contact(v.owner_id, v.phone_number) running_session_info = XFormsSessionSynchronization.get_running_session_info_for_channel( channel) if running_session_info.session_id: session = SQLXFormsSession.by_session_id( running_session_info.session_id) if not session.session_is_open: # This should never happen. But if it does we should set the channel free # and act like there was no available session notify_error( "The supposedly running session was not open and was released. " 'No known way for this to happen, so worth investigating.' ) XFormsSessionSynchronization.clear_stale_channel_claim( channel) session = None else: session = None else: multiple, session = get_single_open_session_or_close_multiple( v.domain, v.owner_id) if multiple: send_sms_to_verified_number( v, get_message(MSG_MULTIPLE_SESSIONS, v)) return True if session: session.phone_number = v.phone_number session.modified_time = datetime.utcnow() session.save() # Metadata to be applied to the inbound message inbound_metadata = MessageMetadata( workflow=session.workflow, reminder_id=session.reminder_id, xforms_session_couch_id=session._id, ) add_msg_tags(msg, inbound_metadata) try: answer_next_question(v, text, msg, session) except Exception: # Catch any touchforms errors log_sms_exception(msg) send_sms_to_verified_number( v, get_message(MSG_TOUCHFORMS_DOWN, v)) return True else: return False
def send_first_message(domain, recipient, phone_entry_or_number, session, responses, logged_subevent, workflow): # This try/except section is just here (temporarily) to support future refactors # If any of these notify, they should be replaced with a comment as to why the two are different # so that someone refactoring in the future will know that this or that param is necessary. try: if session.workflow != workflow: # see if we can eliminate the workflow arg notify_error('Exploratory: session.workflow != workflow', details={ 'session.workflow': session.workflow, 'workflow': workflow}) if session.connection_id != recipient.get_id: # see if we can eliminate the recipient arg notify_error('Exploratory: session.connection_id != recipient.get_id', details={ 'session.connection_id': session.connection_id, 'recipient.get_id': recipient.get_id, 'recipient': recipient }) if session.related_subevent != logged_subevent: # see if we can eliminate the logged_subevent arg notify_error('Exploratory: session.related_subevent != logged_subevent', details={ 'session.connection_id': session.connection_id, 'logged_subevent': logged_subevent}) except Exception: # The above running is not mission critical, so if it errors just leave a message in the log # for us to follow up on. # Absence of the message below and messages above ever notifying # will indicate that we can remove these args. notify_exception(None, "Error in section of code that's just supposed help inform future refactors") if toggles.ONE_PHONE_NUMBER_MULTIPLE_CONTACTS.enabled(domain): if not XFormsSessionSynchronization.claim_channel_for_session(session): send_first_message.apply_async( args=(domain, recipient, phone_entry_or_number, session, responses, logged_subevent, workflow), countdown=60 ) return metrics_counter('commcare.smsforms.session_started', 1, tags={'domain': domain, 'workflow': workflow}) if len(responses) > 0: message = format_message_list(responses) metadata = MessageMetadata( workflow=workflow, xforms_session_couch_id=session.couch_id, ) if isinstance(phone_entry_or_number, PhoneNumber): send_sms_to_verified_number( phone_entry_or_number, message, metadata, logged_subevent=logged_subevent ) else: send_sms( domain, recipient, phone_entry_or_number, message, metadata ) logged_subevent.completed()
def first_soh_process_user(user, test=False): if user_has_reporting_location(user): if user.get_verified_number(): message = STOCK_ON_HAND_REMINDER % {'name': user.name} if not test: send_sms_to_verified_number(user.get_verified_number(), message) else: send_test_message(user.get_verified_number(), message)
def send_message_to_admins(user, message): users = get_users_by_location_id(user.domain, user.location.get_id) in_charge_users = [ u for u in users if get_verified_number_for_recipient(u) and "In Charge" in u.user_data.get('role', []) ] for in_charge_user in in_charge_users: send_sms_to_verified_number(get_verified_number_for_recipient(in_charge_user), message % (in_charge_user.full_name, in_charge_user.location.name))
def send_message_to_admins(user, message): users = get_users_by_location_id(user.domain, user.location.get_id) in_charge_users = [ u for u in users if u.get_verified_number() and "In Charge" in u.user_data.get('role', []) ] for in_charge_user in in_charge_users: send_sms_to_verified_number( in_charge_user.get_verified_number(), message % (in_charge_user.full_name, in_charge_user.location.name))
def _send_delivery_alert_to_facilities(self, location): locs = [c.get_id for c in location.children] users = [] for location_id in locs: users.extend(get_users_by_location_id(self.domain, location_id)) for user in users: if user.get_verified_number(): send_sms_to_verified_number(user.get_verified_number(), DELIVERY_CONFIRM_CHILDREN % {"district_name": location.name})
def send_keyword_response(vn, message_id, logged_event): subevent = logged_event.create_subevent_for_single_sms( vn.owner_doc_type, vn.owner_id) metadata = MessageMetadata( workflow=WORKFLOW_KEYWORD, messaging_subevent_id=subevent.pk, ) message = get_message(message_id, vn) send_sms_to_verified_number(vn, message, metadata=metadata) subevent.completed()
def send_sms_message(self, domain, recipient, phone_entry_or_number, message, logged_subevent): if not message: return metadata = self.get_sms_message_metadata(logged_subevent) if isinstance(phone_entry_or_number, PhoneNumber): send_sms_to_verified_number(phone_entry_or_number, message, metadata=metadata, logged_subevent=logged_subevent) else: send_sms(domain, recipient, phone_entry_or_number, message, metadata=metadata)
def send_soh_reminder(domain, date): sp_ids = set() for user in CommTrackUser.by_domain(domain): if user.is_active and user.location and user.location.location_type == 'FACILITY': sp = SupplyPointCase.get_by_location(user.location) if sp and not StockTransaction.objects.filter(case_id=sp._id, report__date__gte=date, type='stockonhand').exists(): if user.get_verified_number(): send_sms_to_verified_number(user.get_verified_number(), REMINDER_STOCKONHAND) sp_ids.add(sp._id) update_statuses(sp_ids, SupplyPointStatusTypes.SOH_FACILITY, SupplyPointStatusValues.REMINDER_SENT)
def send_supervision_reminder(domain, date): sp_ids = set() for user in CommTrackUser.by_domain(domain): if user.is_active and user.location and user.location.location_type == 'FACILITY': sp = SupplyPointCase.get_by_location(user.location) if sp and not SupplyPointStatus.objects.filter(supply_point=sp._id, status_type=SupplyPointStatusTypes.SUPERVISION_FACILITY, status_date__gte=date).exists(): if user.get_verified_number(): send_sms_to_verified_number(user.get_verified_number(), REMINDER_SUPERVISION) sp_ids.add(sp._id) update_statuses(sp_ids, SupplyPointStatusTypes.SUPERVISION_FACILITY, SupplyPointStatusValues.REMINDER_SENT)
def send_keyword_response(vn, message_id, logged_event): subevent = logged_event.create_subevent_for_single_sms( vn.owner_doc_type, vn.owner_id ) metadata = MessageMetadata( workflow=WORKFLOW_KEYWORD, messaging_subevent_id=subevent.pk, ) message = get_message(message_id, vn) send_sms_to_verified_number(vn, message, metadata=metadata) subevent.completed()
def fire_sms_event(reminder, handler, recipients, verified_numbers, logged_event, workflow=None): current_event = reminder.current_event case = reminder.case template_params = get_message_template_params(case) uses_custom_content_handler, content_handler = get_custom_content_handler(handler, logged_event) if uses_custom_content_handler and not content_handler: return domain_obj = Domain.get_by_name(reminder.domain, strict=True) for recipient in recipients: logged_subevent = logged_event.create_subevent(handler, reminder, recipient) try: lang = recipient.get_language_code() except Exception: lang = None if content_handler: message = content_handler(reminder, handler, recipient) else: message = current_event.message.get(lang, current_event.message[handler.default_lang]) try: message = Message.render(message, **template_params) except Exception: logged_subevent.error(MessagingEvent.ERROR_CANNOT_RENDER_MESSAGE) continue verified_number, unverified_number = get_recipient_phone_number( reminder, recipient, verified_numbers) if message: metadata = MessageMetadata( workflow=workflow or get_workflow(handler), reminder_id=reminder._id, messaging_subevent_id=logged_subevent.pk, ) if verified_number is not None: send_sms_to_verified_number(verified_number, message, metadata, logged_subevent=logged_subevent) elif isinstance(recipient, CouchUser) and unverified_number: send_sms(reminder.domain, recipient, unverified_number, message, metadata) elif (is_commcarecase(recipient) and unverified_number and domain_obj.send_to_duplicated_case_numbers): send_sms(reminder.domain, recipient, unverified_number, message, metadata) else: logged_subevent.error(MessagingEvent.ERROR_NO_PHONE_NUMBER) continue logged_subevent.completed()
def reminder_to_visit_website(): domains = EWSGhanaConfig.get_all_enabled_domains() for domain in domains: for user in CommCareUser.by_domain(domain): thirteen_days_ago = datetime.datetime.utcnow() - datetime.timedelta(weeks=13) if user.location and user.last_login < thirteen_days_ago and user.get_verified_number()\ and user.location.location_type.name in ['district', 'region', 'country']: message = WEB_REMINDER % user.name verified_number = user.get_verified_number() send_sms_to_verified_number(verified_number, message) if can_receive_email(user, verified_number): email = str(user.email) send_mail('REMINDER TO VISIT WEBSITE', message, '*****@*****.**', [email])
def global_keyword_current(v, text, msg, text_words, open_sessions): if len(open_sessions) == 1: session = open_sessions[0] outbound_metadata = MessageMetadata( workflow=session.workflow, reminder_id=session.reminder_id, xforms_session_couch_id=session._id, ) resp = current_question(session.session_id) send_sms_to_verified_number(v, resp.event.text_prompt, metadata=outbound_metadata) return True