def launch(request): config = get_tool_conf() if not check_if_success_getting_tool_config(config): return LTIError(config).response_json() CacheConfig = get_cache_config() message_launch = DjangoMessageLaunch( request, config, launch_data_storage=CacheConfig.launch_data_storage) if not CacheConfig.is_dummy_cache: # fetch platform's public key from cache instead of calling the API will speed up the launch process message_launch.set_public_key_caching( CacheConfig.launch_data_storage, cache_lifetime=CacheConfig.cache_lifetime) else: logger.info( 'DummyCache is set up, recommended atleast to us Mysql DB cache for LTI advantage services' ) try: course_id = extract_launch_variables_for_tool_use( request, message_launch) except Exception as e: return LTIError(e).response_json() url = reverse('courses', kwargs={'course_id': course_id}) return redirect(url)
def get_launch_message(self): """ Return the LTI 1.3 launch message object for the current request. """ launch_message = DjangoMessageLaunch( self.request, self.lti_tool_config, launch_data_storage=self.lti_tool_storage) # This will force the LTI launch validation steps. launch_message.get_launch_data() return launch_message
def test_get_members(self): from pylti1p3.contrib.django import DjangoMessageLaunch tool_conf = get_test_tool_conf() with patch.object(DjangoMessageLaunch, "_get_jwt_body", autospec=True) as get_jwt_body: message_launch = DjangoMessageLaunch(FakeRequest(), tool_conf) get_jwt_body.side_effect = lambda x: self._get_jwt_body() with patch('socket.gethostbyname', return_value="127.0.0.1"): with requests_mock.Mocker() as m: m.post(self._get_auth_token_url(), text=json.dumps(self._get_auth_token_response())) m.get(self._get_jwt_body()['https://purl.imsglobal.org/spec/lti-nrps/claim/namesroleservice'] ['context_memberships_url'], text=json.dumps({ 'members': [{ 'status': 'Active', 'user_id': '20eb59f5-26e8-46bc-87b0-57ed54820aeb', 'roles': ['http://purl.imsglobal.org/vocab/lis/v2/membership#Learner'] }], 'id': 'http://canvas.docker/api/lti/courses/1/names_and_roles', 'context': { 'title': 'Test', 'id': '4dde05e8ca1973bcca9bffc13e1548820eee93a3', 'label': 'Test'} }), headers={ 'Status': '200 OK', 'X-Request-Context-Id': 'fb3662e8-527c-4c83-b4d5-b32d247a896c', 'X-XSS-Protection': '1; mode=block', 'X-Content-Type-Options': 'nosniff', 'Transfer-Encoding': 'chunked', 'X-Rate-Limit-Remaining': '600.0', 'X-Runtime': '0.235817', 'Server': 'nginx/1.13.5', 'X-Canvas-Meta': 'o=lti/ims/names_and_roles;n=course_index;t=Course;i=1;b=892132;' 'm=892132;u=0.08;y=0.00;d=0.01;', 'Connection': 'keep-alive', 'ETag': 'W/"a198e0b4e31245287ba175ddf5a9223c"', 'X-Request-Cost': '0.09043669500000007', 'X-UA-Compatible': 'IE=Edge,chrome=1', 'Cache-Control': 'max-age=0, private, must-revalidate', 'Date': 'Mon, 12 Aug 2019 09:50:45 GMT', 'Link': '<http://canvas.docker/api/lti/courses/1/names_and_roles?page=1' '&per_page=10>; rel="current",<http://canvas.docker/api/lti/cou' 'rses/1/names_and_roles?page=1&per_page=10>; rel="first",' '<http://canvas.docker/api/lti/courses/1/names_and_roles' '?page=1&per_page=10>; rel="last"', 'X-Frame-Options': 'SAMEORIGIN', 'Content-Type': 'application/vnd.ims.lti-nrps.v2.membershipcontainer+json; charset=utf-8' }) members = message_launch.validate_registration().get_nrps().get_members() self.assertEqual(len(members), 1) self.assertDictEqual(members[0], { 'status': 'Active', 'user_id': '20eb59f5-26e8-46bc-87b0-57ed54820aeb', 'roles': ['http://purl.imsglobal.org/vocab/lis/v2/membership#Learner'] })
def test_send_scores(self): from pylti1p3.contrib.django import DjangoMessageLaunch tool_conf = get_test_tool_conf() with patch.object(DjangoMessageLaunch, "_get_jwt_body", autospec=True) as get_jwt_body: message_launch = DjangoMessageLaunch(DjangoFakeRequest(), tool_conf) get_jwt_body.side_effect = lambda x: self._get_jwt_body() with patch('socket.gethostbyname', return_value="127.0.0.1"): with requests_mock.Mocker() as m: m.post(self._get_auth_token_url(), text=json.dumps(self._get_auth_token_response())) m.get( 'http://canvas.docker/api/lti/courses/1/line_items', text=json.dumps([{ 'scoreMaximum': 100.0, 'tag': 'score', 'id': 'http://canvas.docker/api/lti/courses/1/line_items/1', 'label': 'Score' }])) expected_result = { 'resultUrl': 'http://canvas.docker/api/lti/courses/1/line_items/1/results/4' } m.post( 'http://canvas.docker/api/lti/courses/1/line_items/1/scores', text=json.dumps(expected_result)) ags = message_launch.validate_registration().get_ags() sub = message_launch.get_launch_data().get('sub') timestamp = datetime.datetime.utcnow().strftime( '%Y-%m-%dT%H:%M:%S+0000') sc = Grade() sc.set_score_given(5) \ .set_score_maximum(100) \ .set_timestamp(timestamp) \ .set_activity_progress('Completed') \ .set_grading_progress('FullyGraded') \ .set_user_id(sub) sc_line_item = LineItem() sc_line_item.set_tag('score') \ .set_score_maximum(100) \ .set_label('Score') resp = ags.put_grade(sc, sc_line_item) self.assertEqual(expected_result, resp['body'])
def _launch(self, request, tool_conf, key_set_url_response=None, force_validation=False): from pylti1p3.contrib.django import DjangoMessageLaunch obj = DjangoMessageLaunch(request, tool_conf) obj.set_jwt_verify_options({ 'verify_aud': False, 'verify_exp': False }) with patch('socket.gethostbyname', return_value="127.0.0.1"): with requests_mock.Mocker() as m: key_set_url_text = key_set_url_response if key_set_url_response else json.dumps(self.jwt_canvas_keys) m.get(TOOL_CONFIG[self.iss]['key_set_url'], text=key_set_url_text) if force_validation: return obj.validate() else: return obj.get_launch_data()
def update_score(self, weighted_earned, weighted_possible, timestamp): """ Use LTI's score service to update the LTI platform's gradebook. This method synchronously send a request to the LTI platform to update the assignment score. """ launch_data = { 'iss': self.profile.platform_id, 'aud': self.profile.client_id, 'https://purl.imsglobal.org/spec/lti-ags/claim/endpoint': { 'lineitem': self.ags_lineitem, 'scope': { 'https://purl.imsglobal.org/spec/lti-ags/scope/lineitem', 'https://purl.imsglobal.org/spec/lti-ags/scope/score', } } } tool_config = DjangoDbToolConf() ags = (DjangoMessageLaunch( request=None, tool_config=tool_config).set_auto_validation(enable=False).set_jwt( { 'body': launch_data }).set_restored().validate_registration().get_ags()) if weighted_possible == 0: weighted_score = 0 else: weighted_score = float(weighted_earned) / float(weighted_possible) ags.put_grade(Grade().set_score_given( weighted_score).set_score_maximum(1).set_timestamp( timestamp.isoformat()).set_activity_progress('Submitted'). set_grading_progress('FullyGraded').set_user_id( self.profile.subject_id))
def test_get_grades(self, name, line_items_exist): # pylint: disable=unused-argument from pylti1p3.contrib.django import DjangoMessageLaunch tool_conf = get_test_tool_conf() with patch.object(DjangoMessageLaunch, "_get_jwt_body", autospec=True) as get_jwt_body: message_launch = DjangoMessageLaunch(FakeRequest(), tool_conf) line_items_url = 'http://canvas.docker/api/lti/courses/1/line_items' get_jwt_body.side_effect = lambda x: self._get_jwt_body() with patch('socket.gethostbyname', return_value="127.0.0.1"): with requests_mock.Mocker() as m: m.post(self._get_auth_token_url(), text=json.dumps(self._get_auth_token_response())) line_items_response = [] if line_items_exist: line_items_response = [{ 'scoreMaximum': 100.0, 'tag': 'score', 'id': 'http://canvas.docker/api/lti/courses/1/line_items/1', 'label': 'Score' }, { 'scoreMaximum': 999.0, 'tag': 'time', 'id': 'http://canvas.docker/api/lti/courses/1/line_items/2', 'label': 'Time Taken' }] else: m.post( line_items_url, text=json.dumps({ 'scoreMaximum': 100.0, 'tag': 'score', 'id': 'http://canvas.docker/api/lti/courses/1/line_items/1', 'label': 'Score' })) m.get(line_items_url, text=json.dumps(line_items_response)) m.get( 'http://canvas.docker/api/lti/courses/1/line_items/1/results', text=json.dumps([{ 'resultScore': 13.0, 'resultMaximum': 100.0, 'userId': '20eb59f5-26e8-46bc-87b0-57ed54820aeb', 'id': 'http://canvas.docker/api/lti/courses/1/line_items/1/results/1', 'scoreOf': 'http://canvas.docker/api/lti/courses/1/line_items/1' }])) score_line_item = LineItem() score_line_item.set_tag('score') \ .set_score_maximum(100) \ .set_label('Score') scores = message_launch.validate_registration().get_ags( ).get_grades(score_line_item) self.assertEqual(len(scores), 1) self.assertDictEqual( scores[0], { 'resultScore': 13.0, 'resultMaximum': 100.0, 'userId': '20eb59f5-26e8-46bc-87b0-57ed54820aeb', 'id': 'http://canvas.docker/api/lti/courses/1/line_items/1/results/1', 'scoreOf': 'http://canvas.docker/api/lti/courses/1/line_items/1' })
def _get_launch_obj(self, request, tool_conf, cache=False): from pylti1p3.contrib.django import DjangoMessageLaunch message_launch = DjangoMessageLaunch(request, tool_conf) if cache: message_launch.set_launch_data_storage(cache) return message_launch