Exemple #1
0
 def test_parse_delete_result_xml(self):
     '''
     Should parse deleteRequest XML.
     '''
     request = OutcomeRequest()
     request.process_xml(DELETE_RESULT_XML)
     self.assertEqual(request.operation, 'deleteResult')
     self.assertEqual(request.lis_result_sourcedid, '261-154-728-17-784')
     self.assertEqual(request.message_identifier, '123456789')
     self.assertEqual(request.score, None)
Exemple #2
0
 def test_has_required_attributes(self):
     request = OutcomeRequest()
     self.assertFalse(request.has_required_attributes())
     request.consumer_key = 'foo'
     request.consumer_secret = 'bar'
     self.assertFalse(request.has_required_attributes())
     request.lis_outcome_service_url = 'http://example.edu/'
     request.lis_result_sourcedid = 1
     request.operation = 'baz'
     self.assertTrue(request.has_required_attributes())
 def test_parse_delete_result_xml(self):
     '''
     Should parse deleteRequest XML.
     '''
     request = OutcomeRequest()
     request.process_xml(DELETE_RESULT_XML)
     self.assertEqual(request.operation, 'deleteResult')
     self.assertEqual(request.lis_result_sourcedid, '261-154-728-17-784')
     self.assertEqual(request.message_identifier, '123456789')
     self.assertEqual(request.score, None)
 def test_has_required_attributes(self):
     request = OutcomeRequest()
     self.assertFalse(request.has_required_attributes())
     request.consumer_key = 'foo'
     request.consumer_secret = 'bar'
     self.assertFalse(request.has_required_attributes())
     request.lis_outcome_service_url = 'http://example.edu/'
     request.lis_result_sourcedid = 1
     request.operation = 'baz'
     self.assertTrue(request.has_required_attributes())
Exemple #5
0
 def test_post_outcome_request(self):
     request_headers = {"User-Agent": "unit-test"}
     request = OutcomeRequest(headers=request_headers)
     self.assertRaises(InvalidLTIConfigError, request.post_outcome_request)
     request.consumer_key = 'consumer'
     request.consumer_secret = 'secret'
     request.lis_outcome_service_url = 'http://example.edu/'
     request.lis_result_sourcedid = 'foo'
     request.operation = REPLACE_REQUEST
     with HTTMock(response_content):
         resp = request.post_outcome_request(nonce='my_nonce',
                                             timestamp='1234567890')
     self.assertIsInstance(resp, OutcomeResponse)
     request = resp.post_response.request
     self.assertTrue('authorization' in request.headers)
     self.assertEqual(request.headers.get('user-agent'), b"unit-test")
     self.assertEqual(request.headers.get('content-type'),
                      b"application/xml")
     auth_header = unquote(request.headers['authorization'].decode('utf-8'))
     correct = ('OAuth '
                'oauth_nonce="my_nonce", oauth_timestamp="1234567890", '
                'oauth_version="1.0", oauth_signature_method="HMAC-SHA1", '
                'oauth_consumer_key="consumer", '
                'oauth_body_hash="glWvnsZZ8lMif1ATz8Tx64CTTaY=", '
                'oauth_signature="XR6A1CmUauXZdJZXa1pJpTQi6OQ="')
     self.assertEqual(auth_header, correct)
Exemple #6
0
 def test_from_post_request(self):
     factory = RequestFactory()
     post_request = factory.post('/',
                                 data=REPLACE_RESULT_XML,
                                 content_type='application/xml')
     request = OutcomeRequest.from_post_request(post_request)
     self.assertEqual(request.operation, 'replaceResult')
     self.assertEqual(request.lis_result_sourcedid, '261-154-728-17-784')
     self.assertEqual(request.message_identifier, '123456789')
     self.assertEqual(request.score, '5')
 def test_from_post_request(self):
     factory = RequestFactory()
     post_request = factory.post('/',
         data=REPLACE_RESULT_XML,
         content_type='application/xml'
     )
     request = OutcomeRequest.from_post_request(post_request)
     self.assertEqual(request.operation, 'replaceResult')
     self.assertEqual(request.lis_result_sourcedid, '261-154-728-17-784')
     self.assertEqual(request.message_identifier, '123456789')
     self.assertEqual(request.score, '5')
