Example #1
0
 def test_send_email_retried_subtask(self):
     # test at a lower level, to ensure that the course gets checked down below too.
     entry = InstructorTask.create(self.course.id, "task_type", "task_key",
                                   "task_input", self.instructor)
     entry_id = entry.id  # pylint: disable=no-member
     subtask_id = "subtask-id-value"
     initialize_subtask_info(entry, "emailed", 100, [subtask_id])
     subtask_status = SubtaskStatus.create(subtask_id,
                                           state=RETRY,
                                           retried_nomax=2)
     update_subtask_status(entry_id, subtask_id, subtask_status)
     bogus_email_id = 1001
     to_list = ['*****@*****.**']
     global_email_context = {'course_title': 'dummy course'}
     # try running with a clean subtask:
     new_subtask_status = SubtaskStatus.create(subtask_id)
     with self.assertRaisesRegexp(DuplicateTaskException,
                                  'already retried'):
         send_course_email(entry_id, bogus_email_id,
                           to_list, global_email_context,
                           new_subtask_status.to_dict())
     # try again, with a retried subtask with lower count:
     new_subtask_status = SubtaskStatus.create(subtask_id,
                                               state=RETRY,
                                               retried_nomax=1)
     with self.assertRaisesRegexp(DuplicateTaskException,
                                  'already retried'):
         send_course_email(entry_id, bogus_email_id,
                           to_list, global_email_context,
                           new_subtask_status.to_dict())
Example #2
0
def _reserve_task(course_id, task_type, task_key, task_input, requester):
    """
    Creates a database entry to indicate that a task is in progress.

    Throws AlreadyRunningError if the task is already in progress.
    Includes the creation of an arbitrary value for task_id, to be
    submitted with the task call to celery.

    Note that there is a chance of a race condition here, when two users
    try to run the same task at almost exactly the same time.  One user
    could be after the check and before the create when the second user
    gets to the check.  At that point, both users are able to run their
    tasks simultaneously.  This is deemed a small enough risk to not
    put in further safeguards.
    """

    if _task_is_running(course_id, task_type, task_key):
        log.warning("Duplicate task found for task_type %s and task_key %s", task_type, task_key)
        raise AlreadyRunningError("requested task is already running")

    try:
        most_recent_id = InstructorTask.objects.latest('id').id
    except InstructorTask.DoesNotExist:
        most_recent_id = "None found"
    finally:
        log.warning(
            "No duplicate tasks found: task_type %s, task_key %s, and most recent task_id = %s",
            task_type,
            task_key,
            most_recent_id
        )

    # Create log entry now, so that future requests will know it's running.
    return InstructorTask.create(course_id, task_type, task_key, task_input, requester)
Example #3
0
def _reserve_task(course_id, task_type, task_key, task_input, requester):
    """
    Creates a database entry to indicate that a task is in progress.

    Throws AlreadyRunningError if the task is already in progress.
    Includes the creation of an arbitrary value for task_id, to be
    submitted with the task call to celery.

    The InstructorTask.create method makes sure the InstructorTask entry is committed.
    When called from any view that is wrapped by TransactionMiddleware,
    and thus in a "commit-on-success" transaction, an autocommit buried within here
    will cause any pending transaction to be committed by a successful
    save here.  Any future database operations will take place in a
    separate transaction.

    Note that there is a chance of a race condition here, when two users
    try to run the same task at almost exactly the same time.  One user
    could be after the check and before the create when the second user
    gets to the check.  At that point, both users are able to run their
    tasks simultaneously.  This is deemed a small enough risk to not
    put in further safeguards.
    """

    if _task_is_running(course_id, task_type, task_key):
        raise AlreadyRunningError("requested task is already running")

    # Create log entry now, so that future requests will know it's running.
    return InstructorTask.create(course_id, task_type, task_key, task_input, requester)
