Exemple #1
0
 def test_is_workflow_complete(self):
     self.assertFalse(
         ai_worker_api.is_grading_workflow_complete(self.workflow_uuid))
     workflow = AIGradingWorkflow.objects.get(uuid=self.workflow_uuid)
     workflow.mark_complete_and_save()
     self.assertTrue(
         ai_worker_api.is_grading_workflow_complete(self.workflow_uuid))
Exemple #2
0
def grade_essay(workflow_uuid):
    """
    Asynchronous task to grade an essay using a text classifier
    (trained using a supervised ML algorithm).

    If the task could not be completed successfully,
    it will be retried a few times; if it continues to fail,
    it is left incomplete.  Incomplate tasks can be rescheduled
    manually through the AI API.

    Args:
        workflow_uuid (str): The UUID of the workflow associated
            with this grading task.

    Returns:
        None

    Raises:
        AIError: An error occurred while making an AI worker API call.
        AIAlgorithmError: An error occurred while retrieving or using an AI algorithm.

    """
    # Short-circuit if the workflow is already marked complete
    # This is an optimization, but grading tasks could still
    # execute multiple times depending on when they get picked
    # up by workers and marked complete.
    try:
        if ai_worker_api.is_grading_workflow_complete(workflow_uuid):
            return
    except AIError:
        msg = (
            u"An unexpected error occurred while checking the "
            u"completion of grading workflow with UUID {uuid}"
        ).format(uuid=workflow_uuid)
        logger.exception(msg)
        raise grade_essay.retry()

    # Retrieve the task parameters
    try:
        params = ai_worker_api.get_grading_task_params(workflow_uuid)
        essay_text = params['essay_text']
        classifier_set = params['classifier_set']
        algorithm_id = params['algorithm_id']
        valid_scores = params['valid_scores']
    except (AIError, KeyError):
        msg = (
            u"An error occurred while retrieving the AI grading task "
            u"parameters for the workflow with UUID {}"
        ).format(workflow_uuid)
        logger.exception(msg)
        raise grade_essay.retry()

    # Validate that the we have valid scores for each criterion
    for criterion_name in classifier_set.keys():
        msg = None
        if criterion_name not in valid_scores:
            msg = (
                u"Could not find {criterion} in the list of valid scores "
                u"for grading workflow with UUID {uuid}"
            ).format(criterion=criterion_name, uuid=workflow_uuid)
        elif len(valid_scores[criterion_name]) == 0:
            msg = (
                u"Valid scores for {criterion} is empty for "
                u"grading workflow with UUID {uuid}"
            ).format(criterion=criterion_name, uuid=workflow_uuid)
        if msg:
            logger.exception(msg)
            raise AIGradingInternalError(msg)

    # Retrieve the AI algorithm
    try:
        algorithm = AIAlgorithm.algorithm_for_id(algorithm_id)
    except AIAlgorithmError:
        msg = (
            u"An error occurred while retrieving "
            u"the algorithm ID (grading workflow UUID {})"
        ).format(workflow_uuid)
        logger.exception(msg)
        raise grade_essay.retry()

    # Use the algorithm to evaluate the essay for each criterion
    # Provide an in-memory cache so the algorithm can re-use
    # results for multiple rubric criteria.
    try:
        cache = dict()
        scores_by_criterion = {
            criterion_name: _closest_valid_score(
                algorithm.score(essay_text, classifier, cache),
                valid_scores[criterion_name]
            )
            for criterion_name, classifier in classifier_set.iteritems()
        }
    except AIAlgorithmError:
        msg = (
            u"An error occurred while scoring essays using "
            u"an AI algorithm (worker workflow UUID {})"
        ).format(workflow_uuid)
        logger.exception(msg)
        raise grade_essay.retry()

    # Create the assessment and mark the workflow complete
    try:
        ai_worker_api.create_assessment(workflow_uuid, scores_by_criterion)
    except AIError:
        msg = (
            u"An error occurred while creating assessments "
            u"for the AI grading workflow with UUID {uuid}. "
            u"The assessment scores were: {scores}"
        ).format(uuid=workflow_uuid, scores=scores_by_criterion)
        logger.exception(msg)
        raise grade_essay.retry()
 def test_is_workflow_complete_database_error(self, mock_call):
     mock_call.side_effect = DatabaseError("Oh no!")
     with self.assertRaises(AIGradingInternalError):
         ai_worker_api.is_grading_workflow_complete(self.workflow_uuid)
 def test_is_workflow_complete_no_such_workflow(self):
     with self.assertRaises(AIGradingRequestError):
         ai_worker_api.is_grading_workflow_complete('no such workflow')
 def test_is_workflow_complete(self):
     self.assertFalse(ai_worker_api.is_grading_workflow_complete(self.workflow_uuid))
     workflow = AIGradingWorkflow.objects.get(uuid=self.workflow_uuid)
     workflow.mark_complete_and_save()
     self.assertTrue(ai_worker_api.is_grading_workflow_complete(self.workflow_uuid))
Exemple #6
0
 def test_is_workflow_complete_database_error(self, mock_call):
     mock_call.side_effect = DatabaseError("Oh no!")
     with self.assertRaises(AIGradingInternalError):
         ai_worker_api.is_grading_workflow_complete(self.workflow_uuid)
Exemple #7
0
 def test_is_workflow_complete_no_such_workflow(self):
     with self.assertRaises(AIGradingRequestError):
         ai_worker_api.is_grading_workflow_complete('no such workflow')