Exemple #8
0
    def post_grades(self, parameters):

        # Secret of the external Tool
        consumer_key = 'aef4193cf141d02a2adde6a9d5afaff028d9bf23139a4d1def1ee224f7682ca0'
        consumer_secret = 'f0b884c83c858fd8078bd187c0c54c281a32df2653401b0c415dec63580dc847'

        # Create POST-Request
        outcome_request = OutcomeRequest({
            'consumer_key':
            consumer_key,
            'consumer_secret':
            consumer_secret,
            'lis_outcome_service_url':
            parameters['lis_outcome_service_url'],
            'lis_result_sourcedid':
            parameters['lis_result_sourcedid']
        })

        # Replace result in Moodle
        outcome_response = outcome_request.post_replace_result(
            parameters['score'])
def update_lms_grades(request=None, sequence=None):
    """Send grade update to LMS (LTI Tool)."""
    outcome_request = OutcomeRequest().from_post_request(
        request) if request else OutcomeRequest()

    outcome_service = sequence.outcome_service
    if outcome_service is None:
        log.info(
            f"Sequence: {sequence} doesn't contain an outcome service, grade is not sent."
        )
        return
    consumer = outcome_service.lms_lti_connection

    outcome_request.consumer_key = consumer.consumer_key
    outcome_request.consumer_secret = consumer.consumer_secret
    outcome_request.lis_outcome_service_url = outcome_service.lis_outcome_service_url
    outcome_request.lis_result_sourcedid = sequence.lis_result_sourcedid

    log.debug(
        "Update LMS grades. Used sequence = {} is completed = {}, grading_policy = {}"
        .format(sequence, sequence.completed,
                sequence.collection_order.grading_policy))

    score = sequence.collection_order.grading_policy.calculate_grade(sequence)
    outcome_request.post_replace_result(score)
    lms_response = outcome_request.outcome_response
    user_id = sequence.lti_user
    if lms_response.is_success():
        log.info(
            "Successfully sent updated grade to LMS. Student:{}, grade:{}, comment: success"
            .format(user_id, score))
    elif lms_response.is_processing():
        log.info(
            "Grade update is being processed by LMS. Student:{}, grade:{}, comment: processing"
            .format(user_id, score))
    elif lms_response.has_warning():
        log.warning(
            "Grade update response has warnings. Student:{}, grade:{}, comment: warning"
            .format(user_id, score))
    else:
        log.error(
            "Grade update request failed. Student:{}, grade:{}, comment:{}".
            format(user_id, score, lms_response.code_major))
Exemple #10
0
def send_grade_update(consumer_key, consumer_secret, lis_outcome_service_url,
                      lis_result_sourcedid, score):
    """
    Send lms grade for an lti component
    :param consumer_key: lti client key
    :param consumer_secret: lti client secret
    :param lis_outcome_service_url: outcome service url to send outcome request to
    :param lis_result_sourcedid: context identifier for consumer to use
    :param score: score between 0.0 and 1.0
    :return:
    """
    outcome_request = OutcomeRequest()
    outcome_request.consumer_key = consumer_key
    outcome_request.consumer_secret = consumer_secret
    outcome_request.lis_outcome_service_url = lis_outcome_service_url
    outcome_request.lis_result_sourcedid = lis_result_sourcedid

    # construct info string for logging
    args = "score={}, lis_outcome_service_url={} lis_result_sourcedid={}, consumer_key={}, consumer_secret={}, ".format(
        score, lis_outcome_service_url, lis_result_sourcedid, consumer_key,
        consumer_secret)

    log.debug("Updating LMS grade, with parameters: {}".format(args))

    # send request to update score
    outcome_request.post_replace_result(score)

    # check out the request response
    lms_response = outcome_request.outcome_response

    # logging
    if lms_response.is_success():
        log.info("Successfully sent updated grade to LMS. {}".format(args))
    elif lms_response.is_processing():
        log.info(
            "Grade update is being processed by LMS. {}, comment: {}".format(
                args, 'processing'))
    elif lms_response.has_warning():
        log.warning(
            "Grade update response has warnings. {}, comment={}".format(
                args, 'processing'))
    else:
        log.error("Grade update request failed. {}, comment={}".format(
            args, lms_response.code_major))

    return lms_response
