def test_percolate_failure_user_unenroll_program(self, mock_on_commit): """ If search_percolate fails we should raise an Exception with some useful information for Sentry Case when there is not program enrollment. """ with mute_signals(post_save): profile = ProfileFactory.create(filled_out=True) program_enrollment = ProgramEnrollmentFactory.create(user=profile.user) program_enrollment_id = program_enrollment.id program_enrollment.delete() with self.assertRaises(ProgramEnrollment.DoesNotExist): search_percolate_queries(program_enrollment_id, "doesnt_matter")
def send_automatic_emails(program_enrollment): """ Send all automatic emails which match the search criteria for a program enrollment Args: program_enrollment (ProgramEnrollment): A ProgramEnrollment """ percolate_queries = search_percolate_queries(program_enrollment.id, PercolateQuery.AUTOMATIC_EMAIL_TYPE) automatic_emails = AutomaticEmail.objects.filter( query__in=percolate_queries, enabled=True, ).exclude(sentautomaticemail__user__programenrollment=program_enrollment) user = program_enrollment.user for automatic_email in automatic_emails: try: with mark_emails_as_sent(automatic_email, [user.email]) as user_ids: # user_ids should just contain user.id except when we already sent the user the email # in a separate process recipient_emails = User.objects.filter(id__in=user_ids).values_list('email', flat=True) MailgunClient.send_batch( automatic_email.email_subject, automatic_email.email_body, [(context['email'], context) for context in get_mail_vars(list(recipient_emails))], sender_name=automatic_email.sender_name, ) except: # pylint: disable=bare-except log.exception("Error sending mailgun mail for automatic email %s", automatic_email)
def send_automatic_emails(program_enrollment): """ Send all automatic emails which match the search criteria for a program enrollment Args: program_enrollment (ProgramEnrollment): A ProgramEnrollment """ percolate_queries = search_percolate_queries( program_enrollment.id, PercolateQuery.AUTOMATIC_EMAIL_TYPE) automatic_emails = AutomaticEmail.objects.filter( query__in=percolate_queries, enabled=True, ).exclude(sentautomaticemail__user__programenrollment=program_enrollment) user = program_enrollment.user for automatic_email in automatic_emails: try: with mark_emails_as_sent(automatic_email, [user.email]) as user_ids: # user_ids should just contain user.id except when we already sent the user the email # in a separate process recipient_emails = User.objects.filter( id__in=user_ids).values_list('email', flat=True) MailgunClient.send_batch( automatic_email.email_subject, automatic_email.email_body, [(context['email'], context) for context in get_mail_vars(list(recipient_emails))], sender_name=automatic_email.sender_name, ) except: # pylint: disable=bare-except log.exception("Error sending mailgun mail for automatic email %s", automatic_email)
def test_not_percolated(self, mock_on_commit): """If there are no percolated queries we should return an empty queryset""" with mute_signals(post_save): profile = ProfileFactory.create(filled_out=True) program_enrollment = ProgramEnrollmentFactory.create(user=profile.user) assert list( search_percolate_queries( program_enrollment.id, PercolateQuery.AUTOMATIC_EMAIL_TYPE)) == []
def test_percolate_failure(self, mock_on_commit): """ If search_percolate fails we should raise an Exception with some useful information for Sentry """ failures = [{ "shard": 0, "index": "index", "status": "BAD_REQUEST", "reason": { "type": "parse_exception", "reason": "failed to parse request", "caused_by": { "type": "mapper_parsing_exception", "reason": "Cannot generate dynamic mappings of type [_id] for [_id]" } } }] failure_payload = { "took": 1, "_shards": { "total": 5, "successful": 0, "failed": 5, "failures": failures }, "total": 0, "matches": [] } with mute_signals(post_save): profile = ProfileFactory.create(filled_out=True) program_enrollment = ProgramEnrollmentFactory.create(user=profile.user) with self.assertRaises(PercolateException) as ex, patch( 'search.api.get_conn', return_value=Mock(percolate=Mock( return_value=failure_payload))): search_percolate_queries(program_enrollment.id, "doesnt_matter") assert ex.exception.args[0] == "Failed to percolate: {}".format( failures)
def test_percolate_failure(self, mock_on_commit): """ If search_percolate fails we should raise an Exception with some useful information for Sentry """ failures = [ { "shard": 0, "index": "index", "status": "BAD_REQUEST", "reason": { "type": "parse_exception", "reason": "failed to parse request", "caused_by": { "type": "mapper_parsing_exception", "reason": "Cannot generate dynamic mappings of type [_id] for [_id]" } } } ] failure_payload = { "took": 1, "_shards": { "total": 5, "successful": 0, "failed": 5, "failures": failures }, "total": 0, "matches": [] } with mute_signals(post_save): profile = ProfileFactory.create(filled_out=True) program_enrollment = ProgramEnrollmentFactory.create(user=profile.user) with self.assertRaises(PercolateException) as ex, patch( 'search.api.get_conn', return_value=Mock(percolate=Mock(return_value=failure_payload)) ): search_percolate_queries(program_enrollment.id, "doesnt_matter") assert ex.exception.args[0] == "Failed to percolate: {}".format(failures)
def test_search_percolate_queries(self, mock_on_commit): """search_percolate_queries should find all PercolateQuery which match the given ProgramEnrollment""" with mute_signals(post_save): profile = ProfileFactory.create(filled_out=True) program_enrollment = ProgramEnrollmentFactory.create(user=profile.user) matching_query = { "query": { "match": { "profile.first_name": profile.first_name, } } } query = PercolateQuery.objects.create( query=matching_query, original_query={}, source_type=PercolateQuery.AUTOMATIC_EMAIL_TYPE, ) # Another query which matches but has a different source_type PercolateQuery.objects.create( query=matching_query, original_query={}, source_type=PercolateQuery.DISCUSSION_CHANNEL_TYPE, ) # Another query that doesn't match PercolateQuery.objects.create( query={"query": { "match": { "profile.first_name": "missing", } }}, original_query={}, source_type=PercolateQuery.AUTOMATIC_EMAIL_TYPE) # Only the first appears in the results assert list( search_percolate_queries( program_enrollment.id, PercolateQuery.AUTOMATIC_EMAIL_TYPE, ).values_list("id", flat=True)) == [query.id]
def test_search_percolate_queries(self, mock_on_commit): """search_percolate_queries should find all PercolateQuery which match the given ProgramEnrollment""" with mute_signals(post_save): profile = ProfileFactory.create(filled_out=True) program_enrollment = ProgramEnrollmentFactory.create(user=profile.user) matching_query = { "query": { "match": { "profile.first_name": profile.first_name, } } } query = PercolateQuery.objects.create( query=matching_query, original_query={}, source_type=PercolateQuery.AUTOMATIC_EMAIL_TYPE, ) # Another query which matches but has a different source_type PercolateQuery.objects.create( query=matching_query, original_query={}, source_type=PercolateQuery.DISCUSSION_CHANNEL_TYPE, ) # Another query that doesn't match PercolateQuery.objects.create(query={ "query": { "match": { "profile.first_name": "missing", } } }, original_query={}, source_type=PercolateQuery.AUTOMATIC_EMAIL_TYPE) # Only the first appears in the results assert list( search_percolate_queries( program_enrollment.id, PercolateQuery.AUTOMATIC_EMAIL_TYPE, ).values_list("id", flat=True) ) == [query.id]
def test_not_percolated(self, mock_on_commit): """If there are no percolated queries we should return an empty queryset""" with mute_signals(post_save): profile = ProfileFactory.create(filled_out=True) program_enrollment = ProgramEnrollmentFactory.create(user=profile.user) assert list(search_percolate_queries(program_enrollment.id, PercolateQuery.AUTOMATIC_EMAIL_TYPE)) == []