Example #4
0
def _reserve_task(course_id, task_type, task_key, task_input, requester):
    """
    Creates a database entry to indicate that a task is in progress.

    Throws AlreadyRunningError if the task is already in progress.
    Includes the creation of an arbitrary value for task_id, to be
    submitted with the task call to celery.

    Note that there is a chance of a race condition here, when two users
    try to run the same task at almost exactly the same time.  One user
    could be after the check and before the create when the second user
    gets to the check.  At that point, both users are able to run their
    tasks simultaneously.  This is deemed a small enough risk to not
    put in further safeguards.
    """

    if _task_is_running(course_id, task_type, task_key):
        log.warning("Duplicate task found for task_type %s and task_key %s",
                    task_type, task_key)
        raise AlreadyRunningError("requested task is already running")

    try:
        most_recent_id = InstructorTask.objects.latest('id').id
    except InstructorTask.DoesNotExist:
        most_recent_id = "None found"
    finally:
        log.warning(
            "No duplicate tasks found: task_type %s, task_key %s, and most recent task_id = %s",
            task_type, task_key, most_recent_id)

    # Create log entry now, so that future requests will know it's running.
    return InstructorTask.create(course_id, task_type, task_key, task_input,
                                 requester)
Example #5
0
 def test_nonexistent_to_option(self):
     """
     Tests exception when the to_option in the email doesn't exist
     """
     email = CourseEmail(course_id=self.course.id, to_option="IDONTEXIST")
     email.save()
     entry = InstructorTask.create(self.course.id, "task_type", "task_key", "task_input", self.instructor)
     task_input = {"email_id": email.id}  # pylint: disable=no-member
     with self.assertRaisesRegexp(Exception, 'Unexpected bulk email TO_OPTION found: IDONTEXIST'):
         perform_delegate_email_batches(entry.id, self.course.id, task_input, "action_name")  # pylint: disable=no-member
Example #6
0
 def test_wrong_course_id_in_email(self):
     """
     Tests exception when the course_id in CourseEmail is not the same as one explicitly passed in.
     """
     email = CourseEmail(course_id=SlashSeparatedCourseKey("bogus", "course", "id"), to_option=SEND_TO_ALL)
     email.save()
     entry = InstructorTask.create(self.course.id, "task_type", "task_key", "task_input", self.instructor)
     task_input = {"email_id": email.id}  # pylint: disable=no-member
     with self.assertRaisesRegexp(ValueError, 'does not match email value'):
         perform_delegate_email_batches(entry.id, self.course.id, task_input, "action_name")  # pylint: disable=no-member
 def test_wrong_course_id_in_task(self):
     """
     Tests exception when the course_id in task is not the same as one explicitly passed in.
     """
     email = CourseEmail(course_id=self.course.id, to_option=SEND_TO_ALL)
     email.save()
     entry = InstructorTask.create("bogus/task/id", "task_type", "task_key", "task_input", self.instructor)
     task_input = {"email_id": email.id}  # pylint: disable=E1101
     with self.assertRaisesRegexp(ValueError, "does not match task value"):
         perform_delegate_email_batches(entry.id, self.course.id, task_input, "action_name")  # pylint: disable=E1101
Example #8
0
 def test_wrong_course_id_in_task(self):
     """
     Tests exception when the course_id in task is not the same as one explicitly passed in.
     """
     email = CourseEmail(course_id=self.course.id, to_option=SEND_TO_ALL)
     email.save()
     entry = InstructorTask.create("bogus/task/id", "task_type", "task_key", "task_input", self.instructor)
     task_input = {"email_id": email.id}
     with self.assertRaisesRegexp(ValueError, 'does not match task value'):
         perform_delegate_email_batches(entry.id, self.course.id, task_input, "action_name")
Example #9
0
 def test_send_email_undefined_subtask(self):
     # test at a lower level, to ensure that the course gets checked down below too.
     entry = InstructorTask.create(self.course.id, "task_type", "task_key", "task_input", self.instructor)
     entry_id = entry.id  # pylint: disable=no-member
     to_list = ['*****@*****.**']
     global_email_context = {'course_title': 'dummy course'}
     subtask_id = "subtask-id-value"
     subtask_status = SubtaskStatus.create(subtask_id)
     email_id = 1001
     with self.assertRaisesRegexp(DuplicateTaskException, 'unable to find subtasks of instructor task'):
         send_course_email(entry_id, email_id, to_list, global_email_context, subtask_status.to_dict())