Exemple #11
0
 def test_from_post_request(self):
     factory = RequestFactory()
     post_request = factory.post('/',
                                 data=REPLACE_RESULT_XML,
                                 content_type='application/xml')
     request_headers = {
         "User-Agent": "post-request",
         "Content-Type": "text/xml"
     }
     request = OutcomeRequest.from_post_request(post_request,
                                                request_headers)
     self.assertEqual(request.operation, 'replaceResult')
     self.assertEqual(request.lis_result_sourcedid, '261-154-728-17-784')
     self.assertEqual(request.message_identifier, '123456789')
     self.assertEqual(request.score, '5')
     self.assertEqual(request.headers.get('User-Agent'), "post-request")
     self.assertEqual(request.headers.get('Content-Type'), "text/xml")
 def test_post_outcome_request(self):
     request = OutcomeRequest()
     self.assertRaises(InvalidLTIConfigError, request.post_outcome_request)
     request.consumer_key = 'consumer'
     request.consumer_secret = 'secret'
     request.lis_outcome_service_url = 'http://example.edu/'
     request.lis_result_sourcedid = 'foo'
     request.operation = REPLACE_REQUEST
     with HTTMock(response_content):
         resp = request.post_outcome_request(
             nonce='my_nonce',
             timestamp='1234567890'
         )
     self.assertIsInstance(resp, OutcomeResponse)
     request = resp.post_response.request
     self.assertTrue('authorization' in request.headers)
     auth_header = unquote(request.headers['authorization'].decode('utf-8'))
     correct = ('OAuth '
         'oauth_nonce="my_nonce", oauth_timestamp="1234567890", '
         'oauth_version="1.0", oauth_signature_method="HMAC-SHA1", '
         'oauth_consumer_key="consumer", '
         'oauth_body_hash="glWvnsZZ8lMif1ATz8Tx64CTTaY=", '
         'oauth_signature="XR6A1CmUauXZdJZXa1pJpTQi6OQ="')
     self.assertEqual(auth_header, correct)
    def run(self):
        # Load old tasks from the database
        for todo in self._database.lis_outcome_queue.find({}):
            self._add_to_queue(todo)

        try:
            while not self._stopped:
                time.sleep(0.5)
                data = self._queue.get()

                mongo_id, username, courseid, taskid, consumer_key, service_url, result_id, nb_attempt = data

                try:
                    course = self._course_factory.get_course(courseid)
                    task = course.get_task(taskid)

                    consumer_secret = course.lti_keys()[consumer_key]

                    grade = self._user_manager.get_task_cache(
                        username, task.get_course_id(), task.get_id())["grade"]
                    grade = grade / 100.0
                    if grade > 1:
                        grade = 1
                    if grade < 0:
                        grade = 0
                except Exception:
                    self._logger.error(
                        "An exception occurred while getting a course/LTI secret/grade in LTIOutcomeManager.",
                        exc_info=True)
                    continue

                try:
                    outcome_response = OutcomeRequest({
                        "consumer_key":
                        consumer_key,
                        "consumer_secret":
                        consumer_secret,
                        "lis_outcome_service_url":
                        service_url,
                        "lis_result_sourcedid":
                        result_id
                    }).post_replace_result(grade)

                    if outcome_response.code_major == "success":
                        self._delete_in_db(mongo_id)
                        self._logger.debug("Successfully sent grade to TC: %s",
                                           str(data))
                        continue
                except Exception:
                    self._logger.error(
                        "An exception occurred while sending a grade to the TC.",
                        exc_info=True)

                if nb_attempt < 5:
                    self._logger.debug(
                        "An error occurred while sending a grade to the TC. Retrying..."
                    )
                    self._increment_attempt(mongo_id)
                else:
                    self._logger.error(
                        "An error occurred while sending a grade to the TC. Maximum number of retries reached."
                    )
                    self._delete_in_db(mongo_id)
        except KeyboardInterrupt:
            pass
