示例#1
0
    def test_get_grading_task_params_num_queries(self):
        with self.assertNumQueries(6):
            ai_worker_api.get_grading_task_params(self.workflow_uuid)

        # The second time through we should be caching the queries
        # to determine the valid scores for a classifier
        with self.assertNumQueries(2):
            ai_worker_api.get_grading_task_params(self.workflow_uuid)
示例#2
0
    def test_get_grading_task_params_num_queries(self):
        with self.assertNumQueries(6):
            ai_worker_api.get_grading_task_params(self.workflow_uuid)

        # The second time through we should be caching the queries
        # to determine the valid scores for a classifier
        with self.assertNumQueries(2):
            ai_worker_api.get_grading_task_params(self.workflow_uuid)
示例#3
0
    def test_invalid_classifier_data(self):
        # Modify the classifier data so it is not valid JSON
        invalid_json = "{"
        for classifier in AIClassifier.objects.all():
            classifier.classifier_data.save(uuid4().hex, ContentFile(invalid_json))

        # Should get an error when retrieving task params
        with self.assertRaises(AIGradingInternalError):
            ai_worker_api.get_grading_task_params(self.workflow_uuid)
示例#4
0
    def test_get_grading_task_params_no_classifiers(self):
        # Remove the classifiers from the workflow
        workflow = AIGradingWorkflow.objects.get(uuid=self.workflow_uuid)
        workflow.classifier_set = None
        workflow.save()

        # Should get an error when retrieving task params
        with self.assertRaises(AIGradingInternalError):
            ai_worker_api.get_grading_task_params(self.workflow_uuid)
示例#5
0
    def test_get_grading_task_params_no_classifiers(self):
        # Remove the classifiers from the workflow
        workflow = AIGradingWorkflow.objects.get(uuid=self.workflow_uuid)
        workflow.classifier_set = None
        workflow.save()

        # Should get an error when retrieving task params
        with self.assertRaises(AIGradingInternalError):
            ai_worker_api.get_grading_task_params(self.workflow_uuid)
示例#6
0
    def test_invalid_classifier_data(self):
        # Modify the classifier data so it is not valid JSON
        invalid_json = "{"
        for classifier in AIClassifier.objects.all():
            classifier.classifier_data.save(uuid4().hex,
                                            ContentFile(invalid_json))

        # Should get an error when retrieving task params
        with self.assertRaises(AIGradingInternalError):
            ai_worker_api.get_grading_task_params(self.workflow_uuid)
示例#7
0
 def test_get_grading_task_params(self):
     params = ai_worker_api.get_grading_task_params(self.workflow_uuid)
     expected_params = {
         'essay_text': ANSWER,
         'classifier_set': CLASSIFIERS,
         'algorithm_id': ALGORITHM_ID,
         'valid_scores': {
             u"vøȼȺƀᵾłȺɍɏ": [0, 1, 2],
             u"ﻭɼค๓๓คɼ": [0, 1, 2]
         }
     }
     self.assertItemsEqual(params, expected_params)
示例#8
0
 def test_get_grading_task_params(self):
     params = ai_worker_api.get_grading_task_params(self.workflow_uuid)
     expected_params = {
         'essay_text': ANSWER,
         'classifier_set': CLASSIFIERS,
         'algorithm_id': ALGORITHM_ID,
         'valid_scores': {
             u"vøȼȺƀᵾłȺɍɏ": [0, 1, 2],
             u"ﻭɼค๓๓คɼ": [0, 1, 2]
         }
     }
     self.assertItemsEqual(params, expected_params)
示例#9
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()
示例#10
0
 def test_get_grading_task_params_database_error(self, mock_call):
     mock_call.side_effect = DatabaseError("KABOOM!")
     with self.assertRaises(AIGradingInternalError):
         ai_worker_api.get_grading_task_params(self.submission_uuid)
示例#11
0
 def test_get_grading_task_params_no_workflow(self):
     with self.assertRaises(AIGradingRequestError):
         ai_worker_api.get_grading_task_params("invalid_uuid")
示例#12
0
 def test_get_grading_task_params_database_error(self, mock_call):
     mock_call.side_effect = DatabaseError("KABOOM!")
     with self.assertRaises(AIGradingInternalError):
         ai_worker_api.get_grading_task_params(self.submission_uuid)
示例#13
0
 def test_get_grading_task_params_no_workflow(self):
     with self.assertRaises(AIGradingRequestError):
         ai_worker_api.get_grading_task_params("invalid_uuid")