Example #10
0
 def test_nonexistent_course(self):
     """
     Tests exception when the course in the email doesn't exist
     """
     course_id = "I/DONT/EXIST"
     email = CourseEmail(course_id=course_id)
     email.save()
     entry = InstructorTask.create(course_id, "task_type", "task_key", "task_input", self.instructor)
     task_input = {"email_id": email.id}  # pylint: disable=E1101
     with self.assertRaisesRegexp(ValueError, "Course not found"):
         perform_delegate_email_batches(entry.id, course_id, task_input, "action_name")  # pylint: disable=E1101
Example #11
0
 def test_nonexistent_course(self):
     """
     Tests exception when the course in the email doesn't exist
     """
     course_id = SlashSeparatedCourseKey("I", "DONT", "EXIST")
     email = CourseEmail(course_id=course_id)
     email.save()
     entry = InstructorTask.create(course_id, "task_type", "task_key", "task_input", self.instructor)
     task_input = {"email_id": email.id}  # pylint: disable=no-member
     # (?i) is a regex for ignore case
     with self.assertRaisesRegexp(ValueError, r"(?i)course not found"):
         perform_delegate_email_batches(entry.id, course_id, task_input, "action_name")  # pylint: disable=no-member
Example #12
0
 def test_nonexistent_course(self):
     """
     Tests exception when the course in the email doesn't exist
     """
     course_id = "I/DONT/EXIST"
     email = CourseEmail(course_id=course_id)
     email.save()
     entry = InstructorTask.create(course_id, "task_type", "task_key",
                                   "task_input", self.instructor)
     task_input = {"email_id": email.id}  # pylint: disable=E1101
     with self.assertRaisesRegexp(ValueError, "Course not found"):
         perform_delegate_email_batches(entry.id, course_id, task_input,
                                        "action_name")  # pylint: disable=E1101
Example #13
0
 def test_wrong_course_id_in_email(self):
     """
     Tests exception when the course_id in CourseEmail is not the same as one explicitly passed in.
     """
     email = CourseEmail.create(
         SlashSeparatedCourseKey("bogus", "course", "id"), self.instructor,
         [SEND_TO_MYSELF], "re: subject", "dummy body goes here")
     entry = InstructorTask.create(self.course.id, "task_type", "task_key",
                                   "task_input", self.instructor)
     task_input = {"email_id": email.id}
     with self.assertRaisesRegexp(ValueError, 'does not match email value'):
         perform_delegate_email_batches(entry.id, self.course.id,
                                        task_input, "action_name")
Example #14
0
 def test_send_email_missing_subtask(self):
     # test at a lower level, to ensure that the course gets checked down below too.
     entry = InstructorTask.create(self.course.id, "task_type", "task_key", "task_input", self.instructor)
     entry_id = entry.id  # pylint: disable=no-member
     to_list = ['*****@*****.**']
     global_email_context = {'course_title': 'dummy course'}
     subtask_id = "subtask-id-value"
     initialize_subtask_info(entry, "emailed", 100, [subtask_id])
     different_subtask_id = "bogus-subtask-id-value"
     subtask_status = SubtaskStatus.create(different_subtask_id)
     bogus_email_id = 1001
     with self.assertRaisesRegexp(DuplicateTaskException, 'unable to find status for subtask of instructor task'):
         send_course_email(entry_id, bogus_email_id, to_list, global_email_context, subtask_status.to_dict())
