From 50eaaaa449b5b8fe68d0909555335c3bd5372352 Mon Sep 17 00:00:00 2001 From: Jake Noble Date: Tue, 24 Jun 2014 13:04:11 -0500 Subject: [PATCH 1/3] updates to remote_lrs --- test/remote_lrs_test.py | 148 ++++++++++++++++++++++++++++++++++++--- tincan/remote_lrs.py | 15 ++-- tincan/statement_list.py | 27 +++++++ 3 files changed, 171 insertions(+), 19 deletions(-) create mode 100644 tincan/statement_list.py diff --git a/test/remote_lrs_test.py b/test/remote_lrs_test.py index 57b08b1..b606883 100644 --- a/test/remote_lrs_test.py +++ b/test/remote_lrs_test.py @@ -18,6 +18,8 @@ from tincan.context import Context from tincan.context_activities import ContextActivities from tincan.score import Score +from tincan.group import Group +from tincan.base import Base from tincan.result import Result from tincan.substatement import Substatement from tincan.statement_ref import StatementRef @@ -52,6 +54,8 @@ def setUp(self): display=LanguageMap({"en-US": "experienced"}) ) + self.group = Group(member=[self.agent, self.agent2]) + self.activity = Activity( id="http://tincanapi.com/TinCanPython/Test/Unit/0", definition=ActivityDefinition() @@ -130,8 +134,8 @@ def test_save_statement(self): self.assertIsInstance(response, LRSResponse) self.assertTrue(response.success) - self.assertEqual(statement, response.content) self.assertIsNotNone(response.content.id) + self.shallow_compare(statement, response.content) def test_save_statement_with_id(self): statement = Statement( @@ -144,21 +148,21 @@ def test_save_statement_with_id(self): self.assertIsInstance(response, LRSResponse) self.assertTrue(response.success) - self.assertEqual(statement, response.content) + self.shallow_compare(statement, response.content, True) def test_save_statement_conflict(self): - test_id = str(uuid.uuid4()) + test_id = unicode(uuid.uuid4()) statement1 = Statement( actor=self.agent, verb=self.verb, - object=self.statement_ref, + object=self.substatement, id=test_id ) statement2 = Statement( actor=self.agent2, verb=self.verb, - object=self.statement_ref, + object=self.substatement, id=test_id ) response = self.lrs.save_statement(statement1) @@ -183,7 +187,20 @@ def test_save_statement_ref(self): self.assertIsInstance(response, LRSResponse) self.assertTrue(response.success) - self.assertEqual(statement, response.content) + self.shallow_compare(statement, response.content, True) + + def test_save_statement_group(self): + statement = Statement( + actor=self.agent, + verb=self.verb, + object=self.group, + id=str(uuid.uuid4()) + ) + response = self.lrs.save_statement(statement) + + self.assertIsInstance(response, LRSResponse) + self.assertTrue(response.success) + self.shallow_compare(statement, response.content, True) def test_save_statement_substatement(self): statement = Statement( @@ -196,7 +213,7 @@ def test_save_statement_substatement(self): self.assertIsInstance(response, LRSResponse) self.assertTrue(response.success) - self.assertEqual(statement, response.content) + self.shallow_compare(statement, response.content, True) def test_save_statements(self): statement1 = Statement( @@ -210,14 +227,15 @@ def test_save_statements(self): object=self.activity, context=self.context ) + #TODO:statements objects that are passed have their id's updated? response = self.lrs.save_statements([statement1, statement2]) self.assertIsInstance(response, LRSResponse) self.assertTrue(response.success) self.assertIsNotNone(response.content[0].id) self.assertIsNotNone(response.content[1].id) - self.assertEquals(statement1, response.content[0]) - self.assertEquals(statement2, response.content[1]) + self.shallow_compare(statement1, response.content[0]) + self.shallow_compare(statement2, response.content[1]) def test_retrieve_statement(self): statement = Statement( @@ -234,11 +252,38 @@ def test_retrieve_statement(self): response = self.lrs.retrieve_statement(save_resp.content.id) self.assertIsInstance(response, LRSResponse) self.assertTrue(response.success) - self.assertEquals(statement, response.content) + self.shallow_compare(statement, response.content) else: print "test_retrieve_statement: save_statement failed" def test_query_statements(self): + s1 = Statement( + actor=self.agent, + verb=self.verb, + object=self.parent, + result=self.result, + id=str(uuid.uuid4()) + ) + self.lrs.save_statement(s1) + + s2 = Statement( + actor=self.agent, + verb=self.verb, + object=self.parent, + result=self.result, + id=str(uuid.uuid4()) + ) + self.lrs.save_statement(s2) + + s3 = Statement( + actor=self.agent, + verb=self.verb, + object=self.parent, + result=self.result, + id=str(uuid.uuid4()) + ) + self.lrs.save_statement(s3) + query = { "agent": self.agent, "verb": self.verb, @@ -246,23 +291,95 @@ def test_query_statements(self): "related_activities": True, "related_agents": True, "format": "ids", - "limit": 10 + "limit": 2 + } + response = self.lrs.query_statements(query) + + print "++++++++++++++++++++++++++++++++++" + if hasattr(response.request, 'content'): + print response.request.content + print "----------------------------------" + print response.data + print "++++++++++++++++++++++++++++++++++" + + self.assertIsInstance(response, LRSResponse) + self.assertTrue(response.success) + self.assertIsInstance(response.content, StatementsResult) + self.assertTrue(hasattr(response.content, 'more')) + self.assertIsNotNone(response.content.more) + self.shallow_compare(s1, response.content.statements[0]) + self.shallow_compare(s2, response.content.statements[1]) + + def test_query_none(self): + query = { + "agent": Agent(mbox="unique@tincanapi.com"), + "verb": self.verb, + "activity": self.parent, + "related_activities": True, + "related_agents": True, + "format": "ids", + "limit": 2 } response = self.lrs.query_statements(query) self.assertIsInstance(response, LRSResponse) self.assertTrue(response.success) self.assertIsInstance(response.content, StatementsResult) + self.assertTrue(hasattr(response.content, 'more')) + self.assertTrue(hasattr(response.content, 'statements')) + self.assertEquals(response.content.statements, []) def test_more_statements(self): + s1 = Statement( + actor=self.agent, + verb=self.verb, + object=self.parent, + result=self.result, + id=str(uuid.uuid4()) + ) + self.lrs.save_statement(s1) + + s2 = Statement( + actor=self.agent, + verb=self.verb, + object=self.parent, + result=self.result, + id=str(uuid.uuid4()) + ) + self.lrs.save_statement(s2) + + s3 = Statement( + actor=self.agent, + verb=self.verb, + object=self.parent, + result=self.result, + id=str(uuid.uuid4()) + ) + self.lrs.save_statement(s3) + query = { + "agent": self.agent, + "verb": self.verb, + "activity": self.parent, + "related_activities": True, + "related_agents": True, "format": "ids", "limit": 2 } query_resp = self.lrs.query_statements(query) + self.assertIsInstance(query_resp, LRSResponse) + if query_resp.success and query_resp.content.more is not None: response = self.lrs.more_statements(query_resp.content) + + print "++++++++++++++++++++++++++++++++++" + if hasattr(response.request, 'content'): + print response.request.content + print "----------------------------------" + print response.data + print "++++++++++++++++++++++++++++++++++" + self.assertIsInstance(response, LRSResponse) self.assertTrue(response.success) self.assertIsInstance(response.content, StatementsResult) @@ -369,6 +486,15 @@ def test_delete_agent_profile(self): self.assertIsInstance(response, LRSResponse) self.assertTrue(response.success) + def shallow_compare(self, s1, s2, compare_ids=False): + for k, v in vars(s1).iteritems(): + if not k == '_id' or compare_ids: + self.assertTrue(hasattr(s2, k)) + if isinstance(v, Base): + self.shallow_compare(v, getattr(s2, k)) + else: + self.assertEqual(v, getattr(s2, k)) + if __name__ == "__main__": suite = unittest.TestLoader().loadTestsFromTestCase(RemoteLRSTest) unittest.TextTestRunner(verbosity=2).run(suite) diff --git a/tincan/remote_lrs.py b/tincan/remote_lrs.py index ed60d75..4c52c98 100644 --- a/tincan/remote_lrs.py +++ b/tincan/remote_lrs.py @@ -16,10 +16,11 @@ import urllib import json import base64 - from urlparse import urlparse + from tincan.lrs_response import LRSResponse from tincan.http_request import HTTPRequest +from tincan.statement_list import StatementList from tincan.agent import Agent from tincan.statement import Statement from tincan.activity import Activity @@ -204,14 +205,12 @@ def save_statements(self, statements): """Save statements to LRS and update their statement id's :param statements: A list of statement objects to be saved - :type statements: list + :type statements: :class:`StatementList` :return: LRS Response object with the saved list of statements as content :rtype: :class:`tincan.lrs_response.LRSResponse` """ - def make_statement(st): - return st if isinstance(st, Statement) else Statement(st) - - statements = [make_statement(s) for s in statements] + if not isinstance(statements, StatementList): + statements = StatementList(statements) request = HTTPRequest( endpoint=self.endpoint, @@ -220,7 +219,7 @@ def make_statement(st): ) request.headers["Content-Type"] = "application/json" - request.content = json.dumps([s.to_json(self.version) for s in statements]) + request.content = statements.to_json() lrs_response = self._send_request(request) @@ -344,7 +343,7 @@ def more_statements(self, more_url): lrs_response = self._send_request(request) if lrs_response.success: - lrs_response.content = StatementsResult(json.loads(lrs_response.data)) + lrs_response.content = StatementsResult.from_json(lrs_response.data) return lrs_response diff --git a/tincan/statement_list.py b/tincan/statement_list.py new file mode 100644 index 0000000..e0a501c --- /dev/null +++ b/tincan/statement_list.py @@ -0,0 +1,27 @@ +# Copyright 2014 Rustici Software +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from tincan.statement import Statement +from tincan.typed_list import TypedList + +""" +.. module:: statement_list + :synopsis: A type checked Statement list + +""" + + +class StatementList(TypedList): + + _cls = Statement \ No newline at end of file From 0f63bd1d7ee48d7ff82c831fc38b526ff8912c87 Mon Sep 17 00:00:00 2001 From: Jake Noble Date: Tue, 24 Jun 2014 13:56:24 -0500 Subject: [PATCH 2/3] fixed save more statments issue --- test/remote_lrs_test.py | 172 ++++++++++++++++++---------------------- tincan/remote_lrs.py | 8 +- 2 files changed, 82 insertions(+), 98 deletions(-) diff --git a/test/remote_lrs_test.py b/test/remote_lrs_test.py index b606883..c519f71 100644 --- a/test/remote_lrs_test.py +++ b/test/remote_lrs_test.py @@ -35,71 +35,69 @@ class RemoteLRSTest(unittest.TestCase): def setUp(self): - if not hasattr(self, "set"): - self.endpoint = lrs_properties.endpoint - self.version = lrs_properties.version - self.username = lrs_properties.username - self.password = lrs_properties.password - self.lrs = RemoteLRS( - version=self.version, - endpoint=self.endpoint, - username=self.username, - password=self.password, - ) - - self.agent = Agent(mbox="mailto:tincanpython@tincanapi.com") - self.agent2 = Agent(mbox="Agent2.mailto:tincanpython@tincanapi.com") - self.verb = Verb( - id="http://adlnet.gov/expapi/verbs/experienced", - display=LanguageMap({"en-US": "experienced"}) - ) - - self.group = Group(member=[self.agent, self.agent2]) - - self.activity = Activity( - id="http://tincanapi.com/TinCanPython/Test/Unit/0", - definition=ActivityDefinition() - ) - self.activity.definition.type = "http://id.tincanapi.com/activitytype/unit-test" - self.activity.definition.name = LanguageMap({"en-US": "Python Tests"}) - self.activity.definition.description = LanguageMap( - {"en-US": "Unit test in the test suite for the Python library"} - ) - - self.parent = Activity( - id="http://tincanapi.com/TinCanPython/Test", - definition=ActivityDefinition()) - self.activity.definition.type = "http://id.tincanapi.com/activitytype/unit-test-suite" - self.parent.definition.name = LanguageMap({"en-US": "Python Tests"}) - self.parent.definition.description = LanguageMap( - {"en-US": "Unit test in the test suite for the Python library"} - ) - - self.statement_ref = StatementRef(id=uuid.uuid4()) - - self.context = Context(registration=uuid.uuid4(), statement=self.statement_ref) - #self.context.context_activities = ContextActivities(parent=[self.parent]) - - self.score = Score( - raw=97, - scaled=0.97, - max=100, - min=0 - ) - - self.result = Result( - score=self.score, - success=True, - completion=True, - duration="PT120S" - ) - - self.substatement = Substatement( - actor=self.agent, - verb=self.verb, - object=self.activity, - ) - self.set = True + self.endpoint = lrs_properties.endpoint + self.version = lrs_properties.version + self.username = lrs_properties.username + self.password = lrs_properties.password + self.lrs = RemoteLRS( + version=self.version, + endpoint=self.endpoint, + username=self.username, + password=self.password, + ) + + self.agent = Agent(mbox="mailto:tincanpython@tincanapi.com") + self.agent2 = Agent(mbox="Agent2.mailto:tincanpython@tincanapi.com") + self.verb = Verb( + id="http://adlnet.gov/expapi/verbs/experienced", + display=LanguageMap({"en-US": "experienced"}) + ) + + self.group = Group(member=[self.agent, self.agent2]) + + self.activity = Activity( + id="http://tincanapi.com/TinCanPython/Test/Unit/0", + definition=ActivityDefinition() + ) + self.activity.definition.type = "http://id.tincanapi.com/activitytype/unit-test" + self.activity.definition.name = LanguageMap({"en-US": "Python Tests"}) + self.activity.definition.description = LanguageMap( + {"en-US": "Unit test in the test suite for the Python library"} + ) + + self.parent = Activity( + id="http://tincanapi.com/TinCanPython/Test", + definition=ActivityDefinition()) + self.activity.definition.type = "http://id.tincanapi.com/activitytype/unit-test-suite" + self.parent.definition.name = LanguageMap({"en-US": "Python Tests"}) + self.parent.definition.description = LanguageMap( + {"en-US": "Unit test in the test suite for the Python library"} + ) + + self.statement_ref = StatementRef(id=uuid.uuid4()) + + self.context = Context(registration=uuid.uuid4(), statement=self.statement_ref) + #self.context.context_activities = ContextActivities(parent=[self.parent]) + + self.score = Score( + raw=97, + scaled=0.97, + max=100, + min=0 + ) + + self.result = Result( + score=self.score, + success=True, + completion=True, + duration="PT120S" + ) + + self.substatement = Substatement( + actor=self.agent, + verb=self.verb, + object=self.activity, + ) def tearDown(self): pass @@ -227,7 +225,6 @@ def test_save_statements(self): object=self.activity, context=self.context ) - #TODO:statements objects that are passed have their id's updated? response = self.lrs.save_statements([statement1, statement2]) self.assertIsInstance(response, LRSResponse) @@ -248,13 +245,12 @@ def test_retrieve_statement(self): ) save_resp = self.lrs.save_statement(statement) - if(save_resp.success): - response = self.lrs.retrieve_statement(save_resp.content.id) - self.assertIsInstance(response, LRSResponse) - self.assertTrue(response.success) - self.shallow_compare(statement, response.content) - else: - print "test_retrieve_statement: save_statement failed" + self.assertTrue(save_resp.success) + + response = self.lrs.retrieve_statement(save_resp.content.id) + self.assertIsInstance(response, LRSResponse) + self.assertTrue(response.success) + self.shallow_compare(statement, response.content) def test_query_statements(self): s1 = Statement( @@ -295,13 +291,6 @@ def test_query_statements(self): } response = self.lrs.query_statements(query) - print "++++++++++++++++++++++++++++++++++" - if hasattr(response.request, 'content'): - print response.request.content - print "----------------------------------" - print response.data - print "++++++++++++++++++++++++++++++++++" - self.assertIsInstance(response, LRSResponse) self.assertTrue(response.success) self.assertIsInstance(response.content, StatementsResult) @@ -369,22 +358,13 @@ def test_more_statements(self): query_resp = self.lrs.query_statements(query) self.assertIsInstance(query_resp, LRSResponse) + self.assertTrue(query_resp.success) + self.assertIsNotNone(query_resp.content.more) - if query_resp.success and query_resp.content.more is not None: - response = self.lrs.more_statements(query_resp.content) - - print "++++++++++++++++++++++++++++++++++" - if hasattr(response.request, 'content'): - print response.request.content - print "----------------------------------" - print response.data - print "++++++++++++++++++++++++++++++++++" - - self.assertIsInstance(response, LRSResponse) - self.assertTrue(response.success) - self.assertIsInstance(response.content, StatementsResult) - else: - print "test_more_statements: query_statements failed or did not return a more url" + response = self.lrs.more_statements(query_resp.content) + self.assertIsInstance(response, LRSResponse) + self.assertTrue(response.success) + self.assertIsInstance(response.content, StatementsResult) def test_retrieve_state_ids(self): response = self.lrs.retrieve_state_ids(activity=self.activity, agent=self.agent) @@ -491,7 +471,7 @@ def shallow_compare(self, s1, s2, compare_ids=False): if not k == '_id' or compare_ids: self.assertTrue(hasattr(s2, k)) if isinstance(v, Base): - self.shallow_compare(v, getattr(s2, k)) + self.shallow_compare(v, getattr(s2, k), True) else: self.assertEqual(v, getattr(s2, k)) diff --git a/tincan/remote_lrs.py b/tincan/remote_lrs.py index 4c52c98..c26289b 100644 --- a/tincan/remote_lrs.py +++ b/tincan/remote_lrs.py @@ -115,8 +115,12 @@ def _send_request(self, request): web_req = httplib.HTTPConnection(parsed.hostname, parsed.port) path = parsed.path - if params: - path += "?" + params + if parsed.query or parsed.path: + path += "?" + if parsed.query: + path += parsed.query + if params: + path += params if hasattr(request, "content") and request.content is not None: web_req.request( From 950aaa4449da57721f21a30c6d1325f283631690 Mon Sep 17 00:00:00 2001 From: Jake Noble Date: Tue, 24 Jun 2014 14:05:12 -0500 Subject: [PATCH 3/3] literally one line to fix SubStatement vs. Substatement --- test/remote_lrs_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/remote_lrs_test.py b/test/remote_lrs_test.py index ec032d2..ea944b2 100644 --- a/test/remote_lrs_test.py +++ b/test/remote_lrs_test.py @@ -93,7 +93,7 @@ def setUp(self): duration="PT120S" ) - self.substatement = Substatement( + self.substatement = SubStatement( actor=self.agent, verb=self.verb, object=self.activity,