Exemple #14
0
def callback_sequence_item_grade(request):
    outcome_response = OutcomeResponse(message_identifier='unknown',
                                       code_major=CODE_MAJOR_CODES[2],
                                       severity=SEVERITY_CODES[0])

    try:
        outcome_request = OutcomeRequest().from_post_request(request)
        score = float(outcome_request.score)
        if not 0.0 <= score <= 1.0:
            raise InvalidLTIConfigError(
                '[LTI] score value is outside the permitted range of 0.0-1.0')
        operation = outcome_request.operation
        if not operation == 'replaceResult':
            raise InvalidLTIConfigError(
                '[LTI] request operation {} cannot be proceed'.format(
                    operation))
    except (InvalidLTIConfigError, ValueError) as err:
        body = escape(request.body) if request.body else ''
        error_message = "Request body XML parsing error: {} {}".format(
            err.message, body)
        log.debug("Failure to archive grade from the source: %s" +
                  error_message)
        outcome_response.description = escape(error_message)
        return HttpResponse(outcome_response.generate_response_xml(),
                            content_type='application/xml')
    sequence_item_id, user_id, _activity, _suffix = outcome_request.lis_result_sourcedid.text.split(
        ':')
    outcome_response.code_major = CODE_MAJOR_CODES[0]
    outcome_response.description = 'Score for {sourced_id} is now {score}'.format(
        sourced_id=outcome_request.lis_result_sourcedid, score=score)
    outcome_response.message_identifier = outcome_request.message_identifier
    outcome_response.operation = operation

    xml = outcome_response.generate_response_xml()
    log.debug(
        "Received CallBack with the submitted answer for sequence item {}.".
        format(sequence_item_id))
    try:
        sequence_item = SequenceItem.objects.get(id=sequence_item_id)
    except SequenceItem.DoesNotExist:
        error_message = "Sequence Item with the ID={} was not found".format(
            sequence_item_id)
        outcome_response.description = escape(error_message)
        log.debug("[LTI] {}".format(error_message))
        return HttpResponseNotFound(outcome_response.generate_response_xml(),
                                    content_type='application/xml')

    sequence_item.score = score
    sequence_item.save()

    log.debug("[LTI] Sequence item {} grade is updated".format(sequence_item))
    last_log_submit = Log.objects.filter(sequence_item=sequence_item,
                                         log_type='S').last()
    attempt = (last_log_submit.attempt if last_log_submit else 0) + 1
    correct = bool(score)
    Log.objects.create(
        sequence_item=sequence_item,
        log_type=Log.SUBMITTED,
        answer=correct,
        attempt=attempt,
    )
    log.debug(
        "New Log is created log_type: 'Submitted', attempt: {}, correct: {}, sequence is completed: {}"
        .format(attempt, correct, sequence_item.sequence.completed))
    message_to_consumer = {"sequence_status": "updated"}
    sequence = sequence_item.sequence
    if sequence_item.sequence.collection_order.ui_option:
        ui_details = sequence_item.sequence.sequence_ui_details()
        message_to_consumer["ui_details"] = ui_details

    NextButtonConsumer.send_message_to_channel(
        f'{sequence_item.id}_{sequence_item.position}', message_to_consumer)
    if sequence.lis_result_sourcedid:
        policy = sequence.collection_order.grading_policy.policy_instance(
            sequence=sequence, request=request, user_id=user_id)
        policy.send_grade()

    return HttpResponse(xml, content_type="application/xml")