Example #15
0
 def test_send_email_running_subtask(self):
     # test at a lower level, to ensure that the course gets checked down below too.
     entry = InstructorTask.create(self.course.id, "task_type", "task_key", "task_input", self.instructor)
     entry_id = entry.id  # pylint: disable=no-member
     subtask_id = "subtask-id-value"
     initialize_subtask_info(entry, "emailed", 100, [subtask_id])
     subtask_status = SubtaskStatus.create(subtask_id)
     update_subtask_status(entry_id, subtask_id, subtask_status)
     check_subtask_is_valid(entry_id, subtask_id, subtask_status)
     bogus_email_id = 1001
     to_list = ['*****@*****.**']
     global_email_context = {'course_title': 'dummy course'}
     with self.assertRaisesRegexp(DuplicateTaskException, 'already being executed'):
         send_course_email(entry_id, bogus_email_id, to_list, global_email_context, subtask_status.to_dict())
 def test_send_email_completed_subtask(self):
     # test at a lower level, to ensure that the course gets checked down below too.
     entry = InstructorTask.create(self.course.id, "task_type", "task_key", "task_input", self.instructor)
     entry_id = entry.id  # pylint: disable=E1101
     subtask_id = "subtask-id-value"
     initialize_subtask_info(entry, "emailed", 100, [subtask_id])
     subtask_status = SubtaskStatus.create(subtask_id, state=SUCCESS)
     update_subtask_status(entry_id, subtask_id, subtask_status)
     bogus_email_id = 1001
     to_list = ["*****@*****.**"]
     global_email_context = {"course_title": "dummy course"}
     new_subtask_status = SubtaskStatus.create(subtask_id)
     with self.assertRaisesRegexp(DuplicateTaskException, "already completed"):
         send_course_email(entry_id, bogus_email_id, to_list, global_email_context, new_subtask_status.to_dict())
Example #17
0
 def test_send_email_undefined_email(self):
     # test at a lower level, to ensure that the course gets checked down below too.
     entry = InstructorTask.create(self.course.id, "task_type", "task_key", "task_input", self.instructor)
     entry_id = entry.id  # pylint: disable=E1101
     to_list = ['*****@*****.**']
     global_email_context = {'course_title': 'dummy course'}
     subtask_id = "subtask-id-value"
     subtask_status = create_subtask_status(subtask_id)
     bogus_email_id = 1001
     with self.assertRaises(CourseEmail.DoesNotExist):
         # we skip the call that updates subtask status, since we've not set up the InstructorTask
         # for the subtask, and it's not important to the test.
         with patch('bulk_email.tasks.update_subtask_status'):
             send_course_email(entry_id, bogus_email_id, to_list, global_email_context, subtask_status)
Example #18
0
 def test_nonexistent_email(self, mock_log, result):
     """
     Tests retries when the email doesn't exist
     """
     # create an InstructorTask object to pass through
     course_id = self.course.id
     entry = InstructorTask.create(course_id, "task_type", "task_key", "task_input", self.instructor)
     task_input = {"email_id": -1}
     with self.assertRaises(CourseEmail.DoesNotExist):
         perform_delegate_email_batches(entry.id, course_id, task_input, "action_name")  # pylint: disable=no-member
     ((log_str, __, email_id), __) = mock_log.warning.call_args
     self.assertTrue(mock_log.warning.called)
     self.assertIn('Failed to get CourseEmail with id', log_str)
     self.assertEqual(email_id, -1)
     self.assertFalse(result.called)
Example #19
0
 def test_send_email_undefined_email(self):
     # test at a lower level, to ensure that the course gets checked down below too.
     entry = InstructorTask.create(self.course.id, "task_type", "task_key", "task_input", self.instructor)
     entry_id = entry.id  # pylint: disable=no-member
     to_list = ['*****@*****.**']
     global_email_context = {'course_title': 'dummy course'}
     subtask_id = "subtask-id-undefined-email"
     initialize_subtask_info(entry, "emailed", 100, [subtask_id])
     subtask_status = SubtaskStatus.create(subtask_id)
     bogus_email_id = 1001
     with self.assertRaises(CourseEmail.DoesNotExist):
         # we skip the call that updates subtask status, since we've not set up the InstructorTask
         # for the subtask, and it's not important to the test.
         with patch('bulk_email.tasks.update_subtask_status'):
             send_course_email(entry_id, bogus_email_id, to_list, global_email_context, subtask_status.to_dict())
Example #20
0
 def test_send_email_with_locked_instructor_task(self):
     # test at a lower level, to ensure that the course gets checked down below too.
     entry = InstructorTask.create(self.course.id, "task_type", "task_key", "task_input", self.instructor)
     entry_id = entry.id  # pylint: disable=no-member
     subtask_id = "subtask-id-locked-model"
     initialize_subtask_info(entry, "emailed", 100, [subtask_id])
     subtask_status = SubtaskStatus.create(subtask_id)
     bogus_email_id = 1001
     to_list = ['*****@*****.**']
     global_email_context = {'course_title': 'dummy course'}
     with patch('instructor_task.subtasks.InstructorTask.save') as mock_task_save:
         mock_task_save.side_effect = DatabaseError
         with self.assertRaises(DatabaseError):
             send_course_email(entry_id, bogus_email_id, to_list, global_email_context, subtask_status.to_dict())
         self.assertEquals(mock_task_save.call_count, MAX_DATABASE_LOCK_RETRIES)
 def test_send_email_undefined_email(self):
     # test at a lower level, to ensure that the course gets checked down below too.
     entry = InstructorTask.create(self.course.id, "task_type", "task_key", "task_input", self.instructor)
     entry_id = entry.id  # pylint: disable=E1101
     to_list = ["*****@*****.**"]
     global_email_context = {"course_title": "dummy course"}
     subtask_id = "subtask-id-undefined-email"
     initialize_subtask_info(entry, "emailed", 100, [subtask_id])
     subtask_status = SubtaskStatus.create(subtask_id)
     bogus_email_id = 1001
     with self.assertRaises(CourseEmail.DoesNotExist):
         # we skip the call that updates subtask status, since we've not set up the InstructorTask
         # for the subtask, and it's not important to the test.
         with patch("bulk_email.tasks.update_subtask_status"):
             send_course_email(entry_id, bogus_email_id, to_list, global_email_context, subtask_status.to_dict())
 def test_wrong_course_id_in_email(self):
     """
     Tests exception when the course_id in CourseEmail is not the same as one explicitly passed in.
     """
     email = CourseEmail.create(
         SlashSeparatedCourseKey("bogus", "course", "id"),
         self.instructor,
         [SEND_TO_MYSELF],
         "re: subject",
         "dummy body goes here"
     )
     entry = InstructorTask.create(self.course.id, "task_type", "task_key", "task_input", self.instructor)
     task_input = {"email_id": email.id}
     with self.assertRaisesRegexp(ValueError, 'does not match email value'):
         perform_delegate_email_batches(entry.id, self.course.id, task_input, "action_name")
Example #23
0
 def test_send_email_retried_subtask(self):
     # test at a lower level, to ensure that the course gets checked down below too.
     entry = InstructorTask.create(self.course.id, "task_type", "task_key", "task_input", self.instructor)
     entry_id = entry.id  # pylint: disable=no-member
     subtask_id = "subtask-id-value"
     initialize_subtask_info(entry, "emailed", 100, [subtask_id])
     subtask_status = SubtaskStatus.create(subtask_id, state=RETRY, retried_nomax=2)
     update_subtask_status(entry_id, subtask_id, subtask_status)
     bogus_email_id = 1001
     to_list = ['*****@*****.**']
     global_email_context = {'course_title': 'dummy course'}
     # try running with a clean subtask:
     new_subtask_status = SubtaskStatus.create(subtask_id)
     with self.assertRaisesRegexp(DuplicateTaskException, 'already retried'):
         send_course_email(entry_id, bogus_email_id, to_list, global_email_context, new_subtask_status.to_dict())
     # try again, with a retried subtask with lower count:
     new_subtask_status = SubtaskStatus.create(subtask_id, state=RETRY, retried_nomax=1)
     with self.assertRaisesRegexp(DuplicateTaskException, 'already retried'):
         send_course_email(entry_id, bogus_email_id, to_list, global_email_context, new_subtask_status.to_dict())