def setUp(self): super(TestBaseAnalyst, self).setUp() BaseAnalyst.__abstractmethods__ = frozenset() self.ctx = ContextFixtures() self.base_analyst = BaseAnalyst() self.project_repo = ProjectRepository(db) self.result_repo = ResultRepository(db) self.task_repo = TaskRepository(db) assert_dict_equal.__self__.maxDiff = None assert_equal.__self__.maxDiff = None
def setUp(self): super(TestZ3950Analyst, self).setUp() self.ctx = ContextFixtures() self.z3950_analyst = Z3950Analyst() self.result_repo = ResultRepository(db) self.task_repo = TaskRepository(db) self.data = { 'user_id': [1], 'control_number': ['123'], 'reference': ['abc'], 'foo': ['bar'], 'comments': ['Some comment'] }
def setup_repositories(app): """Setup repositories.""" from pybossa.repositories import UserRepository from pybossa.repositories import ProjectRepository from pybossa.repositories import ProjectStatsRepository from pybossa.repositories import AnnouncementRepository from pybossa.repositories import BlogRepository from pybossa.repositories import TaskRepository from pybossa.repositories import AuditlogRepository from pybossa.repositories import WebhookRepository from pybossa.repositories import ResultRepository from pybossa.repositories import HelpingMaterialRepository global user_repo global project_repo global project_stats_repo global announcement_repo global blog_repo global task_repo global auditlog_repo global webhook_repo global result_repo global helping_repo language = app.config.get('FULLTEXTSEARCH_LANGUAGE') user_repo = UserRepository(db) project_repo = ProjectRepository(db) project_stats_repo = ProjectStatsRepository(db) announcement_repo = AnnouncementRepository(db) blog_repo = BlogRepository(db) task_repo = TaskRepository(db, language) auditlog_repo = AuditlogRepository(db) webhook_repo = WebhookRepository(db) result_repo = ResultRepository(db) helping_repo = HelpingMaterialRepository(db)
def setup_repositories(): """Setup repositories.""" from pybossa.repositories import UserRepository from pybossa.repositories import ProjectRepository from pybossa.repositories import BlogRepository from pybossa.repositories import TaskRepository from pybossa.repositories import AuditlogRepository from pybossa.repositories import WebhookRepository from pybossa.repositories import ResultRepository from pybossa.repositories import UserScoreRepository global user_repo global project_repo global blog_repo global task_repo global auditlog_repo global webhook_repo global result_repo global user_score_repo user_repo = UserRepository(db) project_repo = ProjectRepository(db) blog_repo = BlogRepository(db) task_repo = TaskRepository(db) auditlog_repo = AuditlogRepository(db) webhook_repo = WebhookRepository(db) result_repo = ResultRepository(db) user_score_repo = UserScoreRepository(db)
def setup_repositories(app): """Setup repositories.""" from pybossa.repositories import UserRepository from pybossa.repositories import ProjectRepository from pybossa.repositories import ProjectStatsRepository from pybossa.repositories import AnnouncementRepository from pybossa.repositories import BlogRepository from pybossa.repositories import TaskRepository from pybossa.repositories import AuditlogRepository from pybossa.repositories import WebhookRepository from pybossa.repositories import ResultRepository from pybossa.repositories import HelpingMaterialRepository from pybossa.repositories import PerformanceStatsRepository global user_repo global project_repo global project_stats_repo global announcement_repo global blog_repo global task_repo global auditlog_repo global webhook_repo global result_repo global helping_repo global performance_stats_repo language = app.config.get('FULLTEXTSEARCH_LANGUAGE') rdancy_upd_exp = app.config.get('REDUNDANCY_UPDATE_EXPIRATION', 30) user_repo = UserRepository(db) project_repo = ProjectRepository(db) project_stats_repo = ProjectStatsRepository(db) announcement_repo = AnnouncementRepository(db) blog_repo = BlogRepository(db) task_repo = TaskRepository(db, language, rdancy_upd_exp) auditlog_repo = AuditlogRepository(db) webhook_repo = WebhookRepository(db) result_repo = ResultRepository(db) helping_repo = HelpingMaterialRepository(db) performance_stats_repo = PerformanceStatsRepository(db)
def setUp(self): super(TestResultAuthorization, self).setUp() self.result_repo = ResultRepository(db)
class TestResultAuthorization(Test): mock_anonymous = mock_current_user() mock_authenticated = mock_current_user(anonymous=False, admin=False, id=2) mock_pro = mock_current_user(anonymous=False, admin=False, id=2, pro=True) mock_admin = mock_current_user(anonymous=False, admin=True, id=1) mock_owner = mock_current_user(anonymous=False, admin=False, id=1) def setUp(self): super(TestResultAuthorization, self).setUp() self.result_repo = ResultRepository(db) def create_result(self, n_answers=1, filter_by=False): task = TaskFactory.create(n_answers=n_answers) TaskRunFactory.create(task=task) if filter_by: return self.result_repo.filter_by(project_id=1) else: return self.result_repo.get_by(project_id=1) @with_context @patch('pybossa.auth.current_user', new=mock_anonymous) def test_anonymous_user_can_read_result(self): """Test anonymous users can read results""" result = self.create_result() assert ensure_authorized_to('read', result) @with_context @patch('pybossa.auth.current_user', new=mock_authenticated) def test_auth_user_can_read_result(self): """Test auth users can read results""" result = self.create_result() assert ensure_authorized_to('read', result) @with_context @patch('pybossa.auth.current_user', new=mock_admin) def test_admin_user_can_read_result(self): """Test admin users can read results""" result = self.create_result() assert ensure_authorized_to('read', result) @with_context @patch('pybossa.auth.current_user', new=mock_anonymous) def test_anonymous_user_cannot_save_results(self): """Test anonymous users cannot save results of a specific project""" result = Result() assert_raises(Unauthorized, ensure_authorized_to, 'create', result) @with_context @patch('pybossa.auth.current_user', new=mock_authenticated) def test_authenticated_user_cannot_save_results(self): """Test authenticated users cannot save results of a specific project""" result = Result() assert_raises(Forbidden, ensure_authorized_to, 'create', result) @with_context @patch('pybossa.auth.current_user', new=mock_admin) def test_admin_user_cannot_save_results(self): """Test admin users cannot save results of a specific project""" result = Result() assert_raises(Forbidden, ensure_authorized_to, 'create', result) @with_context @patch('pybossa.auth.current_user', new=mock_anonymous) def test_anonymous_user_cannot_delete_results(self): """Test anonymous users cannot delete results of a specific project""" result = Result() assert_raises(Unauthorized, ensure_authorized_to, 'delete', result) @with_context @patch('pybossa.auth.current_user', new=mock_authenticated) def test_authenticated_user_cannot_delete_results(self): """Test authenticated users cannot delete results of a specific project""" result = Result() assert_raises(Forbidden, ensure_authorized_to, 'delete', result) @with_context @patch('pybossa.auth.current_user', new=mock_admin) def test_admin_user_cannot_delete_results(self): """Test admin users cannot delete results of a specific project""" result = Result() assert_raises(Forbidden, ensure_authorized_to, 'delete', result) @with_context @patch('pybossa.auth.current_user', new=mock_anonymous) def test_anonymous_user_cannot_update_results(self): """Test anonymous users cannot update results of a specific project""" result = self.create_result() assert_raises(Unauthorized, ensure_authorized_to, 'update', result) @with_context @patch('pybossa.auth.current_user', new=mock_authenticated) def test_auth_user_cannot_update_results(self): """Test auth users but not owner cannot update results of a specific project""" result = self.create_result() assert_raises(Forbidden, ensure_authorized_to, 'update', result) @with_context @patch('pybossa.auth.current_user', new=mock_owner) def test_auth_owner_can_update_results(self): """Test auth owner can update results of a specific project""" result = self.create_result() result.info = dict(new='value') assert ensure_authorized_to('update', result) updated_result = self.result_repo.get_by(id=result.id) err_msg = "The result has not been updated" assert updated_result.info['new'] == 'value', err_msg
class TestResultAPI(TestAPI): def setUp(self): super(TestResultAPI, self).setUp() self.result_repo = ResultRepository(db) def create_result(self, n_results=1, n_answers=1, owner=None, filter_by=False): if owner: owner = owner else: owner = UserFactory.create() project = ProjectFactory.create(owner=owner) tasks = [] for i in range(n_results): tasks.append(TaskFactory.create(n_answers=n_answers, project=project)) for i in range(n_answers): for task in tasks: TaskRunFactory.create(task=task, project=project) if filter_by: return self.result_repo.filter_by(project_id=1) else: return self.result_repo.get_by(project_id=1) @with_context def test_result_query_without_params(self): """ Test API Result query""" result = self.create_result(n_answers=10) res = self.app.get("/api/result") results = json.loads(res.data) assert len(results) == 1, results result = results[0] assert result["info"] is None, result assert len(result["task_run_ids"]) == 10, result assert result["task_run_ids"] == [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], result assert result["project_id"] == 1, result assert result["task_id"] == 1, result assert result["created"] is not None, result result = self.create_result(n_answers=10) result = result_repo.get(2) result.created = "2119-01-01T14:37:30.642119" result_repo.update(result) url = "/api/result?desc=true" res = self.app.get(url) data = json.loads(res.data) err_msg = "It should get the last item first." assert data[0]["created"] == "2119-01-01T14:37:30.642119", err_msg url = "/api/result" res = self.app.get(url) data = json.loads(res.data) err_msg = "It should get not the last item first." assert data[0]["created"] != "2119-01-01T14:37:30.642119", err_msg # The output should have a mime-type: application/json assert res.mimetype == "application/json", res @with_context def test_result_query_without_params_with_context(self): """ Test API Result query with context.""" result = self.create_result(n_answers=10) res = self.app.get("/api/result") results = json.loads(res.data) assert len(results) == 1, results result = results[0] assert result["info"] is None, result assert len(result["task_run_ids"]) == 10, result assert result["task_run_ids"] == [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], result assert result["project_id"] == 1, result assert result["task_id"] == 1, result assert result["created"] is not None, result # The output should have a mime-type: application/json assert res.mimetype == "application/json", res @with_context def test_result_query_with_params(self): """Test API query for result with params works""" owner = UserFactory.create() results = self.create_result(n_results=10, filter_by=True, owner=owner) # Test for real field res = self.app.get("/api/result?api_key=" + owner.api_key) data = json.loads(res.data) # Should return one result assert len(data) == 10, data # Correct result assert data[0]["project_id"] == 1, data res = self.app.get("/api/project?api_key=" + owner.api_key) project = json.loads(res.data) assert len(project) == 1, project assert project[0]["owner_id"] == owner.id, project owner_two = UserFactory.create() res = self.app.get("/api/result?api_key=" + owner_two.api_key) data = json.loads(res.data) # Should return zero results assert len(data) == 0, data owner_two = UserFactory.create() res = self.app.get("/api/result?all=1&api_key=" + owner_two.api_key) data = json.loads(res.data) # Should return ten results assert len(data) == 10, data assert data[0]["project_id"] == 1, data # Valid field but wrong value url = "/api/result?project_id=99999999&api_key=" + owner.api_key res = self.app.get(url) data = json.loads(res.data) assert len(data) == 0, data # Multiple fields url = "/api/result?project_id=1&task_id=1&api_key=" + owner.api_key res = self.app.get(url) data = json.loads(res.data) # One result assert len(data) == 1, data # Correct result assert data[0]["project_id"] == 1, data assert data[0]["task_id"] == 1, data # Multiple fields url = "/api/result?project_id=1&task_id=1&api_key=" + owner_two.api_key res = self.app.get(url) data = json.loads(res.data) # Zero result assert len(data) == 0, data url = "/api/result?all=1&project_id=1&task_id=1&api_key=" + owner_two.api_key res = self.app.get(url) data = json.loads(res.data) # One result assert len(data) == 1, data # Correct result assert data[0]["project_id"] == 1, data assert data[0]["task_id"] == 1, data # Limits url = "/api/result?project_id=1&limit=5&api_key=" + owner.api_key res = self.app.get(url) data = json.loads(res.data) for item in data: assert item["project_id"] == 1, item assert len(data) == 5, len(data) # Limits url = "/api/result?project_id=1&limit=5&api_key=" + owner_two.api_key res = self.app.get(url) data = json.loads(res.data) assert len(data) == 0, data # Limits url = "/api/result?all=1&project_id=1&limit=5&api_key=" + owner_two.api_key res = self.app.get(url) data = json.loads(res.data) for item in data: assert item["project_id"] == 1, item assert len(data) == 5, len(data) # Keyset pagination url = "/api/result?project_id=1&limit=5&last_id=1&api_key=" + owner.api_key res = self.app.get(url) data = json.loads(res.data) for item in data: assert item["project_id"] == 1, item assert len(data) == 5, data assert data[0]["id"] == 2, data[0] # Keyset pagination url = "/api/result?project_id=1&limit=5&last_id=1&api_key=" + owner_two.api_key res = self.app.get(url) data = json.loads(res.data) assert len(data) == 0, data # Keyset pagination url = "/api/result?all=1&project_id=1&limit=5&last_id=1&api_key=" + owner.api_key res = self.app.get(url) data = json.loads(res.data) for item in data: assert item["project_id"] == 1, item assert len(data) == 5, data assert data[0]["id"] == 2, data[0] @with_context def test_result_post(self): """Test API Result creation""" admin = UserFactory.create() user = UserFactory.create() non_owner = UserFactory.create() project = ProjectFactory.create(owner=user) data = dict(info="final result") # anonymous user # no api-key res = self.app.post("/api/result", data=json.dumps(data)) error_msg = "Should not be allowed to create" assert_equal(res.status, "401 UNAUTHORIZED", error_msg) ### real user but not allowed as not owner! res = self.app.post("/api/result?api_key=" + non_owner.api_key, data=json.dumps(data)) error_msg = "Should not be able to post tasks for projects of others" assert_equal(res.status, "403 FORBIDDEN", error_msg) # now a real user res = self.app.post("/api/result?api_key=" + user.api_key, data=json.dumps(data)) assert_equal(res.status, "403 FORBIDDEN", error_msg) # now the root user res = self.app.post("/api/result?api_key=" + admin.api_key, data=json.dumps(data)) assert_equal(res.status, "403 FORBIDDEN", error_msg) # POST with not JSON data url = "/api/result?api_key=%s" % user.api_key res = self.app.post(url, data=data) err = json.loads(res.data) assert res.status_code == 415, err assert err["status"] == "failed", err assert err["target"] == "result", err assert err["action"] == "POST", err assert err["exception_cls"] == "ValueError", err # POST with not allowed args res = self.app.post(url + "&foo=bar", data=json.dumps(data)) err = json.loads(res.data) assert res.status_code == 415, err assert err["status"] == "failed", err assert err["target"] == "result", err assert err["action"] == "POST", err assert err["exception_cls"] == "AttributeError", err # POST with fake data data["wrongfield"] = 13 res = self.app.post(url, data=json.dumps(data)) err = json.loads(res.data) assert res.status_code == 415, err assert err["status"] == "failed", err assert err["target"] == "result", err assert err["action"] == "POST", err assert err["exception_cls"] == "TypeError", err def test_result_post_with_reserved_fields_returns_error(self): user = UserFactory.create() project = ProjectFactory.create(owner=user) data = {"created": "today", "id": 222, "project_id": project.id} res = self.app.post("/api/result?api_key=" + user.api_key, data=json.dumps(data)) assert res.status_code == 400, res.status_code error = json.loads(res.data) assert error["exception_msg"] == "Reserved keys in payload", error def test_result_put_with_reserved_fields_returns_error(self): user = UserFactory.create() result = self.create_result(owner=user) print result url = "/api/result/%s?api_key=%s" % (result.id, user.api_key) data = {"created": "today", "project_id": 1, "id": 222} res = self.app.put(url, data=json.dumps(data)) assert res.status_code == 400, res.status_code error = json.loads(res.data) assert error["exception_msg"] == "Reserved keys in payload", error @with_context def test_result_update(self): """Test API result update""" admin = UserFactory.create() user = UserFactory.create() non_owner = UserFactory.create() data = dict(info=dict(foo="bar")) datajson = json.dumps(data) result = self.create_result(owner=user) ## anonymous res = self.app.put("/api/result/%s" % result.id, data=datajson) assert_equal(res.status, "401 UNAUTHORIZED", res.status) ### real user but not allowed as not owner! url = "/api/result/%s?api_key=%s" % (result.id, non_owner.api_key) res = self.app.put(url, data=datajson) assert_equal(res.status, "403 FORBIDDEN", res.status) ### real user url = "/api/result/%s?api_key=%s" % (result.id, user.api_key) res = self.app.put(url, data=datajson) out = json.loads(res.data) assert_equal(res.status, "200 OK", res.data) assert_equal(result.info["foo"], data["info"]["foo"]) assert result.id == out["id"], out ### root res = self.app.put("/api/result/%s?api_key=%s" % (result.id, admin.api_key), data=datajson) assert_equal(res.status, "403 FORBIDDEN", res.status) # PUT with not JSON data res = self.app.put(url, data=None) err = json.loads(res.data) assert res.status_code == 415, err assert err["status"] == "failed", err assert err["target"] == "result", err assert err["action"] == "PUT", err assert err["exception_cls"] == "ValueError", err # PUT with not allowed args res = self.app.put(url + "&foo=bar", data=json.dumps(data)) err = json.loads(res.data) assert res.status_code == 415, err assert err["status"] == "failed", err assert err["target"] == "result", err assert err["action"] == "PUT", err assert err["exception_cls"] == "AttributeError", err # PUT with fake data data["wrongfield"] = 13 res = self.app.put(url, data=json.dumps(data)) err = json.loads(res.data) assert res.status_code == 415, err assert err["status"] == "failed", err assert err["target"] == "result", err assert err["action"] == "PUT", err assert err["exception_cls"] == "TypeError", err @with_context def test_result_delete(self): """Test API result delete""" admin = UserFactory.create() user = UserFactory.create() non_owner = UserFactory.create() result = self.create_result(owner=user) ## anonymous res = self.app.delete("/api/result/%s" % result.id) error_msg = "Anonymous should not be allowed to update" assert_equal(res.status, "401 UNAUTHORIZED", error_msg) ### real user but not allowed as not owner! url = "/api/result/%s?api_key=%s" % (result.id, non_owner.api_key) res = self.app.delete(url) error_msg = "Should not be able to update tasks of others" assert_equal(res.status, "403 FORBIDDEN", error_msg) #### real user # DELETE with not allowed args res = self.app.delete(url + "&foo=bar") err = json.loads(res.data) assert res.status_code == 415, err assert err["status"] == "failed", err assert err["target"] == "result", err assert err["action"] == "DELETE", err assert err["exception_cls"] == "AttributeError", err # DELETE returns 403 url = "/api/result/%s?api_key=%s" % (result.id, user.api_key) res = self.app.delete(url) assert_equal(res.status, "403 FORBIDDEN", res.data) #### root user url = "/api/result/%s?api_key=%s" % (result.id, admin.api_key) res = self.app.delete(url) assert_equal(res.status, "403 FORBIDDEN", res.data) @with_context def test_get_last_version(self): """Test API result returns always latest version.""" result = self.create_result() project = project_repo.get(result.project_id) task = task_repo.get_task(result.task_id) task.n_answers = 2 TaskRunFactory.create(task=task, project=project) result = result_repo.get_by(project_id=project.id) assert result.last_version is True, result.last_version result_id = result.id results = result_repo.filter_by(project_id=project.id, last_version=False) assert len(results) == 2, len(results) for r in results: if r.id == result_id: assert r.last_version is True, r.last_version else: assert r.last_version is False, r.last_version
class TestIIIFAnnotationAnalyst(Test): def setUp(self): super(TestIIIFAnnotationAnalyst, self).setUp() self.ctx = ContextFixtures() self.result_repo = ResultRepository(db) self.task_repo = TaskRepository(db) self.iiif_analyst = IIIFAnnotationAnalyst() self.comments = ['Some comment'] self.tags = { 'foo': [ dict(x=100, y=100, w=100, h=100), dict(x=200, y=200, w=200, h=200) ], 'bar': [dict(x=300, y=300, w=300, h=300)] } transcription_data = {'foo': ['bar', 'baz'], 'qux': ['quux', 'quuz']} self.transcriptions_df = pandas.DataFrame(transcription_data) self.comment_annos = [] for comment in self.comments: self.comment_annos.append({ 'motivation': 'commenting', 'body': { 'type': 'TextualBody', 'value': comment, 'purpose': 'commenting', 'format': 'text/plain' }, 'target': 'example.com' }) self.tagging_annos = [] for tag, rect_list in self.tags.items(): for rect in rect_list: self.tagging_annos.append({ 'motivation': 'tagging', 'body': { 'type': 'TextualBody', 'purpose': 'tagging', 'value': tag }, 'target': { 'source': 'example.com', 'selector': { 'conformsTo': 'http://www.w3.org/TR/media-frags/', 'type': 'FragmentSelector', 'value': '?xywh={0},{1},{2},{3}'.format( rect['x'], rect['y'], rect['w'], rect['h']) } } }) self.transcription_annos = [] for tag, value_list in transcription_data.items(): for value in value_list: self.transcription_annos.append({ 'motivation': 'describing', 'body': [{ 'type': 'TextualBody', 'purpose': 'tagging', 'value': tag }, { 'type': 'TextualBody', 'purpose': 'describing', 'value': value, 'format': 'text/plain' }], 'target': 'example.com' }) self.data = { 'user_id': [1, 2, 3], 'info': [self.comment_annos, self.tagging_annos, self.transcription_annos] } def test_get_comments(self): """Test IIIF Annotation comments are returned.""" task_run_df = pandas.DataFrame(self.data) comments = self.iiif_analyst.get_comments(task_run_df) expected = [(1, comment) for comment in self.comments] assert_equal(comments, expected) def test_get_tags(self): """Test IIIF Annotation tags are returned.""" task_run_df = pandas.DataFrame(self.data) tags = self.iiif_analyst.get_tags(task_run_df) assert_dict_equal(tags, self.tags) def test_get_tags_with_body_list(self): """Test IIIF Annotation tags are returned when body is a list.""" self.tagging_annos[0]['body'] = [ self.tagging_annos[0]['body'], { 'type': 'TextualBody', 'purpose': 'classifying', 'value': 'foo' } ] task_run_df = pandas.DataFrame(self.data) tags = self.iiif_analyst.get_tags(task_run_df) assert_dict_equal(tags, self.tags) def test_get_transcriptions_df(self): """Test IIIF Annotation transcriptions are returned.""" task_run_df = pandas.DataFrame(self.data) df = self.iiif_analyst.get_transcriptions_df(task_run_df) assert_dict_equal(df.to_dict(), self.transcriptions_df.to_dict()) @with_context @patch('pybossa_lc.model.base.wa_client') def test_analysis_with_no_transcriptions(self, mock_client): """Test IIIF analysis with no transcriptions.""" n_answers = 3 target = 'example.com' task = self.ctx.create_task(n_answers, target) TaskRunFactory.create_batch(n_answers, task=task, info=[{ 'motivation': 'describing', 'body': [{ 'purpose': 'describing', 'value': '' }, { 'purpose': 'tagging', 'value': 'foo' }] }]) result = self.result_repo.filter_by(project_id=task.project_id)[0] fake_search = MagicMock() fake_search.return_value = [] mock_client.search_annotations = fake_search self.iiif_analyst.analyse(result.id) assert_equal(mock_client.create_annotation.called, False) @with_context @patch('pybossa_lc.model.base.wa_client') def test_fragment_selector_stripped(self, mock_client): """Test IIIF fragment selector is stripped if rule applied.""" n_answers = 3 source = 'example.com' target = { 'source': source, 'selector': { 'conformsTo': 'http://www.w3.org/TR/media-frags/', 'type': 'FragmentSelector', 'value': '?xywh=100,100,100,100' } } rules = dict(remove_fragment_selector=True) anno_collection = 'http://eg.com/collection' task = self.ctx.create_task(n_answers, target, rules=rules, anno_collection=anno_collection) tag = 'foo' value = 'bar' TaskRunFactory.create_batch(n_answers, task=task, info=[{ 'motivation': 'describing', 'body': [{ 'purpose': 'describing', 'value': value }, { 'purpose': 'tagging', 'value': 'foo' }] }]) result = self.result_repo.filter_by(project_id=task.project_id)[0] fake_search = MagicMock() fake_search.return_value = [] mock_client.search_annotations = fake_search self.iiif_analyst.analyse(result.id) func = mock_client.create_annotation func.assert_called_once_with( anno_collection, { 'motivation': 'describing', 'type': 'Annotation', 'generator': [{ "id": flask_app.config.get('GITHUB_REPO'), "type": "Software", "name": "LibCrowds", "homepage": flask_app.config.get('SPA_SERVER_NAME') }, { "id": url_for('api.api_task', oid=task.id), "type": "Software" }], 'body': [{ 'type': 'TextualBody', 'purpose': 'describing', 'value': value, 'format': 'text/plain' }, { 'type': 'TextualBody', 'purpose': 'tagging', 'value': tag }], 'target': source }) @with_context @patch('pybossa_lc.model.base.wa_client') def test_comment_annotation_created(self, mock_client): """Test IIIF comment annotations are created.""" n_answers = 1 target = 'example.com' anno_collection = 'http://eg.com/collection' task = self.ctx.create_task(n_answers, target, anno_collection=anno_collection) user = UserFactory() value = 'foo' TaskRunFactory.create_batch(n_answers, user=user, task=task, info=[{ 'motivation': 'commenting', 'body': { 'value': value } }]) result = self.result_repo.filter_by(project_id=task.project_id)[0] fake_search = MagicMock() fake_search.return_value = [] mock_client.search_annotations = fake_search self.iiif_analyst.analyse(result.id) func = mock_client.create_annotation func.assert_called_once_with( anno_collection, { 'motivation': 'commenting', 'type': 'Annotation', 'creator': { 'id': url_for('api.api_user', oid=user.id), 'type': 'Person', 'name': user.fullname, 'nickname': user.name }, 'generator': [{ "id": flask_app.config.get('GITHUB_REPO'), "type": "Software", "name": "LibCrowds", "homepage": flask_app.config.get('SPA_SERVER_NAME') }, { "id": url_for('api.api_task', oid=task.id), "type": "Software" }], 'body': { 'type': 'TextualBody', 'purpose': 'commenting', 'value': value, 'format': 'text/plain' }, 'target': target }) @with_context @patch('pybossa_lc.model.base.wa_client') def test_transcriptions_are_normalised(self, mock_client): """Test IIIF transcriptions are normalised according to set rules.""" n_answers = 1 target = 'example.com' anno_collection = 'http://eg.com/collection' rules = dict(case='title', whitespace='full_stop', trim_punctuation=True) task = self.ctx.create_task(n_answers, target, rules=rules, anno_collection=anno_collection) tag = 'foo' values = ['HeLLo!', ' hello ', ' hELLO.'] for value in values: TaskRunFactory.create(task=task, info=[{ 'motivation': 'describing', 'body': [{ 'purpose': 'describing', 'value': value }, { 'purpose': 'tagging', 'value': tag }] }]) result = self.result_repo.filter_by(project_id=task.project_id)[0] fake_search = MagicMock() fake_search.return_value = [] mock_client.search_annotations = fake_search self.iiif_analyst.analyse(result.id) func = mock_client.create_annotation func.assert_called_once_with( anno_collection, { 'motivation': 'describing', 'type': 'Annotation', 'generator': [{ "id": flask_app.config.get('GITHUB_REPO'), "type": "Software", "name": "LibCrowds", "homepage": flask_app.config.get('SPA_SERVER_NAME') }, { "id": url_for('api.api_task', oid=task.id), "type": "Software" }], 'body': [{ 'type': 'TextualBody', 'purpose': 'describing', 'value': 'Hello', 'format': 'text/plain' }, { 'type': 'TextualBody', 'purpose': 'tagging', 'value': tag }], 'target': target }) @with_context @patch('pybossa_lc.model.base.wa_client') def test_with_matching_transcriptions(self, mock_client): """Test IIIF results with matching transcriptions.""" n_answers = 3 target = 'example.com' anno_collection = 'http://eg.com/collection' task = self.ctx.create_task(n_answers, target, rules={}, anno_collection=anno_collection) value = 'foo' tag = 'bar' TaskRunFactory.create_batch(n_answers, task=task, info=[{ 'motivation': 'describing', 'body': [{ 'purpose': 'describing', 'value': value }, { 'purpose': 'tagging', 'value': tag }] }]) result = self.result_repo.filter_by(project_id=task.project_id)[0] fake_search = MagicMock() fake_search.return_value = [] mock_client.search_annotations = fake_search self.iiif_analyst.analyse(result.id) func = mock_client.create_annotation func.assert_called_once_with( anno_collection, { 'motivation': 'describing', 'type': 'Annotation', 'generator': [{ "id": flask_app.config.get('GITHUB_REPO'), "type": "Software", "name": "LibCrowds", "homepage": flask_app.config.get('SPA_SERVER_NAME') }, { "id": url_for('api.api_task', oid=task.id), "type": "Software" }], 'body': [{ 'type': 'TextualBody', 'purpose': 'describing', 'value': value, 'format': 'text/plain' }, { 'type': 'TextualBody', 'purpose': 'tagging', 'value': tag }], 'target': target }) @with_context @patch('pybossa_lc.model.base.wa_client') def test_redundancy_increased_when_not_max(self, mock_client): """Test IIIF task redundancy is updated when max not reached.""" n_answers = 3 target = 'example.com' task = self.ctx.create_task(n_answers, target, max_answers=n_answers + 1) for i in range(n_answers): TaskRunFactory.create(task=task, info=[{ 'motivation': 'describing', 'body': [{ 'purpose': 'describing', 'value': i }, { 'purpose': 'tagging', 'value': i }] }]) result = self.result_repo.filter_by(project_id=task.project_id)[0] fake_search = MagicMock() fake_search.return_value = [] mock_client.search_annotations = fake_search self.iiif_analyst.analyse(result.id) assert_equal(mock_client.create_annotation.called, False) updated_task = self.task_repo.get_task(task.id) assert_equal(updated_task.n_answers, n_answers + 1) @with_context @patch('pybossa_lc.model.base.wa_client') def test_redundancy_not_increased_when_max(self, mock_client): """Test IIIF task redundancy is not updated when max is reached.""" n_answers = 3 target = 'example.com' task = self.ctx.create_task(n_answers, target, max_answers=n_answers) for i in range(n_answers): TaskRunFactory.create(task=task, info=[{ 'motivation': 'describing', 'body': [{ 'purpose': 'describing', 'value': i }, { 'purpose': 'tagging', 'value': i }] }]) result = self.result_repo.filter_by(project_id=task.project_id)[0] fake_search = MagicMock() fake_search.return_value = [] mock_client.search_annotations = fake_search self.iiif_analyst.analyse(result.id) assert_equal(mock_client.create_annotation.called, False) updated_task = self.task_repo.get_task(task.id) assert_equal(updated_task.n_answers, n_answers) @with_context @patch('pybossa_lc.model.base.wa_client') def test_redundancy_not_increased_for_tags(self, mock_client): """Test IIIF task redundancy is not updated for tags.""" n_answers = 3 target = 'example.com' task = self.ctx.create_task(n_answers, target, max_answers=n_answers + 1) for i in range(n_answers): TaskRunFactory.create( task=task, info=[{ 'motivation': 'tagging', 'body': { 'type': 'TextualBody', 'purpose': 'tagging', 'value': 'foo' }, 'target': { 'source': 'example.com', 'selector': { 'conformsTo': 'http://www.w3.org/TR/media-frags/', 'type': 'FragmentSelector', 'value': '?xywh={0},{0},{0},{0}'.format(i) } } }]) result = self.result_repo.filter_by(project_id=task.project_id)[0] fake_search = MagicMock() fake_search.return_value = [] mock_client.search_annotations = fake_search self.iiif_analyst.analyse(result.id) updated_task = self.task_repo.get_task(task.id) assert_equal(updated_task.n_answers, n_answers) @with_context @patch('pybossa_lc.model.base.wa_client') def test_redundancy_not_increased_for_comments(self, mock_client): """Test IIIF task redundancy is not updated for comments.""" n_answers = 3 target = 'example.com' task = self.ctx.create_task(n_answers, target, max_answers=n_answers + 1) for i in range(n_answers): TaskRunFactory.create(task=task, info=[{ 'motivation': 'commenting', 'body': { 'type': 'TextualBody', 'value': i, 'purpose': 'commenting', 'format': 'text/plain' } }]) result = self.result_repo.filter_by(project_id=task.project_id)[0] fake_search = MagicMock() fake_search.return_value = [] mock_client.search_annotations = fake_search self.iiif_analyst.analyse(result.id) updated_task = self.task_repo.get_task(task.id) assert_equal(updated_task.n_answers, n_answers) @with_context @patch('pybossa_lc.model.base.wa_client') def test_redundancy_not_increased_when_no_values(self, mock_client): """Test IIIF task redundancy is not updated when no values.""" n_answers = 3 target = 'example.com' task = self.ctx.create_task(n_answers, target, max_answers=n_answers + 1) for i in range(n_answers): TaskRunFactory.create(task=task, info=[{ 'motivation': 'describing', 'body': [{ 'purpose': 'describing', 'value': '' }, { 'purpose': 'tagging', 'value': 'foo' }] }]) result = self.result_repo.filter_by(project_id=task.project_id)[0] fake_search = MagicMock() fake_search.return_value = [] mock_client.search_annotations = fake_search self.iiif_analyst.analyse(result.id) updated_task = self.task_repo.get_task(task.id) assert_equal(updated_task.n_answers, n_answers) assert_equal(mock_client.create_annotation.called, False) @with_context @patch('pybossa_lc.model.base.wa_client') def test_equal_regions_combined(self, mock_client): """Test IIIF equal tag regions are combined.""" n_answers = 3 target = 'example.com' anno_collection = 'http://eg.com/collection' task = self.ctx.create_task(n_answers, target, rules={}, anno_collection=anno_collection) rect = dict(x=400, y=200, w=100, h=150) tag = 'foo' TaskRunFactory.create_batch( n_answers, task=task, info=[{ 'motivation': 'tagging', 'body': { 'type': 'TextualBody', 'purpose': 'tagging', 'value': tag }, 'target': { 'source': 'example.com', 'selector': { 'conformsTo': 'http://www.w3.org/TR/media-frags/', 'type': 'FragmentSelector', 'value': '?xywh={0},{1},{2},{3}'.format(rect['x'], rect['y'], rect['w'], rect['h']) } } }]) result = self.result_repo.filter_by(project_id=task.project_id)[0] fake_search = MagicMock() fake_search.return_value = [] mock_client.search_annotations = fake_search self.iiif_analyst.analyse(result.id) func = mock_client.create_annotation func.assert_called_once_with( anno_collection, { 'motivation': 'tagging', 'type': 'Annotation', 'generator': [{ "id": flask_app.config.get('GITHUB_REPO'), "type": "Software", "name": "LibCrowds", "homepage": flask_app.config.get('SPA_SERVER_NAME') }, { "id": url_for('api.api_task', oid=task.id), "type": "Software" }], 'body': { 'type': 'TextualBody', 'purpose': 'tagging', 'value': tag }, 'target': { 'source': 'example.com', 'selector': { 'conformsTo': 'http://www.w3.org/TR/media-frags/', 'type': 'FragmentSelector', 'value': '?xywh={0},{1},{2},{3}'.format(rect['x'], rect['y'], rect['w'], rect['h']) } } }) @with_context @patch('pybossa_lc.model.base.wa_client') def test_equal_regions_combined(self, mock_client): """Test IIIF equal tag regions are combined.""" n_answers = 3 target = 'example.com' anno_collection = 'http://eg.com/collection' task = self.ctx.create_task(n_answers, target, rules={}, anno_collection=anno_collection) rect = dict(x=400, y=200, w=100, h=150) tag = 'foo' TaskRunFactory.create_batch( n_answers, task=task, info=[{ 'motivation': 'tagging', 'body': { 'type': 'TextualBody', 'purpose': 'tagging', 'value': tag }, 'target': { 'source': 'example.com', 'selector': { 'conformsTo': 'http://www.w3.org/TR/media-frags/', 'type': 'FragmentSelector', 'value': '?xywh={0},{1},{2},{3}'.format(rect['x'], rect['y'], rect['w'], rect['h']) } } }]) result = self.result_repo.filter_by(project_id=task.project_id)[0] fake_search = MagicMock() fake_search.return_value = [] mock_client.search_annotations = fake_search self.iiif_analyst.analyse(result.id) func = mock_client.create_annotation func.assert_called_once_with( anno_collection, { 'motivation': 'tagging', 'type': 'Annotation', 'generator': [{ "id": flask_app.config.get('GITHUB_REPO'), "type": "Software", "name": "LibCrowds", "homepage": flask_app.config.get('SPA_SERVER_NAME') }, { "id": url_for('api.api_task', oid=task.id), "type": "Software" }], 'body': { 'type': 'TextualBody', 'purpose': 'tagging', 'value': tag }, 'target': { 'source': 'example.com', 'selector': { 'conformsTo': 'http://www.w3.org/TR/media-frags/', 'type': 'FragmentSelector', 'value': '?xywh={0},{1},{2},{3}'.format(rect['x'], rect['y'], rect['w'], rect['h']) } } }) @with_context @patch('pybossa_lc.model.base.wa_client') def test_similar_regions_combined(self, mock_client): """Test IIIF similar tag regions are combined.""" n_answers = 3 target = 'example.com' anno_collection = 'http://eg.com/collection' task = self.ctx.create_task(n_answers, target, rules={}, anno_collection=anno_collection) rect1 = dict(x=90, y=100, w=110, h=90) rect2 = dict(x=100, y=110, w=90, h=100) rect3 = dict(x=110, y=90, w=100, h=110) rects = [rect1, rect2, rect3] tag = 'foo' for rect in rects: TaskRunFactory.create( task=task, info=[{ 'motivation': 'tagging', 'body': { 'type': 'TextualBody', 'purpose': 'tagging', 'value': tag }, 'target': { 'source': 'example.com', 'selector': { 'conformsTo': 'http://www.w3.org/TR/media-frags/', 'type': 'FragmentSelector', 'value': '?xywh={0},{1},{2},{3}'.format( rect['x'], rect['y'], rect['w'], rect['h']) } } }]) result = self.result_repo.filter_by(project_id=task.project_id)[0] fake_search = MagicMock() fake_search.return_value = [] mock_client.search_annotations = fake_search self.iiif_analyst.analyse(result.id) func = mock_client.create_annotation func.assert_called_once_with( anno_collection, { 'motivation': 'tagging', 'type': 'Annotation', 'generator': [{ "id": flask_app.config.get('GITHUB_REPO'), "type": "Software", "name": "LibCrowds", "homepage": flask_app.config.get('SPA_SERVER_NAME') }, { "id": url_for('api.api_task', oid=task.id), "type": "Software" }], 'body': { 'type': 'TextualBody', 'purpose': 'tagging', 'value': tag }, 'target': { 'source': 'example.com', 'selector': { 'conformsTo': 'http://www.w3.org/TR/media-frags/', 'type': 'FragmentSelector', 'value': '?xywh=90,90,120,110' } } }) @with_context @patch('pybossa_lc.model.base.wa_client') def test_different_regions_combined(self, mock_client): """Test IIIF different tag regions are not combined.""" n_answers = 3 target = 'example.com' anno_collection = 'http://eg.com/collection' task = self.ctx.create_task(n_answers, target, rules={}, anno_collection=anno_collection) rect1 = dict(x=10, y=10, w=10, h=10) rect2 = dict(x=100, y=100, w=100, h=100) rect3 = dict(x=200, y=200, w=200, h=200) rects = [rect1, rect2, rect3] tag = 'foo' for rect in rects: TaskRunFactory.create( task=task, info=[{ 'motivation': 'tagging', 'body': { 'type': 'TextualBody', 'purpose': 'tagging', 'value': tag }, 'target': { 'source': 'example.com', 'selector': { 'conformsTo': 'http://www.w3.org/TR/media-frags/', 'type': 'FragmentSelector', 'value': '?xywh={0},{1},{2},{3}'.format( rect['x'], rect['y'], rect['w'], rect['h']) } } }]) result = self.result_repo.filter_by(project_id=task.project_id)[0] fake_search = MagicMock() fake_search.return_value = [] mock_client.search_annotations = fake_search self.iiif_analyst.analyse(result.id) assert_equal(mock_client.create_annotation.call_args_list, [ call( anno_collection, { 'motivation': 'tagging', 'type': 'Annotation', 'generator': [{ "id": flask_app.config.get('GITHUB_REPO'), "type": "Software", "name": "LibCrowds", "homepage": flask_app.config.get('SPA_SERVER_NAME') }, { "id": url_for('api.api_task', oid=task.id), "type": "Software" }], 'body': { 'type': 'TextualBody', 'purpose': 'tagging', 'value': tag }, 'target': { 'source': 'example.com', 'selector': { 'conformsTo': 'http://www.w3.org/TR/media-frags/', 'type': 'FragmentSelector', 'value': '?xywh={0},{1},{2},{3}'.format( rect1['x'], rect1['y'], rect1['w'], rect1['h']) } } }), call( anno_collection, { 'motivation': 'tagging', 'type': 'Annotation', 'generator': [ { "id": flask_app.config.get('GITHUB_REPO'), "type": "Software", "name": "LibCrowds", "homepage": flask_app.config.get('SPA_SERVER_NAME') }, { "id": url_for('api.api_task', oid=task.id), "type": "Software" } ], 'body': { 'type': 'TextualBody', 'purpose': 'tagging', 'value': tag }, 'target': { 'source': 'example.com', 'selector': { 'conformsTo': 'http://www.w3.org/TR/media-frags/', 'type': 'FragmentSelector', 'value': '?xywh={0},{1},{2},{3}'.format( rect2['x'], rect2['y'], rect2['w'], rect2['h']) } } }), call( anno_collection, { 'motivation': 'tagging', 'type': 'Annotation', 'generator': [ { "id": flask_app.config.get('GITHUB_REPO'), "type": "Software", "name": "LibCrowds", "homepage": flask_app.config.get('SPA_SERVER_NAME') }, { "id": url_for('api.api_task', oid=task.id), "type": "Software" } ], 'body': { 'type': 'TextualBody', 'purpose': 'tagging', 'value': tag }, 'target': { 'source': 'example.com', 'selector': { 'conformsTo': 'http://www.w3.org/TR/media-frags/', 'type': 'FragmentSelector', 'value': '?xywh={0},{1},{2},{3}'.format( rect3['x'], rect3['y'], rect3['w'], rect3['h']) } } }) ]) @with_context @patch('pybossa_lc.model.base.wa_client') def test_old_annotations_deleted(self, mock_client): """Test IIIF old Annotations deleted.""" n_answers = 3 target = 'example.com' task = self.ctx.create_task(n_answers, target) user = UserFactory() TaskRunFactory.create_batch(n_answers, user=user, task=task, info=[{ 'motivation': 'commenting', 'body': { 'value': 'foo' } }]) result = self.result_repo.filter_by(project_id=task.project_id)[0] fake_annos = [{'id': 'baz'}, {'id': 'qux'}] fake_search = MagicMock() fake_search.return_value = fake_annos mock_client.search_annotations = fake_search self.iiif_analyst.analyse(result.id, analyse_full=True) mock_client.delete_batch.assert_called_once_with(fake_annos) @with_context @patch('pybossa_lc.model.base.wa_client') def test_results_with_annotations_not_analysed(self, mock_client): """Test results with Annotations already not analysed by default.""" n_answers = 3 target = 'example.com' task = self.ctx.create_task(n_answers, target) user = UserFactory() TaskRunFactory.create_batch(n_answers, user=user, task=task, info=[{ 'motivation': 'commenting', 'body': { 'value': 'foo' } }]) result = self.result_repo.filter_by(project_id=task.project_id)[0] fake_annos = [{'id': 'baz'}, {'id': 'qux'}] fake_search = MagicMock() fake_search.return_value = fake_annos mock_client.search_annotations = fake_search self.iiif_analyst.analyse(result.id) assert_equal(mock_client.delete_batch.called, False) @with_context @patch('pybossa_lc.model.base.wa_client') def test_annotation_collection_iri_added_to_result_info(self, mock_client): """Test IIIF result info updated with AnnotationCollection IRI.""" n_answers = 3 target = 'example.com' anno_collection = 'annotations.example.com' task = self.ctx.create_task(n_answers, target, anno_collection=anno_collection) user = UserFactory() TaskRunFactory.create_batch(n_answers, user=user, task=task, info=[{ 'motivation': 'commenting', 'body': { 'value': 'foo' } }]) result = self.result_repo.filter_by(project_id=task.project_id)[0] fake_search = MagicMock() fake_search.return_value = [] mock_client.search_annotations = fake_search self.iiif_analyst.analyse(result.id) assert_dict_equal(result.info, {'annotations': anno_collection}) @with_context @patch('pybossa_lc.model.base.wa_client') def test_task_rejected(self, mock_client): """Test IIIF annotation not created when task rejected * n_answers.""" n_answers = 3 target = 'example.com' anno_collection = 'annotations.example.com' task = self.ctx.create_task(n_answers, target, anno_collection=anno_collection) user = UserFactory() reason = 'invalid-task' TaskRunFactory.create_batch(n_answers, user=user, task=task, info={'reject': reason}) result = self.result_repo.filter_by(project_id=task.project_id)[0] fake_search = MagicMock() fake_search.return_value = [] mock_client.search_annotations = fake_search self.iiif_analyst.analyse(result.id) assert not mock_client.create_annotation.called assert_dict_equal(result.info, { 'annotations': anno_collection, 'rejected': reason })
def setUp(self): super(TestResultRepository, self).setUp() self.result_repo = ResultRepository(db)
def setUp(self): super(TestResultAPI, self).setUp() self.result_repo = ResultRepository(db)
class TestZ3950Analyst(Test): def setUp(self): super(TestZ3950Analyst, self).setUp() self.ctx = ContextFixtures() self.z3950_analyst = Z3950Analyst() self.result_repo = ResultRepository(db) self.task_repo = TaskRepository(db) self.data = { 'user_id': [1], 'control_number': ['123'], 'reference': ['abc'], 'foo': ['bar'], 'comments': ['Some comment'] } def test_get_comments(self): """Test Z3950 comments are returned.""" task_run_df = pandas.DataFrame(self.data) comments = self.z3950_analyst.get_comments(task_run_df) expected = [(self.data['user_id'][i], self.data['comments'][i]) for i in range(len(self.data['user_id']))] assert_equal(comments, expected) def test_get_tags(self): """Test Z3950 tags are returned.""" task_run_df = pandas.DataFrame(self.data) tags = self.z3950_analyst.get_tags(task_run_df) assert_dict_equal(tags, {}) def test_get_transcriptions_df(self): """Test Z3950 transcriptions are returned.""" task_run_df = pandas.DataFrame(self.data) df = self.z3950_analyst.get_transcriptions_df(task_run_df) assert_dict_equal( df.to_dict(), { 'control_number': dict(enumerate(self.data['control_number'])), 'reference': dict(enumerate(self.data['reference'])) }) @with_context @patch('pybossa_lc.model.base.wa_client') def test_analysis_with_no_transcriptions(self, mock_client): """Test Z3950 analysis with no transcriptions.""" n_answers = 3 target = 'example.com' task = self.ctx.create_task(n_answers, target) TaskRunFactory.create_batch(n_answers, task=task, info={ 'control_number': '', 'reference': '', 'comments': '' }) result = self.result_repo.filter_by(project_id=task.project_id)[0] fake_search = MagicMock() fake_search.return_value = [] mock_client.search_annotations = fake_search self.z3950_analyst.analyse(result.id) assert_equal(mock_client.create_annotation.called, False) @with_context @patch('pybossa_lc.model.base.wa_client') def test_analysis_with_no_transcriptions_and_old_keys(self, mock_client): """Test Z3950 analysis with no transcriptions and old keys.""" n_answers = 3 target = 'example.com' task = self.ctx.create_task(n_answers, target) TaskRunFactory.create_batch(n_answers, task=task, info={ 'oclc': '', 'shelfmark': '', 'comments': '' }) result = self.result_repo.filter_by(project_id=task.project_id)[0] fake_search = MagicMock() fake_search.return_value = [] mock_client.search_annotations = fake_search self.z3950_analyst.analyse(result.id) assert_equal(mock_client.create_annotation.called, False) @with_context @patch('pybossa_lc.model.base.wa_client') def test_analysis_with_no_reference(self, mock_client): """Test Z3950 analysis with no reference.""" n_answers = 3 target = 'example.com' task = self.ctx.create_task(n_answers, target) TaskRunFactory.create_batch(n_answers, task=task, info={ 'control_number': 'foo', 'reference': '', 'comments': '' }) result = self.result_repo.filter_by(project_id=task.project_id)[0] fake_search = MagicMock() fake_search.return_value = [] mock_client.search_annotations = fake_search self.z3950_analyst.analyse(result.id) assert_equal(mock_client.create_annotation.called, False) @with_context @patch('pybossa_lc.model.base.wa_client') def test_analysis_with_no_control_number(self, mock_client): """Test Z3950 analysis with no control number.""" n_answers = 3 target = 'example.com' task = self.ctx.create_task(n_answers, target) TaskRunFactory.create_batch(n_answers, task=task, info={ 'control_number': '', 'reference': 'foo', 'comments': '' }) result = self.result_repo.filter_by(project_id=task.project_id)[0] fake_search = MagicMock() fake_search.return_value = [] mock_client.search_annotations = fake_search self.z3950_analyst.analyse(result.id) assert_equal(mock_client.create_annotation.called, False) @with_context @patch('pybossa_lc.model.base.wa_client') def test_comment_annotation_created(self, mock_client): """Test Z3950 comment annotations are created.""" n_answers = 1 target = 'example.com' anno_collection = 'http://eg.com/collection' task = self.ctx.create_task(n_answers, target, anno_collection=anno_collection) user = UserFactory() value = 'foo' TaskRunFactory.create_batch(n_answers, user=user, task=task, info={ 'control_number': '', 'reference': '', 'comments': value }) result = self.result_repo.filter_by(project_id=task.project_id)[0] fake_search = MagicMock() fake_search.return_value = [] mock_client.search_annotations = fake_search self.z3950_analyst.analyse(result.id) func = mock_client.create_annotation func.assert_called_once_with( anno_collection, { 'motivation': 'commenting', 'type': 'Annotation', 'creator': { 'id': url_for('api.api_user', oid=user.id), 'type': 'Person', 'name': user.fullname, 'nickname': user.name }, 'generator': [{ "id": flask_app.config.get('GITHUB_REPO'), "type": "Software", "name": "LibCrowds", "homepage": flask_app.config.get('SPA_SERVER_NAME') }, { "id": url_for('api.api_task', oid=task.id), "type": "Software" }], 'body': { 'type': 'TextualBody', 'purpose': 'commenting', 'value': value, 'format': 'text/plain' }, 'target': target }) @with_context @patch('pybossa_lc.model.base.wa_client') def test_transcriptions_are_normalised(self, mock_client): """Test Z3950 transcriptions are normalised according to set rules.""" n_answers = 1 target = 'example.com' anno_collection = 'http://eg.com/collection' rules = dict(case='title', whitespace='full_stop', trim_punctuation=True) task = self.ctx.create_task(n_answers, target, rules=rules, anno_collection=anno_collection) control_number = 'foo' references = ['OR 123 456.', 'Or.123.456. ', 'or 123 456'] for value in references: TaskRunFactory.create(task=task, info={ 'reference': value, 'control_number': control_number, 'comments': '' }) result = self.result_repo.filter_by(project_id=task.project_id)[0] fake_search = MagicMock() fake_search.return_value = [] mock_client.search_annotations = fake_search self.z3950_analyst.analyse(result.id) assert_equal( mock_client.create_annotation.call_args_list, [ call( anno_collection, { 'motivation': 'describing', 'type': 'Annotation', 'generator': [{ "id": flask_app.config.get('GITHUB_REPO'), "type": "Software", "name": "LibCrowds", "homepage": flask_app.config.get('SPA_SERVER_NAME') }, { "id": url_for('api.api_task', oid=task.id), "type": "Software" }], 'body': [{ 'type': 'TextualBody', 'purpose': 'describing', 'value': control_number.capitalize(), 'format': 'text/plain' }, { 'type': 'TextualBody', 'purpose': 'tagging', 'value': 'control_number' }], 'target': target }), call( anno_collection, { 'motivation': 'describing', 'type': 'Annotation', 'generator': [ { "id": flask_app.config.get('GITHUB_REPO'), "type": "Software", "name": "LibCrowds", "homepage": flask_app.config.get('SPA_SERVER_NAME') }, { "id": url_for('api.api_task', oid=task.id), "type": "Software" } ], 'body': [{ 'type': 'TextualBody', 'purpose': 'describing', 'value': 'Or.123.456', 'format': 'text/plain' }, { 'type': 'TextualBody', 'purpose': 'tagging', 'value': 'reference' }], 'target': target }) ]) @with_context @patch('pybossa_lc.model.base.wa_client') def test_with_matching_transcriptions(self, mock_client): """Test Z3950 results with matching transcriptions.""" n_answers = 3 target = 'example.com' anno_collection = 'http://eg.com/collection' task = self.ctx.create_task(n_answers, target, rules={}, anno_collection=anno_collection) reference = 'foo' control_number = 'bar' TaskRunFactory.create_batch(n_answers, task=task, info={ 'reference': reference, 'control_number': control_number, 'comments': '' }) result = self.result_repo.filter_by(project_id=task.project_id)[0] fake_search = MagicMock() fake_search.return_value = [] mock_client.search_annotations = fake_search self.z3950_analyst.analyse(result.id) func = mock_client.create_annotation assert_equal( mock_client.create_annotation.call_args_list, [ call( anno_collection, { 'motivation': 'describing', 'type': 'Annotation', 'generator': [{ "id": flask_app.config.get('GITHUB_REPO'), "type": "Software", "name": "LibCrowds", "homepage": flask_app.config.get('SPA_SERVER_NAME') }, { "id": url_for('api.api_task', oid=task.id), "type": "Software" }], 'body': [{ 'type': 'TextualBody', 'purpose': 'describing', 'value': control_number, 'format': 'text/plain' }, { 'type': 'TextualBody', 'purpose': 'tagging', 'value': 'control_number' }], 'target': target }), call( anno_collection, { 'motivation': 'describing', 'type': 'Annotation', 'generator': [ { "id": flask_app.config.get('GITHUB_REPO'), "type": "Software", "name": "LibCrowds", "homepage": flask_app.config.get('SPA_SERVER_NAME') }, { "id": url_for('api.api_task', oid=task.id), "type": "Software" } ], 'body': [{ 'type': 'TextualBody', 'purpose': 'describing', 'value': reference, 'format': 'text/plain' }, { 'type': 'TextualBody', 'purpose': 'tagging', 'value': 'reference' }], 'target': target }) ]) @with_context @patch('pybossa_lc.model.base.wa_client') def test_redundancy_increased_when_not_max(self, mock_client): """Test Z3950 task redundancy is updated when max not reached.""" n_answers = 3 target = 'example.com' task = self.ctx.create_task(n_answers, target, max_answers=4) for i in range(n_answers): TaskRunFactory.create(task=task, info={ 'reference': i, 'control_number': i, 'comments': '' }) result = self.result_repo.filter_by(project_id=task.project_id)[0] fake_search = MagicMock() fake_search.return_value = [] mock_client.search_annotations = fake_search self.z3950_analyst.analyse(result.id) assert_equal(mock_client.create_annotation.called, False) updated_task = self.task_repo.get_task(task.id) assert_equal(updated_task.n_answers, n_answers + 1) @with_context @patch('pybossa_lc.model.base.wa_client') def test_redundancy_not_increased_when_max(self, mock_client): """Test Z3950 task redundancy is not updated when max is reached.""" n_answers = 3 target = 'example.com' task = self.ctx.create_task(n_answers, target, max_answers=3) for i in range(n_answers): TaskRunFactory.create(task=task, info={ 'reference': i, 'control_number': i, 'comments': '' }) result = self.result_repo.filter_by(project_id=task.project_id)[0] fake_search = MagicMock() fake_search.return_value = [] mock_client.search_annotations = fake_search self.z3950_analyst.analyse(result.id) assert_equal(mock_client.create_annotation.called, False) updated_task = self.task_repo.get_task(task.id) assert_equal(updated_task.n_answers, n_answers) @with_context @patch('pybossa_lc.model.base.wa_client') def test_redundancy_not_increased_for_comments(self, mock_client): """Test Z3950 task redundancy is not updated for comments.""" n_answers = 3 target = 'example.com' task = self.ctx.create_task(n_answers, target, max_answers=n_answers + 1) for i in range(n_answers): TaskRunFactory.create(task=task, info={ 'reference': 'foo', 'control_number': 'bar', 'comments': i }) result = self.result_repo.filter_by(project_id=task.project_id)[0] fake_search = MagicMock() fake_search.return_value = [] mock_client.search_annotations = fake_search self.z3950_analyst.analyse(result.id) updated_task = self.task_repo.get_task(task.id) assert_equal(updated_task.n_answers, n_answers) @with_context @patch('pybossa_lc.model.base.wa_client') def test_redundancy_not_increased_when_no_values(self, mock_client): """Test Z3950 task redundancy is not updated when no values.""" n_answers = 3 target = 'example.com' task = self.ctx.create_task(n_answers, target, max_answers=n_answers + 1) for i in range(n_answers): TaskRunFactory.create(task=task, info={ 'reference': '', 'control_number': '', 'comments': '' }) result = self.result_repo.filter_by(project_id=task.project_id)[0] fake_search = MagicMock() fake_search.return_value = [] mock_client.search_annotations = fake_search self.z3950_analyst.analyse(result.id) updated_task = self.task_repo.get_task(task.id) assert_equal(updated_task.n_answers, n_answers) assert_equal(mock_client.create_annotation.called, False) @with_context @patch('pybossa_lc.model.base.wa_client') def test_old_annotations_deleted(self, mock_client): """Test Z3950 old Annotations deleted.""" n_answers = 3 target = 'example.com' task = self.ctx.create_task(n_answers, target) TaskRunFactory.create_batch(n_answers, task=task, info={ 'reference': '', 'control_number': '', 'comments': '' }) result = self.result_repo.filter_by(project_id=task.project_id)[0] fake_annos = [{'id': 'baz'}, {'id': 'qux'}] fake_search = MagicMock() fake_search.return_value = fake_annos mock_client.search_annotations = fake_search self.z3950_analyst.analyse(result.id, analyse_full=True) mock_client.delete_batch.assert_called_once_with(fake_annos) @with_context @patch('pybossa_lc.model.base.wa_client') def test_results_with_annotations_not_analysed(self, mock_client): """Test results with Annotations already not analysed by default.""" n_answers = 3 target = 'example.com' task = self.ctx.create_task(n_answers, target) TaskRunFactory.create_batch(n_answers, task=task, info={ 'reference': 'foo', 'control_number': 'bar', 'comments': '' }) result = self.result_repo.filter_by(project_id=task.project_id)[0] fake_annos = [{'id': 'baz'}, {'id': 'qux'}] fake_search = MagicMock() fake_search.return_value = fake_annos mock_client.search_annotations = fake_search self.z3950_analyst.analyse(result.id) assert_equal(mock_client.delete_batch.called, False) @with_context @patch('pybossa_lc.model.base.wa_client') def test_annotation_collection_iri_added_to_result_info(self, mock_client): """Test Z3950 result info updated with AnnotationCollection IRI.""" n_answers = 3 target = 'example.com' anno_collection = 'annotations.example.com' task = self.ctx.create_task(n_answers, target, anno_collection=anno_collection) TaskRunFactory.create_batch(n_answers, task=task, info={ 'reference': 'foo', 'control_number': 'bar', 'comments': '' }) result = self.result_repo.filter_by(project_id=task.project_id)[0] fake_search = MagicMock() fake_search.return_value = [] mock_client.search_annotations = fake_search self.z3950_analyst.analyse(result.id) assert_dict_equal(result.info, {'annotations': anno_collection}) @with_context @patch('pybossa_lc.model.base.wa_client') def test_task_rejected(self, mock_client): """Test Z3950 annotation not created when task rejected * n_answers.""" n_answers = 3 target = 'example.com' anno_collection = 'annotations.example.com' task = self.ctx.create_task(n_answers, target, anno_collection=anno_collection) user = UserFactory() reason = 'invalid-task' TaskRunFactory.create_batch(n_answers, user=user, task=task, info={'reject': reason}) result = self.result_repo.filter_by(project_id=task.project_id)[0] fake_search = MagicMock() fake_search.return_value = [] mock_client.search_annotations = fake_search self.z3950_analyst.analyse(result.id) assert not mock_client.create_annotation.called assert_dict_equal(result.info, { 'annotations': anno_collection, 'rejected': reason })
class TestResultRepository(Test): def setUp(self): super(TestResultRepository, self).setUp() self.result_repo = ResultRepository(db) @with_context def create_result(self, n_answers=1, filter_by=False): task = TaskFactory.create(n_answers=n_answers) TaskRunFactory.create(task=task) if filter_by: return self.result_repo.filter_by(project_id=1) else: return self.result_repo.get_by(project_id=1) @with_context def test_get_return_none_if_no_result(self): """Test get method returns None if there is no result with the specified id""" result = self.result_repo.get(2) assert result is None, result @with_context def test_get_returns_result(self): """Test get method returns a result if exists""" n_answers = 1 task = TaskFactory.create(n_answers=n_answers) task_run = TaskRunFactory.create(task=task) result = self.result_repo.filter_by(project_id=1) err_msg = "There should be a result" assert len(result) == 1, err_msg result = result[0] assert result.project_id == 1, err_msg assert result.task_id == task.id, err_msg assert len(result.task_run_ids) == n_answers, err_msg err_msg = "The task_run id is missing in the results array" for tr_id in result.task_run_ids: assert tr_id == task_run.id, err_msg @with_context def test_get_by_returns_result(self): """Test get_by method returns a result if exists""" n_answers = 1 task = TaskFactory.create(n_answers=n_answers) task_run = TaskRunFactory.create(task=task) result = self.result_repo.get_by(project_id=1) err_msg = "There should be a result" assert result.project_id == 1, err_msg assert result.task_id == task.id, err_msg assert len(result.task_run_ids) == n_answers, err_msg err_msg = "The task_run id is missing in the results array" for tr_id in result.task_run_ids: assert tr_id == task_run.id, err_msg @with_context def test_get_returns_result_after_increasig_redundancy(self): """Test get method returns a result if after increasing redundancy""" n_answers = 1 task = TaskFactory.create(n_answers=n_answers) task_run = TaskRunFactory.create(task=task) result = self.result_repo.filter_by(project_id=1) err_msg = "There should be a result" assert len(result) == 1, err_msg result = result[0] assert result.project_id == 1, err_msg assert result.task_id == task.id, err_msg assert len(result.task_run_ids) == n_answers, err_msg err_msg = "The task_run id is missing in the results array" for tr_id in result.task_run_ids: assert tr_id == task_run.id, err_msg # Increase redundancy tmp = task_repo.get_task(task.id) tmp.n_answers = 2 task_repo.update(task) err_msg = "There should be only one result" results = result_repo.filter_by(project_id=1) assert len(results) == 1, err_msg task_run_2 = TaskRunFactory.create(task=task) err_msg = "There should be 1 results" results = result_repo.filter_by(project_id=1) assert len(results) == 1, err_msg err_msg = "There should be 2 results" results = result_repo.filter_by(project_id=1, last_version=False) assert len(results) == 2, err_msg assert results[1].project_id == 1, err_msg assert results[1].task_id == task.id, err_msg err_msg = "First result should have only one task run ID" assert len(results[0].task_run_ids) == 1, err_msg err_msg = "Second result should have only two task run IDs" assert len(results[1].task_run_ids) == 2, err_msg err_msg = "The task_run id is missing in the results array" for tr_id in results[1].task_run_ids: assert tr_id in [task_run.id, task_run_2.id], err_msg @with_context def test_get_returns_no_result(self): """Test get method does not return a result if task not completed""" n_answers = 3 task = TaskFactory.create(n_answers=n_answers) TaskRunFactory.create(task=task) result = self.result_repo.filter_by(project_id=1) err_msg = "There should not be a result" assert len(result) == 0, err_msg @with_context def test_fulltext_search_result(self): """Test fulltext search in JSON info works.""" result = self.create_result() text = 'something word you me bar' data = {'foo': text} result.info = data self.result_repo.update(result) info = 'foo::word' res = self.result_repo.filter_by(info=info, fulltextsearch='1') assert len(res) == 1, len(res) assert res[0][0].info['foo'] == text, res[0] res = self.result_repo.filter_by(info=info) assert len(res) == 0, len(res) @with_context def test_fulltext_search_result_01(self): """Test fulltext search in JSON info works.""" result = self.create_result() text = 'something word you me bar' data = {'foo': text, 'bar': 'foo'} result.info = data self.result_repo.update(result) info = 'foo::word&bar|bar::foo' res = self.result_repo.filter_by(info=info, fulltextsearch='1') assert len(res) == 1, len(res) assert res[0][0].info['foo'] == text, res[0] @with_context def test_info_json_search_result(self): """Test search in JSON info works.""" result = self.create_result() text = 'bar' data = {'foo': text} result.info = data self.result_repo.update(result) info = 'foo::bar' res = self.result_repo.filter_by(info=info) assert len(res) == 1, len(res) assert res[0].info['foo'] == text, res[0] @with_context def test_update(self): """Test update persists the changes made to the result""" result = self.create_result() result.info = dict(new='value') self.result_repo.update(result) updated_result = self.result_repo.get(result.id) assert updated_result.info['new'] == 'value', updated_result @with_context def test_update_fails_if_integrity_error(self): """Test update raises a DBIntegrityError if the instance to be updated lacks a required value""" result = self.create_result() result.project_id = None assert_raises(DBIntegrityError, self.result_repo.update, result) @with_context def test_update_only_updates_results(self): """Test update raises a WrongObjectError when an object which is not a Result instance is updated""" bad_object = dict() assert_raises(WrongObjectError, self.result_repo.update, bad_object) @with_context def test_delete_results_from_project(self): """Test delte_results_from_project works.""" project = ProjectFactory.create() task = TaskFactory.create(project=project,n_answers=1) taskrun = TaskRunFactory.create(task=task, project=project) result = result_repo.get_by(project_id=task.project.id) assert result result_repo.delete_results_from_project(project) result = result_repo.get_by(project_id=task.project.id) assert result is None
class TestResultAPI(TestAPI): def setUp(self): super(TestResultAPI, self).setUp() self.result_repo = ResultRepository(db) def create_result(self, n_results=1, n_answers=1, owner=None, filter_by=False): if owner: owner = owner else: owner = UserFactory.create() project = ProjectFactory.create(owner=owner) tasks = [] for i in range(n_results): tasks.append( TaskFactory.create(n_answers=n_answers, project=project)) for i in range(n_answers): for task in tasks: TaskRunFactory.create(task=task, project=project) if filter_by: return self.result_repo.filter_by(project_id=1) else: return self.result_repo.get_by(project_id=1) @with_context @patch('pybossa.api.task.TaskAPI._verify_auth') def test_result_query_without_params(self, auth): """ Test API Result query""" user = UserFactory.create() auth.return_value = True result = self.create_result(n_answers=10) res = self.app.get('/api/result?all=1&api_key=' + user.api_key) results = json.loads(res.data) assert len(results) == 1, results result = results[0] assert result['info'] is None, result assert len(result['task_run_ids']) == 10, result assert result['task_run_ids'] == [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], result assert result['project_id'] == 1, result assert result['task_id'] == 1, result assert result['created'] is not None, result # Related res = self.app.get('/api/result?related=True&all=1&api_key=' + user.api_key) results = json.loads(res.data) assert len(results) == 1, results result = results[0] assert result['info'] is None, result assert len(result['task_run_ids']) == 10, result assert result['task_run_ids'] == [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], result assert result['project_id'] == 1, result assert result['task_id'] == 1, result assert result['created'] is not None, result assert result['task']['id'] == result['task_id'], result assert len(result['task_runs']) == 10, result for tr in result['task_runs']: assert tr['task_id'] == result['task_id'], tr url = '/api/taskrun?id=%s&related=True&all=1&api_key=%s' % ( tr['id'], user.api_key) taskrun = self.app.get(url) taskrun = json.loads(taskrun.data)[0] assert taskrun['result']['id'] == result['id'], taskrun['result'] assert taskrun['task']['id'] == result['task_id'], taskrun['task'] url = '/api/task?&all=1&id=%s&related=True&api_key=%s' % ( result['task_id'], user.api_key) task = self.app.get(url) task = json.loads(task.data)[0] assert task['result']['id'] == result['id'], task['result'] for tr in task['task_runs']: assert tr['id'] in result['task_run_ids'], task['task'] result = self.create_result(n_answers=10) result = result_repo.get(2) result.created = '2119-01-01T14:37:30.642119' result_repo.update(result) url = '/api/result?orderby=created&desc=true&all=1&api_key=' + user.api_key res = self.app.get(url) data = json.loads(res.data) print data err_msg = "It should get the last item first." assert data[0]['created'] == '2119-01-01T14:37:30.642119', err_msg url = '/api/result?orderby=id&desc=false&all=1&api_key=' + user.api_key res = self.app.get(url) data = json.loads(res.data) err_msg = "It should be sorted by id." assert data[1]['id'] == result.id, err_msg url = '/api/result?orderby=wrongattribute&api_key=' + user.api_key res = self.app.get(url) data = json.loads(res.data) err_msg = "It should be 415." assert data['status'] == 'failed', data assert data['status_code'] == 415, data assert 'has no attribute' in data['exception_msg'], data url = '/api/result?&all=1&api_key=' + user.api_key res = self.app.get(url) data = json.loads(res.data) err_msg = "It should get not the last item first." assert data[0]['created'] != '2119-01-01T14:37:30.642119', err_msg # The output should have a mime-type: application/json assert res.mimetype == 'application/json', res @with_context def test_result_query_without_params_with_context(self): """ Test API Result query with context.""" user = UserFactory.create() result = self.create_result(n_answers=10) res = self.app.get('/api/result?&all=1&api_key=' + user.api_key) results = json.loads(res.data) assert len(results) == 1, results result = results[0] assert result['info'] is None, result assert len(result['task_run_ids']) == 10, result assert result['task_run_ids'] == [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], result assert result['project_id'] == 1, result assert result['task_id'] == 1, result assert result['created'] is not None, result # The output should have a mime-type: application/json assert res.mimetype == 'application/json', res @with_context def test_result_query_with_params(self): """Test API query for result with params works""" owner = UserFactory.create() results = self.create_result(n_results=10, filter_by=True, owner=owner) # Test for real field res = self.app.get("/api/result?api_key=" + owner.api_key) data = json.loads(res.data) # Should return one result assert len(data) == 10, data # Correct result assert data[0]['project_id'] == 1, data res = self.app.get("/api/project?api_key=" + owner.api_key) project = json.loads(res.data) assert len(project) == 1, project assert project[0]['owner_id'] == owner.id, project owner_two = UserFactory.create() res = self.app.get("/api/result?api_key=" + owner_two.api_key) data = json.loads(res.data) err_403 = "Regular user should be Forbidden from accessing results" assert res.status_code == 403, err_403 owner_two = UserFactory.create() res = self.app.get("/api/result?all=1&api_key=" + owner_two.api_key) data = json.loads(res.data) assert res.status_code == 403, err_403 # Valid field but wrong value url = "/api/result?project_id=99999999&api_key=" + owner.api_key res = self.app.get(url) data = json.loads(res.data) assert len(data) == 0, data # Multiple fields url = '/api/result?project_id=1&task_id=1&api_key=' + owner.api_key res = self.app.get(url) data = json.loads(res.data) # One result assert len(data) == 1, data # Correct result assert data[0]['project_id'] == 1, data assert data[0]['task_id'] == 1, data # Multiple fields url = '/api/result?project_id=1&task_id=1&api_key=' + owner_two.api_key res = self.app.get(url) assert res.status_code == 403, err_403 url = '/api/result?all=1&project_id=1&task_id=1&api_key=' + owner_two.api_key res = self.app.get(url) assert res.status_code == 403, err_403 # Limits url = "/api/result?project_id=1&limit=5&api_key=" + owner.api_key res = self.app.get(url) data = json.loads(res.data) for item in data: assert item['project_id'] == 1, item assert len(data) == 5, len(data) # Limits url = "/api/result?project_id=1&limit=5&api_key=" + owner_two.api_key res = self.app.get(url) assert res.status_code == 403, err_403 # Limits url = "/api/result?all=1&project_id=1&limit=5&api_key=" + owner_two.api_key res = self.app.get(url) assert res.status_code == 403, err_403 # Keyset pagination url = "/api/result?project_id=1&limit=5&last_id=1&api_key=" + owner.api_key res = self.app.get(url) data = json.loads(res.data) for item in data: assert item['project_id'] == 1, item assert len(data) == 5, data assert data[0]['id'] == 2, data[0] # Keyset pagination url = "/api/result?project_id=1&limit=5&last_id=1&api_key=" + owner_two.api_key res = self.app.get(url) assert res.status_code == 403, err_403 # Keyset pagination url = "/api/result?all=1&project_id=1&limit=5&last_id=1&api_key=" + owner.api_key res = self.app.get(url) data = json.loads(res.data) for item in data: assert item['project_id'] == 1, item assert len(data) == 5, data assert data[0]['id'] == 2, data[0] @with_context def test_result_post(self): """Test API Result creation""" admin = UserFactory.create() user = UserFactory.create() non_owner = UserFactory.create() project = ProjectFactory.create(owner=user) data = dict(info='final result') # anonymous user # no api-key res = self.app.post('/api/result', data=json.dumps(data)) error_msg = 'Should not be allowed to create' assert_equal(res.status, '401 UNAUTHORIZED', error_msg) ### real user but not allowed as not owner! res = self.app.post('/api/result?api_key=' + non_owner.api_key, data=json.dumps(data)) error_msg = 'Should not be able to post tasks for projects of others' assert_equal(res.status, '403 FORBIDDEN', error_msg) # now a real user res = self.app.post('/api/result?api_key=' + user.api_key, data=json.dumps(data)) assert_equal(res.status, '403 FORBIDDEN', error_msg) # now the root user res = self.app.post('/api/result?api_key=' + admin.api_key, data=json.dumps(data)) assert_equal(res.status, '400 BAD REQUEST', error_msg) # POST with not JSON data url = '/api/result?api_key=%s' % user.api_key res = self.app.post(url, data=data) err = json.loads(res.data) assert res.status_code == 415, err assert err['status'] == 'failed', err assert err['target'] == 'result', err assert err['action'] == 'POST', err assert err['exception_cls'] == 'ValueError', err # POST with not allowed args res = self.app.post(url + '&foo=bar', data=json.dumps(data)) err = json.loads(res.data) assert res.status_code == 415, err assert err['status'] == 'failed', err assert err['target'] == 'result', err assert err['action'] == 'POST', err assert err['exception_cls'] == 'AttributeError', err # POST with fake data data['wrongfield'] = 13 res = self.app.post(url, data=json.dumps(data)) err = json.loads(res.data) assert res.status_code == 415, err assert err['status'] == 'failed', err assert err['target'] == 'result', err assert err['action'] == 'POST', err assert err['exception_cls'] == 'TypeError', err @with_context def test_result_post_with_reserved_fields_returns_error(self): user = UserFactory.create() project = ProjectFactory.create(owner=user) data = {'created': 'today', 'id': 222, 'project_id': project.id} res = self.app.post('/api/result?api_key=' + user.api_key, data=json.dumps(data)) assert res.status_code == 400, res.status_code error = json.loads(res.data) assert error['exception_msg'] == "Reserved keys in payload", error @with_context def test_result_put_with_reserved_fields_returns_error(self): user = UserFactory.create() result = self.create_result(owner=user) print result url = '/api/result/%s?api_key=%s' % (result.id, user.api_key) data = {'created': 'today', 'project_id': 1, 'id': 222} res = self.app.put(url, data=json.dumps(data)) assert res.status_code == 400, res.status_code error = json.loads(res.data) assert error['exception_msg'] == "Reserved keys in payload", error @with_context def test_result_update(self): """Test API result update""" admin = UserFactory.create() user = UserFactory.create() make_subadmin(user) non_owner = UserFactory.create() data = dict(info=dict(foo='bar')) datajson = json.dumps(data) result = self.create_result(owner=user) ## anonymous res = self.app.put('/api/result/%s' % result.id, data=datajson) assert_equal(res.status, '401 UNAUTHORIZED', res.status) ### real user but not allowed as not owner! url = '/api/result/%s?api_key=%s' % (result.id, non_owner.api_key) res = self.app.put(url, data=datajson) assert_equal(res.status, '403 FORBIDDEN', res.status) ### real user url = '/api/result/%s?api_key=%s' % (result.id, user.api_key) res = self.app.put(url, data=datajson) out = json.loads(res.data) assert_equal(res.status, '200 OK', res.data) assert_equal(result.info['foo'], data['info']['foo']) assert result.id == out['id'], out ### root data = dict(info=dict(foo='root')) datajson = json.dumps(data) res = self.app.put('/api/result/%s?api_key=%s' % (result.id, admin.api_key), data=datajson) assert_equal(res.status, '200 OK', res.status) assert_equal(result.info['foo'], data['info']['foo']) assert result.id == out['id'], out # PUT with not JSON data res = self.app.put(url, data=None) err = json.loads(res.data) assert res.status_code == 415, err assert err['status'] == 'failed', err assert err['target'] == 'result', err assert err['action'] == 'PUT', err assert err['exception_cls'] == 'ValueError', err # PUT with not allowed args res = self.app.put(url + "&foo=bar", data=json.dumps(data)) err = json.loads(res.data) assert res.status_code == 415, err assert err['status'] == 'failed', err assert err['target'] == 'result', err assert err['action'] == 'PUT', err assert err['exception_cls'] == 'AttributeError', err # PUT with fake data data['wrongfield'] = 13 res = self.app.put(url, data=json.dumps(data)) err = json.loads(res.data) assert res.status_code == 415, err assert err['status'] == 'failed', err assert err['target'] == 'result', err assert err['action'] == 'PUT', err assert err['exception_cls'] == 'TypeError', err @with_context def test_result_delete(self): """Test API result delete""" admin = UserFactory.create() user = UserFactory.create() non_owner = UserFactory.create() result = self.create_result(owner=user) ## anonymous res = self.app.delete('/api/result/%s' % result.id) error_msg = 'Anonymous should not be allowed to update' assert_equal(res.status, '401 UNAUTHORIZED', error_msg) ### real user but not allowed as not owner! url = '/api/result/%s?api_key=%s' % (result.id, non_owner.api_key) res = self.app.delete(url) error_msg = 'Should not be able to update tasks of others' assert_equal(res.status, '403 FORBIDDEN', error_msg) #### real user # DELETE with not allowed args res = self.app.delete(url + "&foo=bar") err = json.loads(res.data) assert res.status_code == 415, err assert err['status'] == 'failed', err assert err['target'] == 'result', err assert err['action'] == 'DELETE', err assert err['exception_cls'] == 'AttributeError', err # DELETE returns 403 url = '/api/result/%s?api_key=%s' % (result.id, user.api_key) res = self.app.delete(url) assert_equal(res.status, '403 FORBIDDEN', res.data) #### root user url = '/api/result/%s?api_key=%s' % (result.id, admin.api_key) res = self.app.delete(url) assert_equal(res.status, '403 FORBIDDEN', res.data) @with_context def test_get_last_version(self): """Test API result returns always latest version.""" result = self.create_result() project = project_repo.get(result.project_id) task = task_repo.get_task(result.task_id) task.n_answers = 2 TaskRunFactory.create(task=task, project=project) result = result_repo.get_by(project_id=project.id) assert result.last_version is True, result.last_version result_id = result.id results = result_repo.filter_by(project_id=project.id, last_version=False) assert len(results) == 2, len(results) for r in results: if r.id == result_id: assert r.last_version is True, r.last_version else: assert r.last_version is False, r.last_version @with_context def test_result_admin_post(self): """Test API Result creation""" admin = UserFactory.create() user = UserFactory.create() project = ProjectFactory.create(owner=user) data = dict(info='final result') # now the root user res = self.app.post('/api/result?api_key=' + admin.api_key, data=json.dumps(data)) assert res.status == '400 BAD REQUEST', res.status assert json.loads(res.data)['exception_msg'] == 'Invalid task id' task = TaskFactory.create(project=project, n_answers=1) data['task_id'] = task.id res = self.app.post('/api/result?api_key=' + admin.api_key, data=json.dumps(data)) assert res.status == '400 BAD REQUEST', res.status assert json.loads(res.data)['exception_msg'] == 'Invalid task' taskrun = TaskRunFactory.create(task=task) res = self.app.post('/api/result?api_key=' + admin.api_key, data=json.dumps(data)) assert res.status == '400 BAD REQUEST', res.status assert json.loads( res.data)['exception_msg'] == 'Record is already present' from pybossa.core import result_repo result_repo.delete_results_from_project(project) res = self.app.post('/api/result?api_key=' + admin.api_key, data=json.dumps(data)) assert res.status == '200 OK', res.status res_data = json.loads(res.data) assert res_data['task_id'] == task.id assert res_data['project_id'] == project.id assert res_data['info'] == data['info'] assert res_data['last_version'] assert res_data['task_run_ids'] == [taskrun.id]
def setUp(self): super(TestBulkTaskIIIFEnhancedImport, self).setUp() self.result_repo = ResultRepository(db) self.manifest_uri = 'http://example.org/iiif/book1/manifest' self.canvas_id_base = 'http://example.org/iiif/book1/canvas/p{0}' self.img_id_base = 'http://example.org/images/book1-page{0}-img{1}'
class TestBulkTaskIIIFEnhancedImport(Test): def setUp(self): super(TestBulkTaskIIIFEnhancedImport, self).setUp() self.result_repo = ResultRepository(db) self.manifest_uri = 'http://example.org/iiif/book1/manifest' self.canvas_id_base = 'http://example.org/iiif/book1/canvas/p{0}' self.img_id_base = 'http://example.org/images/book1-page{0}-img{1}' def create_manifest(self, canvases=1, images=1): manifest = { '@context': 'http://iiif.io/api/presentation/2/context.json', '@id': self.manifest_uri, '@type': 'sc:Manifest', 'label': 'Foo', 'sequences': [{ '@type': 'sc:Sequence', 'canvases': [] }] } for i in range(canvases): canvas = { '@id': self.canvas_id_base.format(i), '@type': 'sc:Canvas', 'label': 'Bar', 'height': 100, 'width': 100, 'images': [] } for j in range(images): image = { '@type': 'oa:Annotation', 'motivation': 'sc:painting', 'resource': { '@id': 'http://example.org/image{}.jpg'.format(j), '@type': 'dctypes:Image', 'service': { '@id': self.img_id_base.format(i, j) } }, 'on': 'http://example.org/{}'.format(i) } canvas['images'].append(image) manifest['sequences'][0]['canvases'].append(canvas) return manifest @with_context def test_bl_tasks_created_with_bl_link(self, requests): """Test that non-BL tasks are created with a non-BL link.""" manifest = self.create_manifest() headers = {'Content-Type': 'application/json'} response = FakeResponse(text=json.dumps(manifest), status_code=200, headers=headers, encoding='utf-8') requests.get.return_value = response importer = BulkTaskIIIFEnhancedImporter(manifest_uri=self.manifest_uri) tasks = importer.tasks() assert_equal(len(tasks), 1) link_query = '?manifest={}#?cv=0'.format(self.manifest_uri) link = 'http://universalviewer.io/uv.html' + link_query assert_equal(tasks[0]['info']['link'], link) @with_context def test_non_bl_tasks_created_with_non_bl_link(self, requests): """Test that non-BL tasks are created with a non-BL link.""" manifest = self.create_manifest() bl_manifest_id = 'https://api.bl.uk/metadata/iiif/id/manifest.json' manifest['@id'] = bl_manifest_id headers = {'Content-Type': 'application/json'} response = FakeResponse(text=json.dumps(manifest), status_code=200, headers=headers, encoding='utf-8') requests.get.return_value = response importer = BulkTaskIIIFEnhancedImporter(manifest_uri=bl_manifest_id) tasks = importer.tasks() assert_equal(len(tasks), 1) link = 'http://access.bl.uk/item/viewer/id#?cv=0' assert_equal(tasks[0]['info']['link'], link) @with_context def test_exeption_if_no_collection_iri_for_parent(self, requests): """Test exception if no collection iri when child tasks generated.""" manifest = self.create_manifest() headers = {'Content-Type': 'application/json'} response = FakeResponse(text=json.dumps(manifest), status_code=200, headers=headers, encoding='utf-8') requests.get.return_value = response parent = ProjectFactory() task = TaskFactory(project=parent, n_answers=1) TaskRunFactory.create(task=task) importer = BulkTaskIIIFEnhancedImporter(manifest_uri=self.manifest_uri, parent_id=parent.id) assert_raises(BulkImportException, importer.tasks) @with_context @patch('pybossa_lc.model.base.wa_client') def test_child_tasks_generated(self, mock_wa_client, requests): """Test that child tasks are generated.""" n_canvases = 3 n_images = 1 manifest = self.create_manifest(canvases=n_canvases, images=n_images) headers = {'Content-Type': 'application/json'} response = FakeResponse(text=json.dumps(manifest), status_code=200, headers=headers, encoding='utf-8') requests.get.return_value = response anno_fixtures = AnnotationFixtures() anno_collection_iri = 'example.org/annotations' category = CategoryFactory( info={'annotations': { 'results': anno_collection_iri }}) parent = ProjectFactory(category=category) tasks = TaskFactory.create_batch(n_canvases, project=parent, n_answers=1) # Create some annotations for each parent task expected = [] return_values = [] for i, task in enumerate(tasks): canvas_id = self.canvas_id_base.format(i) for j in range(n_images): TaskRunFactory.create(task=task) img_id = self.img_id_base.format(i, j) annotations = [ anno_fixtures.create(motivation='tagging', source=canvas_id), anno_fixtures.create(motivation='describing', source=canvas_id), anno_fixtures.create(motivation='commenting', source=canvas_id) ] result = self.result_repo.get_by(task_id=task.id) result.info = dict(annotations=anno_collection_iri) self.result_repo.update(result) return_values.append(annotations) # Store expected task data to check later link_query = '?manifest={}#?cv={}'.format(self.manifest_uri, i) link = 'http://universalviewer.io/uv.html' + link_query for anno in annotations[:2]: expected.append({ 'manifest': self.manifest_uri, 'target': anno['target'], 'link': link, 'tileSource': '{}/info.json'.format(img_id), 'url': '{}/full/max/0/default.jpg'.format(img_id), 'url_m': '{}/full/240,/0/default.jpg'.format(img_id), 'url_b': '{}/full/1024,/0/default.jpg'.format(img_id), 'parent_task_id': task.id }) mock_wa_client.search_annotations.side_effect = return_values importer = BulkTaskIIIFEnhancedImporter(manifest_uri=self.manifest_uri, parent_id=parent.id) tasks = importer.tasks() task_info = [task['info'] for task in tasks] expected = sorted(expected, key=lambda x: x['target']) assert_equal(task_info, expected) @with_context @patch('pybossa_lc.model.base.wa_client') def test_has_child_added_to_parent_results(self, mock_wa_client, requests): """Test that the has_children key is added to parent results.""" manifest = self.create_manifest() headers = {'Content-Type': 'application/json'} response = FakeResponse(text=json.dumps(manifest), status_code=200, headers=headers, encoding='utf-8') requests.get.return_value = response anno_collection_iri = 'example.org/annotations' # Create a task for each canvas n_tasks = 3 category = CategoryFactory( info={'annotations': { 'results': anno_collection_iri }}) parent = ProjectFactory(category=category) tasks = TaskFactory.create_batch(n_tasks, project=parent, n_answers=1) for task in tasks: TaskRunFactory.create(task=task) result = self.result_repo.get_by(task_id=task.id) result.info = dict(annotations=anno_collection_iri) self.result_repo.update(result) importer = BulkTaskIIIFEnhancedImporter(manifest_uri=self.manifest_uri, parent_id=parent.id) mock_wa_client.search_annotations.return_value = [] tasks = importer.tasks() results = self.result_repo.filter_by(project_id=parent.id) result_info = [result.info for result in results] expected = [{ 'annotations': anno_collection_iri, 'has_children': True }] * n_tasks assert_equal(result_info, expected)
def setUp(self): super(TestIIIFAnnotationAnalyst, self).setUp() self.ctx = ContextFixtures() self.result_repo = ResultRepository(db) self.task_repo = TaskRepository(db) self.iiif_analyst = IIIFAnnotationAnalyst() self.comments = ['Some comment'] self.tags = { 'foo': [ dict(x=100, y=100, w=100, h=100), dict(x=200, y=200, w=200, h=200) ], 'bar': [dict(x=300, y=300, w=300, h=300)] } transcription_data = {'foo': ['bar', 'baz'], 'qux': ['quux', 'quuz']} self.transcriptions_df = pandas.DataFrame(transcription_data) self.comment_annos = [] for comment in self.comments: self.comment_annos.append({ 'motivation': 'commenting', 'body': { 'type': 'TextualBody', 'value': comment, 'purpose': 'commenting', 'format': 'text/plain' }, 'target': 'example.com' }) self.tagging_annos = [] for tag, rect_list in self.tags.items(): for rect in rect_list: self.tagging_annos.append({ 'motivation': 'tagging', 'body': { 'type': 'TextualBody', 'purpose': 'tagging', 'value': tag }, 'target': { 'source': 'example.com', 'selector': { 'conformsTo': 'http://www.w3.org/TR/media-frags/', 'type': 'FragmentSelector', 'value': '?xywh={0},{1},{2},{3}'.format( rect['x'], rect['y'], rect['w'], rect['h']) } } }) self.transcription_annos = [] for tag, value_list in transcription_data.items(): for value in value_list: self.transcription_annos.append({ 'motivation': 'describing', 'body': [{ 'type': 'TextualBody', 'purpose': 'tagging', 'value': tag }, { 'type': 'TextualBody', 'purpose': 'describing', 'value': value, 'format': 'text/plain' }], 'target': 'example.com' }) self.data = { 'user_id': [1, 2, 3], 'info': [self.comment_annos, self.tagging_annos, self.transcription_annos] }
class TestResultAPI(TestAPI): def setUp(self): super(TestResultAPI, self).setUp() self.result_repo = ResultRepository(db) def create_result(self, n_results=1, n_answers=1, owner=None, filter_by=False): if owner: owner = owner else: owner = UserFactory.create() project = ProjectFactory.create(owner=owner) tasks = [] for i in range(n_results): tasks.append(TaskFactory.create(n_answers=n_answers, project=project)) for i in range(n_answers): for task in tasks: TaskRunFactory.create(task=task, project=project) if filter_by: return self.result_repo.filter_by(project_id=1) else: return self.result_repo.get_by(project_id=1) @with_context def test_result_query_without_params(self): """ Test API Result query""" result = self.create_result(n_answers=10) res = self.app.get('/api/result') results = json.loads(res.data) assert len(results) == 1, results result = results[0] assert result['info'] is None, result assert len(result['task_run_ids']) == 10, result assert result['task_run_ids'] == [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], result assert result['project_id'] == 1, result assert result['task_id'] == 1, result assert result['created'] is not None, result # The output should have a mime-type: application/json assert res.mimetype == 'application/json', res @with_context def test_result_query_with_params(self): """Test API query for result with params works""" results = self.create_result(n_results=10, filter_by=True) # Test for real field res = self.app.get("/api/result?project_id=1") data = json.loads(res.data) # Should return one result assert len(data) == 10, data # Correct result assert data[0]['project_id'] == 1, data # Valid field but wrong value res = self.app.get("/api/result?project_id=99999999") data = json.loads(res.data) assert len(data) == 0, data # Multiple fields res = self.app.get('/api/result?project_id=1&task_id=1') data = json.loads(res.data) # One result assert len(data) == 1, data # Correct result assert data[0]['project_id'] == 1, data assert data[0]['task_id'] == 1, data # Limits res = self.app.get("/api/result?project_id=1&limit=5") data = json.loads(res.data) for item in data: assert item['project_id'] == 1, item assert len(data) == 5, len(data) # Keyset pagination url = "/api/result?project_id=1&limit=5&last_id=1" res = self.app.get(url) data = json.loads(res.data) for item in data: assert item['project_id'] == 1, item assert len(data) == 5, data assert data[0]['id'] == 2, data[0] @with_context def test_result_post(self): """Test API Result creation""" admin = UserFactory.create() user = UserFactory.create() non_owner = UserFactory.create() project = ProjectFactory.create(owner=user) data = dict(info='final result') # anonymous user # no api-key res = self.app.post('/api/result', data=json.dumps(data)) error_msg = 'Should not be allowed to create' assert_equal(res.status, '401 UNAUTHORIZED', error_msg) ### real user but not allowed as not owner! res = self.app.post('/api/result?api_key=' + non_owner.api_key, data=json.dumps(data)) error_msg = 'Should not be able to post tasks for projects of others' assert_equal(res.status, '403 FORBIDDEN', error_msg) # now a real user res = self.app.post('/api/result?api_key=' + user.api_key, data=json.dumps(data)) assert_equal(res.status, '403 FORBIDDEN', error_msg) # now the root user res = self.app.post('/api/result?api_key=' + admin.api_key, data=json.dumps(data)) assert_equal(res.status, '403 FORBIDDEN', error_msg) # POST with not JSON data url = '/api/result?api_key=%s' % user.api_key res = self.app.post(url, data=data) err = json.loads(res.data) assert res.status_code == 415, err assert err['status'] == 'failed', err assert err['target'] == 'result', err assert err['action'] == 'POST', err assert err['exception_cls'] == 'ValueError', err # POST with not allowed args res = self.app.post(url + '&foo=bar', data=json.dumps(data)) err = json.loads(res.data) assert res.status_code == 415, err assert err['status'] == 'failed', err assert err['target'] == 'result', err assert err['action'] == 'POST', err assert err['exception_cls'] == 'AttributeError', err # POST with fake data data['wrongfield'] = 13 res = self.app.post(url, data=json.dumps(data)) err = json.loads(res.data) assert res.status_code == 415, err assert err['status'] == 'failed', err assert err['target'] == 'result', err assert err['action'] == 'POST', err assert err['exception_cls'] == 'TypeError', err def test_result_post_with_reserved_fields_returns_error(self): user = UserFactory.create() project = ProjectFactory.create(owner=user) data = {'created': 'today', 'id': 222, 'project_id': project.id} res = self.app.post('/api/result?api_key=' + user.api_key, data=json.dumps(data)) assert res.status_code == 400, res.status_code error = json.loads(res.data) assert error['exception_msg'] == "Reserved keys in payload", error def test_result_put_with_reserved_fields_returns_error(self): user = UserFactory.create() result = self.create_result(owner=user) print result url = '/api/result/%s?api_key=%s' % (result.id, user.api_key) data = {'created': 'today', 'project_id': 1, 'id': 222} res = self.app.put(url, data=json.dumps(data)) assert res.status_code == 400, res.status_code error = json.loads(res.data) assert error['exception_msg'] == "Reserved keys in payload", error @with_context def test_result_update(self): """Test API result update""" admin = UserFactory.create() user = UserFactory.create() non_owner = UserFactory.create() data = dict(info=dict(foo='bar')) datajson = json.dumps(data) result = self.create_result(owner=user) ## anonymous res = self.app.put('/api/result/%s' % result.id, data=datajson) assert_equal(res.status, '401 UNAUTHORIZED', res.status) ### real user but not allowed as not owner! url = '/api/result/%s?api_key=%s' % (result.id, non_owner.api_key) res = self.app.put(url, data=datajson) assert_equal(res.status, '403 FORBIDDEN', res.status) ### real user url = '/api/result/%s?api_key=%s' % (result.id, user.api_key) res = self.app.put(url, data=datajson) out = json.loads(res.data) assert_equal(res.status, '200 OK', res.data) assert_equal(result.info['foo'], data['info']['foo']) assert result.id == out['id'], out ### root res = self.app.put('/api/result/%s?api_key=%s' % (result.id, admin.api_key), data=datajson) assert_equal(res.status, '403 FORBIDDEN', res.status) # PUT with not JSON data res = self.app.put(url, data=None) err = json.loads(res.data) assert res.status_code == 415, err assert err['status'] == 'failed', err assert err['target'] == 'result', err assert err['action'] == 'PUT', err assert err['exception_cls'] == 'ValueError', err # PUT with not allowed args res = self.app.put(url + "&foo=bar", data=json.dumps(data)) err = json.loads(res.data) assert res.status_code == 415, err assert err['status'] == 'failed', err assert err['target'] == 'result', err assert err['action'] == 'PUT', err assert err['exception_cls'] == 'AttributeError', err # PUT with fake data data['wrongfield'] = 13 res = self.app.put(url, data=json.dumps(data)) err = json.loads(res.data) assert res.status_code == 415, err assert err['status'] == 'failed', err assert err['target'] == 'result', err assert err['action'] == 'PUT', err assert err['exception_cls'] == 'TypeError', err @with_context def test_result_delete(self): """Test API result delete""" admin = UserFactory.create() user = UserFactory.create() non_owner = UserFactory.create() result = self.create_result(owner=user) ## anonymous res = self.app.delete('/api/result/%s' % result.id) error_msg = 'Anonymous should not be allowed to update' assert_equal(res.status, '401 UNAUTHORIZED', error_msg) ### real user but not allowed as not owner! url = '/api/result/%s?api_key=%s' % (result.id, non_owner.api_key) res = self.app.delete(url) error_msg = 'Should not be able to update tasks of others' assert_equal(res.status, '403 FORBIDDEN', error_msg) #### real user # DELETE with not allowed args res = self.app.delete(url + "&foo=bar") err = json.loads(res.data) assert res.status_code == 415, err assert err['status'] == 'failed', err assert err['target'] == 'result', err assert err['action'] == 'DELETE', err assert err['exception_cls'] == 'AttributeError', err # DELETE returns 403 url = '/api/result/%s?api_key=%s' % (result.id, user.api_key) res = self.app.delete(url) assert_equal(res.status, '403 FORBIDDEN', res.data) #### root user url = '/api/result/%s?api_key=%s' % (result.id, admin.api_key) res = self.app.delete(url) assert_equal(res.status, '403 FORBIDDEN', res.data) @with_context def test_get_last_version(self): """Test API result returns always latest version.""" result = self.create_result() project = project_repo.get(result.project_id) task = task_repo.get_task(result.task_id) task.n_answers = 2 TaskRunFactory.create(task=task, project=project) result = result_repo.get_by(project_id=project.id) assert result.last_version is True, result.last_version result_id = result.id results = result_repo.filter_by(project_id=project.id, last_version=False) assert len(results) == 2, len(results) for r in results: if r.id == result_id: assert r.last_version is True, r.last_version else: assert r.last_version is False, r.last_version
class TestBaseAnalyst(Test): def setUp(self): super(TestBaseAnalyst, self).setUp() BaseAnalyst.__abstractmethods__ = frozenset() self.ctx = ContextFixtures() self.base_analyst = BaseAnalyst() self.project_repo = ProjectRepository(db) self.result_repo = ResultRepository(db) self.task_repo = TaskRepository(db) assert_dict_equal.__self__.maxDiff = None assert_equal.__self__.maxDiff = None @with_context @patch("pybossa_lc.analysis.base.BaseAnalyst.analyse") def test_analyse_all(self, mock_analyse): """Test that all results are analysed.""" project = ProjectFactory() tasks = TaskFactory.create_batch(2, project=project, n_answers=1) for task in tasks: TaskRunFactory.create(task=task) result = self.result_repo.get_by(task_id=tasks[0].id) result.info = dict(annotations=[{}]) self.result_repo.update(result) self.base_analyst.analyse_all(project.id) expected = [call(t.id, analyse_full=True) for t in tasks] assert_equal(mock_analyse.call_args_list, expected) @with_context @patch("pybossa_lc.analysis.base.BaseAnalyst.analyse") def test_analyse_empty(self, mock_analyse): """Test that empty results are analysed.""" project = ProjectFactory() tasks = TaskFactory.create_batch(2, project=project, n_answers=1) for task in tasks: TaskRunFactory.create(task=task) result = self.result_repo.get_by(task_id=tasks[0].id) result.info = dict(annotations=[{}]) self.result_repo.update(result) all_results = self.result_repo.filter_by(project_id=project.id) self.base_analyst.analyse_empty(project.id) expected = [call(r.task_id) for r in all_results if not r.info] assert_equal(mock_analyse.call_args_list, expected) @with_context def test_key_dropped(self): """Test the correct keys are dropped.""" data = [{'foo': None, 'bar': None}] df = pandas.DataFrame(data, range(len(data))) excluded = ['foo'] df = self.base_analyst.drop_keys(df, excluded) assert_not_in('foo', df.keys()) assert_in('bar', df.keys()) @with_context def test_empty_rows_dropped(self): """Test empty rows are dropped.""" data = [{'foo': 'bar'}, {'foo': None}] df = pandas.DataFrame(data, range(len(data))) df = self.base_analyst.drop_empty_rows(df) assert_equals(df['foo'].tolist(), ['bar']) @with_context def test_partial_rows_not_dropped(self): """Test partial rows are not dropped.""" data = [{'foo': 'bar', 'baz': None}] df = pandas.DataFrame(data, range(len(data))) df = self.base_analyst.drop_empty_rows(df) expected = {'foo': {0: 'bar'}, 'baz': {0: None}} assert_dict_equal(df.to_dict(), expected) @with_context def test_match_fails_when_percentage_not_met(self): """Test False is returned when min answers not met.""" data = [{'foo': 'bar', 'baz': None}] df = pandas.DataFrame(data, range(len(data))) min_answers = 2 has_matches = self.base_analyst.has_n_matches(min_answers, df) assert_equal(has_matches, False) @with_context def test_match_fails_when_nan_cols(self): """Test False is returned when NaN columns only.""" data = [{'foo': None}] df = pandas.DataFrame(data, range(len(data))) df = df.replace('', numpy.nan) min_answers = 2 has_matches = self.base_analyst.has_n_matches(min_answers, df) assert_equal(has_matches, False) @with_context def test_match_succeeds_when_percentage_met(self): """Test True returned when match percentage met.""" data = [{'foo': 'bar'}, {'foo': 'bar'}] df = pandas.DataFrame(data, range(len(data))) min_answers = 2 has_matches = self.base_analyst.has_n_matches(min_answers, df) assert_equal(has_matches, True) @with_context def test_get_dataframe_with_dict(self): """Test the task run dataframe with a dict as the info.""" info = {'foo': 'bar'} n_task_runs = 2 task = TaskFactory() taskruns = TaskRunFactory.create_batch(n_task_runs, task=task, info=info) df = self.base_analyst.get_task_run_df(task, taskruns) assert_equal(df['foo'].tolist(), [info['foo']] * n_task_runs) assert_equal(df['info'].tolist(), [info] * n_task_runs) @with_context def test_get_dataframe_with_list(self): """Test the task run dataframe with a list as the info.""" info = [{'foo': 'bar'}, {'baz': 'qux'}] n_task_runs = 2 task = TaskFactory() taskruns = TaskRunFactory.create_batch(n_task_runs, task=task, info=info) df = self.base_analyst.get_task_run_df(task, taskruns) assert_equal(df['info'].tolist(), [info] * n_task_runs) @with_context def test_protected_keys_prefixed_when_exploded(self): """Test that protected info keys are prefixed.""" info = {'foo': 'bar', 'info': 'baz'} task = TaskFactory() taskrun = TaskRunFactory.create(task=task, info=info) df = self.base_analyst.get_task_run_df(task, [taskrun]) assert_equal(df['_info'].tolist(), [info['info']]) @with_context def test_user_ids_in_task_run_dataframe(self): """Test that user IDs are included in the task run dataframe.""" task = TaskFactory() taskruns = TaskRunFactory.create_batch(2, task=task) df = self.base_analyst.get_task_run_df(task, taskruns) assert_equal(df['user_id'].tolist(), [tr.user_id for tr in taskruns]) def test_titlecase_normalisation(self): """Test titlecase normalisation.""" rules = dict(case='title') norm = self.base_analyst.normalise_transcription('Some words', rules) assert_equal(norm, 'Some Words') def test_lowercase_normalisation(self): """Test lowercase normalisation.""" rules = dict(case='lower') norm = self.base_analyst.normalise_transcription('Some words', rules) assert_equal(norm, 'some words') def test_uppercase_normalisation(self): """Test uppercase normalisation.""" rules = dict(case='upper') norm = self.base_analyst.normalise_transcription('Some words', rules) assert_equal(norm, 'SOME WORDS') def test_whitespace_normalisation(self): """Test whitespace normalisation.""" rules = dict(whitespace='normalise') norm = self.base_analyst.normalise_transcription(' Two Words', rules) assert_equal(norm, 'Two Words') def test_whitespace_replace_underscore(self): """Test replacing whitespace with underscore normalisation.""" rules = dict(whitespace='underscore') norm = self.base_analyst.normalise_transcription(' Two Words', rules) assert_equal(norm, 'Two_Words') def test_whitespace_replace_full_stop(self): """Test replacing whitespace with full stop normalisation.""" rules = dict(whitespace='full_stop') norm = self.base_analyst.normalise_transcription(' Two Words', rules) assert_equal(norm, 'Two.Words') def test_trim_punctuation_normalisation(self): """Test trim punctuation normalisation.""" rules = dict(trim_punctuation=True) norm = self.base_analyst.normalise_transcription(':Oh, a word.', rules) assert_equal(norm, 'Oh, a word') def test_date_not_normalised_if_rule_inactive(self): """Test date conversion not applied of rule not activate.""" norm = self.base_analyst.normalise_transcription('foo', {}) assert_equal(norm, 'foo') def test_date_conversion_with_slash(self): """Test date conversion with slash seperators.""" rules = dict(date_format=True, dayfirst=True) norm = self.base_analyst.normalise_transcription('19/11/1984', rules) assert_equal(norm, '1984-11-19') def test_date_conversion_with_hyphen(self): """Test date conversion with hyphen seperator.""" rules = dict(date_format=True, dayfirst=True) norm = self.base_analyst.normalise_transcription('19-11-1984', rules) assert_equal(norm, '1984-11-19') def test_date_conversion_with_no_seperator(self): """Test date conversion with no seperator.""" rules = dict(date_format=True, dayfirst=True) norm = self.base_analyst.normalise_transcription('19111984', rules) assert_equal(norm, '') def test_date_conversion_with_no_year_and_year_last(self): """Test date conversion with no year and year last.""" rules = dict(date_format=True, dayfirst=True) norm = self.base_analyst.normalise_transcription('19/11', rules) assert_equal(norm, '-11-19') def test_date_conversion_with_no_year_and_year_first(self): """Test date conversion with no year and year first.""" rules = dict(date_format=True, yearfirst=True) norm = self.base_analyst.normalise_transcription('11/19', rules) assert_equal(norm, '-11-19') def test_date_conversion_with_invalid_string(self): """Test date conversion with invalid string.""" rules = dict(date_format=True, dayfirst=True) norm = self.base_analyst.normalise_transcription('No date', rules) assert_equal(norm, '') def test_date_conversion_with_zero(self): """Test date conversion with zero.""" rules = dict(date_format=True, dayfirst=True) norm = self.base_analyst.normalise_transcription('0', rules) assert_equal(norm, '') def test_date_conversion_with_non_zero_integer(self): """Test date conversion with non-zero integer.""" rules = dict(date_format=True, dayfirst=True) norm = self.base_analyst.normalise_transcription('1', rules) assert_equal(norm, '') def test_date_conversion_with_trailing_punctuation(self): """Test date conversion with trailing punctuation.""" rules = dict(date_format=True, dayfirst=True) norm = self.base_analyst.normalise_transcription('19/11/', rules) assert_equal(norm, '-11-19') def test_date_conversion_with_trailing_whitespace(self): """Test date conversion with trailing whitespace.""" rules = dict(date_format=True, dayfirst=True) norm = self.base_analyst.normalise_transcription('19/11/1984 ', rules) assert_equal(norm, '1984-11-19') @with_context def test_n_answers_increased_when_task_complete(self): """Test n answers required for a task is updated.""" n_original_answers = 1 task = TaskFactory.create(n_answers=n_original_answers) TaskRunFactory.create(task=task) self.base_analyst.update_n_answers_required(task, False) assert_equal(task.n_answers, n_original_answers + 1) assert_equal(task.state, 'ongoing') @with_context def test_n_answers_not_increased_when_still_task_runs(self): """Test n answers not updated when task runs still required.""" n_original_answers = 2 task = TaskFactory.create(n_answers=n_original_answers) TaskRunFactory.create(task=task) self.base_analyst.update_n_answers_required(task, False) assert_equal(task.n_answers, n_original_answers) assert_equal(task.state, 'ongoing') @with_context def test_n_answers_not_increased_when_max_answers_reached(self): """Test n answers not updated when max answers reached.""" n_answers = 3 task = TaskFactory.create(n_answers=n_answers) TaskRunFactory.create_batch(n_answers, task=task) self.base_analyst.update_n_answers_required(task, False, max_answers=n_answers) assert_equal(task.n_answers, n_answers) assert_equal(task.state, 'completed') @with_context def test_n_answers_reduced_when_task_complete(self): """Test n answers reduced to number of task runs when task complete.""" n_answers = 3 task = TaskFactory.create(n_answers=n_answers) TaskRunFactory.create_batch(n_answers - 1, task=task) self.base_analyst.update_n_answers_required(task, True, max_answers=n_answers) assert_equal(task.n_answers, n_answers - 1) assert_equal(task.state, 'completed') def test_overlap_ratio_is_1_with_equal_rects(self): """Test for an overlap ratio of 1.""" rect = {'x': 100, 'y': 100, 'w': 100, 'h': 100} overlap = self.base_analyst.get_overlap_ratio(rect, rect) assert_equal(overlap, 1) def test_overlap_ratio_is_0_with_adjacent_rects(self): """Test for an overlap ratio of 0.""" r1 = {'x': 100, 'y': 100, 'w': 100, 'h': 100} r2 = {'x': 100, 'y': 201, 'w': 100, 'h': 100} overlap = self.base_analyst.get_overlap_ratio(r1, r2) assert_equal(overlap, 0) def test_overlap_ratio_with_partially_overlapping_rects(self): """Test for an overlap ratio of 0.33.""" r1 = {'x': 100, 'y': 100, 'w': 100, 'h': 100} r2 = {'x': 150, 'y': 100, 'w': 100, 'h': 100} overlap = self.base_analyst.get_overlap_ratio(r1, r2) assert_equal('{:.2f}'.format(overlap), '0.33') def test_overlap_ratio_where_union_is_zero(self): """Test for an overlap ratio where the union is zero.""" r1 = {'x': 0, 'y': 0, 'w': 100, 'h': 100} r2 = {'x': 101, 'y': 0, 'w': 100, 'h': 100} overlap = self.base_analyst.get_overlap_ratio(r1, r2) assert_equal(overlap, 0) def test_rect_from_selection(self): """Test that we get the correct rect.""" coords = dict(x=400, y=200, w=100, h=150) coords_str = '{0},{1},{2},{3}'.format(coords['x'], coords['y'], coords['w'], coords['h']) fake_anno = { 'target': { 'selector': { 'value': '?xywh={}'.format(coords_str) } } } rect = self.base_analyst.get_rect_from_selection_anno(fake_anno) assert_dict_equal(rect, coords) def test_rect_from_selection_with_floats(self): """Test that we get the correct rect with rounded coordinates.""" coords = dict(x=400.001, y=200.499, w=100.501, h=150.999) coords_str = '{0},{1},{2},{3}'.format(coords['x'], coords['y'], coords['w'], coords['h']) fake_anno = { 'target': { 'selector': { 'value': '?xywh={}'.format(coords_str) } } } rect = self.base_analyst.get_rect_from_selection_anno(fake_anno) assert_dict_equal(rect, {'x': 400, 'y': 200, 'w': 101, 'h': 151}) @with_context def test_get_project_template(self): """Test that the correct template is returned.""" category = CategoryFactory() tmpl_fixtures = TemplateFixtures(category) tmpl1 = tmpl_fixtures.create() tmpl2 = tmpl_fixtures.create() fake_templates = [tmpl1, tmpl2] category.info = dict(templates=fake_templates) self.project_repo.update_category(category) project_info = dict(template_id=tmpl1['id']) project = ProjectFactory(category=category, info=project_info) ret_tmpl = self.base_analyst.get_project_template(project) assert_equal(ret_tmpl, tmpl1) @with_context @raises(ValueError) def test_get_invalid_project_template(self): """Test that getting an invalid template throws an error.""" fake_templates = [{'id': 'foo'}] user_info = dict(templates=fake_templates) project_info = dict(template_id='bar') UserFactory.create(info=user_info) project = ProjectFactory(info=project_info) self.base_analyst.get_project_template(project) @with_context @raises(ValueError) def test_get_non_existant_project_template(self): """Test that getting a non-existant template throws an error.""" project = ProjectFactory() self.base_analyst.get_project_template(project) def test_dataframe_keys_replaced(self): """Test that dataframe keys are replaced and columns merged.""" data = [{'foo': '你好', 'baz': 'qux'}, {'foo': 1, 'quux': 'qux'}] old_df = pandas.DataFrame(data, range(len(data))) new_df = self.base_analyst.replace_df_keys(old_df, quux='baz') assert_dict_equal(new_df.to_dict(), { 'foo': { 0: '你好', 1: 1 }, 'baz': { 0: 'qux', 1: 'qux' } }) @with_context @patch('pybossa_lc.analysis.base.send_mail') @patch('pybossa_lc.analysis.base.render_template') @patch('pybossa_lc.analysis.base.Queue.enqueue') def test_comment_annotations_emailed(self, mock_enqueue, mock_render, mock_send_mail): """Test that comment annotation emails are sent.""" mock_render.return_value = True comment = 'foo' creator = 'bar' target = 'example.com' fake_anno = { 'creator': { 'id': 'example.com/user1', 'type': 'Person', 'name': creator, 'nickname': 'nick' }, 'body': { 'type': 'TextualBody', 'purpose': 'commenting', 'value': comment, 'format': 'text/plain' } } task = self.ctx.create_task(1, target) json_anno = json.dumps(fake_anno, indent=2, sort_keys=True) self.base_analyst.email_comment_anno(task, fake_anno) expected_render_args = [ call('/account/email/new_comment_anno.md', annotation=json_anno, creator=creator, comment=comment, raw_image=None, link=None), call('/account/email/new_comment_anno.html', annotation=json_anno, creator=creator, comment=comment, raw_image=None, link=None) ] assert_equal(mock_render.call_args_list, expected_render_args) expected_msg = { 'body': True, 'html': True, 'subject': 'New Comment Annotation', 'recipients': flask_app.config.get('ADMINS') } mock_enqueue.assert_called_once_with(mock_send_mail, expected_msg) @with_context @patch('pybossa_lc.model.base.wa_client') def test_modified_results_not_updated(self, mock_client): """Test results are not updated if an Annotation has been modified.""" task = self.ctx.create_task(1) TaskRunFactory(task=task) result = self.result_repo.get_by(task_id=task.id) self.base_analyst.analyse(result.id) mock_client.search_annotations.return_value = [{ 'modified': 'fake-time' }] assert_equal(mock_client.create_annotation.called, False) @with_context @patch('pybossa_lc.model.base.wa_client') def test_parent_results_not_updated(self, mock_client): """Test results are not updated if an Annotation has children.""" task = self.ctx.create_task(1) TaskRunFactory(task=task) result = self.result_repo.get_by(task_id=task.id) result.info = dict(has_children=True) self.result_repo.update(result) self.base_analyst.analyse(result.id) assert_equal(mock_client.create_annotation.called, False) @with_context @patch('pybossa_lc.model.base.wa_client') def test_result_with_child_not_updated(self, mock_client): """Test that a result is not updated when it has a child.""" task = self.ctx.create_task(1) TaskRunFactory(task=task) result = self.result_repo.get_by(task_id=task.id) info = dict(annotations='foo', has_children=True) result.info = info self.result_repo.update(result) self.base_analyst.analyse(result.id) assert_equal(result.info, info) @with_context def test_analysis_exception_if_no_annotation_collection(self): """Test that AnnotationCollection must be setup.""" task = self.ctx.create_task(1, 'example.com', anno_collection=None) TaskRunFactory.create(task=task) result = self.result_repo.filter_by(project_id=task.project_id)[0] assert_raises(AnalysisException, self.base_analyst.analyse, result.id)
class TestResultRepository(Test): def setUp(self): super(TestResultRepository, self).setUp() self.result_repo = ResultRepository(db) def create_result(self, n_answers=1, filter_by=False): task = TaskFactory.create(n_answers=n_answers) TaskRunFactory.create(task=task) if filter_by: return self.result_repo.filter_by(project_id=1) else: return self.result_repo.get_by(project_id=1) def test_get_return_none_if_no_result(self): """Test get method returns None if there is no result with the specified id""" result = self.result_repo.get(2) assert result is None, result def test_get_returns_result(self): """Test get method returns a result if exists""" n_answers = 1 task = TaskFactory.create(n_answers=n_answers) task_run = TaskRunFactory.create(task=task) result = self.result_repo.filter_by(project_id=1) err_msg = "There should be a result" assert len(result) == 1, err_msg result = result[0] assert result.project_id == 1, err_msg assert result.task_id == task.id, err_msg assert len(result.task_run_ids) == n_answers, err_msg err_msg = "The task_run id is missing in the results array" for tr_id in result.task_run_ids: assert tr_id == task_run.id, err_msg def test_get_by_returns_result(self): """Test get_by method returns a result if exists""" n_answers = 1 task = TaskFactory.create(n_answers=n_answers) task_run = TaskRunFactory.create(task=task) result = self.result_repo.get_by(project_id=1) err_msg = "There should be a result" assert result.project_id == 1, err_msg assert result.task_id == task.id, err_msg assert len(result.task_run_ids) == n_answers, err_msg err_msg = "The task_run id is missing in the results array" for tr_id in result.task_run_ids: assert tr_id == task_run.id, err_msg def test_get_returns_result_after_increasig_redundancy(self): """Test get method returns a result if after increasing redundancy""" n_answers = 1 task = TaskFactory.create(n_answers=n_answers) task_run = TaskRunFactory.create(task=task) result = self.result_repo.filter_by(project_id=1) err_msg = "There should be a result" assert len(result) == 1, err_msg result = result[0] assert result.project_id == 1, err_msg assert result.task_id == task.id, err_msg assert len(result.task_run_ids) == n_answers, err_msg err_msg = "The task_run id is missing in the results array" for tr_id in result.task_run_ids: assert tr_id == task_run.id, err_msg # Increase redundancy tmp = task_repo.get_task(task.id) tmp.n_answers = 2 task_repo.update(task) err_msg = "There should be only one result" results = result_repo.filter_by(project_id=1) assert len(results) == 1, err_msg task_run_2 = TaskRunFactory.create(task=task) err_msg = "There should be 1 results" results = result_repo.filter_by(project_id=1) assert len(results) == 1, err_msg err_msg = "There should be 2 results" results = result_repo.filter_by(project_id=1, last_version=False) assert len(results) == 2, err_msg assert results[1].project_id == 1, err_msg assert results[1].task_id == task.id, err_msg err_msg = "First result should have only one task run ID" assert len(results[0].task_run_ids) == 1, err_msg err_msg = "Second result should have only two task run IDs" assert len(results[1].task_run_ids) == 2, err_msg err_msg = "The task_run id is missing in the results array" for tr_id in results[1].task_run_ids: assert tr_id in [task_run.id, task_run_2.id], err_msg def test_get_returns_no_result(self): """Test get method does not return a result if task not completed""" n_answers = 3 task = TaskFactory.create(n_answers=n_answers) TaskRunFactory.create(task=task) result = self.result_repo.filter_by(project_id=1) err_msg = "There should not be a result" assert len(result) == 0, err_msg def test_fulltext_search_result(self): """Test fulltext search in JSON info works.""" result = self.create_result() text = 'something word you me bar' data = {'foo': text} result.info = data self.result_repo.update(result) info = 'foo::word' res = self.result_repo.filter_by(info=info, fulltextsearch='1') assert len(res) == 1, len(res) assert res[0].info['foo'] == text, res[0] res = self.result_repo.filter_by(info=info) assert len(res) == 0, len(res) def test_fulltext_search_result_01(self): """Test fulltext search in JSON info works.""" result = self.create_result() text = 'something word you me bar' data = {'foo': text, 'bar': 'foo'} result.info = data self.result_repo.update(result) info = 'foo::word&bar|bar::foo' res = self.result_repo.filter_by(info=info, fulltextsearch='1') assert len(res) == 1, len(res) assert res[0].info['foo'] == text, res[0] def test_info_json_search_result(self): """Test search in JSON info works.""" result = self.create_result() text = 'bar' data = {'foo': text} result.info = data self.result_repo.update(result) info = 'foo::bar' res = self.result_repo.filter_by(info=info) assert len(res) == 1, len(res) assert res[0].info['foo'] == text, res[0] def test_update(self): """Test update persists the changes made to the result""" result = self.create_result() result.info = dict(new='value') self.result_repo.update(result) updated_result = self.result_repo.get(result.id) assert updated_result.info['new'] == 'value', updated_result def test_update_fails_if_integrity_error(self): """Test update raises a DBIntegrityError if the instance to be updated lacks a required value""" result = self.create_result() result.project_id = None assert_raises(DBIntegrityError, self.result_repo.update, result) def test_update_only_updates_results(self): """Test update raises a WrongObjectError when an object which is not a Result instance is updated""" bad_object = dict() assert_raises(WrongObjectError, self.result_repo.update, bad_object)
class TestResultAuthorization(Test): mock_anonymous = mock_current_user() mock_authenticated = mock_current_user(anonymous=False, admin=False, id=2) mock_pro = mock_current_user(anonymous=False, admin=False, id=2, pro=True) mock_admin = mock_current_user(anonymous=False, admin=True, id=1) mock_owner = mock_current_user(anonymous=False, admin=False, id=1) def setUp(self): super(TestResultAuthorization, self).setUp() self.result_repo = ResultRepository(db) def create_result(self, n_answers=1, filter_by=False): task = TaskFactory.create(n_answers=n_answers) TaskRunFactory.create(task=task) if filter_by: return self.result_repo.filter_by(project_id=1) else: return self.result_repo.get_by(project_id=1) @patch('pybossa.auth.current_user', new=mock_anonymous) def test_anonymous_user_can_read_result(self): """Test anonymous users can read results""" result = self.create_result() assert ensure_authorized_to('read', result) @patch('pybossa.auth.current_user', new=mock_authenticated) def test_auth_user_can_read_result(self): """Test auth users can read results""" result = self.create_result() assert ensure_authorized_to('read', result) @patch('pybossa.auth.current_user', new=mock_admin) def test_admin_user_can_read_result(self): """Test admin users can read results""" result = self.create_result() assert ensure_authorized_to('read', result) @patch('pybossa.auth.current_user', new=mock_anonymous) def test_anonymous_user_cannot_save_results(self): """Test anonymous users cannot save results of a specific project""" result = Result() assert_raises(Unauthorized, ensure_authorized_to, 'create', result) @patch('pybossa.auth.current_user', new=mock_authenticated) def test_authenticated_user_cannot_save_results(self): """Test authenticated users cannot save results of a specific project""" result = Result() assert_raises(Forbidden, ensure_authorized_to, 'create', result) @patch('pybossa.auth.current_user', new=mock_admin) def test_admin_user_cannot_save_results(self): """Test admin users cannot save results of a specific project""" result = Result() assert_raises(Forbidden, ensure_authorized_to, 'create', result) @patch('pybossa.auth.current_user', new=mock_anonymous) def test_anonymous_user_cannot_delete_results(self): """Test anonymous users cannot delete results of a specific project""" result = Result() assert_raises(Unauthorized, ensure_authorized_to, 'delete', result) @patch('pybossa.auth.current_user', new=mock_authenticated) def test_authenticated_user_cannot_delete_results(self): """Test authenticated users cannot delete results of a specific project""" result = Result() assert_raises(Forbidden, ensure_authorized_to, 'delete', result) @patch('pybossa.auth.current_user', new=mock_admin) def test_admin_user_cannot_delete_results(self): """Test admin users cannot delete results of a specific project""" result = Result() assert_raises(Forbidden, ensure_authorized_to, 'delete', result) @patch('pybossa.auth.current_user', new=mock_anonymous) def test_anonymous_user_cannot_update_results(self): """Test anonymous users cannot update results of a specific project""" result = self.create_result() assert_raises(Unauthorized, ensure_authorized_to, 'update', result) @patch('pybossa.auth.current_user', new=mock_authenticated) def test_auth_user_cannot_update_results(self): """Test auth users but not owner cannot update results of a specific project""" result = self.create_result() assert_raises(Forbidden, ensure_authorized_to, 'update', result) @patch('pybossa.auth.current_user', new=mock_owner) def test_auth_owner_can_update_results(self): """Test auth owner can update results of a specific project""" result = self.create_result() result.info = dict(new='value') assert ensure_authorized_to('update', result) updated_result = self.result_repo.get_by(id=result.id) err_msg = "The result has not been updated" assert updated_result.info['new'] == 'value', err_msg
from default import db, with_context from nose.tools import assert_equal from test_api import TestAPI from mock import patch, call from factories import ProjectFactory, TaskFactory, TaskRunFactory, UserFactory from factories import AnonymousTaskRunFactory, ExternalUidTaskRunFactory from pybossa.repositories import ProjectRepository from pybossa.repositories import TaskRepository from pybossa.repositories import ResultRepository from pybossa.model.counter import Counter project_repo = ProjectRepository(db) task_repo = TaskRepository(db) result_repo = ResultRepository(db) class TestTaskAPI(TestAPI): def create_result(self, n_results=1, n_answers=1, owner=None, filter_by=False): if owner: owner = owner else: admin, owner, user = UserFactory.create_batch(3) project = ProjectFactory.create(owner=owner) tasks = [] for i in range(n_results):
class TestResultRepository(Test): def setUp(self): super(TestResultRepository, self).setUp() self.result_repo = ResultRepository(db) def create_result(self, n_answers=1, filter_by=False): task = TaskFactory.create(n_answers=n_answers) TaskRunFactory.create(task=task) if filter_by: return self.result_repo.filter_by(project_id=1) else: return self.result_repo.get_by(project_id=1) def test_get_return_none_if_no_result(self): """Test get method returns None if there is no result with the specified id""" result = self.result_repo.get(2) assert result is None, result def test_get_returns_result(self): """Test get method returns a result if exists""" n_answers = 1 task = TaskFactory.create(n_answers=n_answers) task_run = TaskRunFactory.create(task=task) result = self.result_repo.filter_by(project_id=1) err_msg = "There should be a result" assert len(result) == 1, err_msg result = result[0] assert result.project_id == 1, err_msg assert result.task_id == task.id, err_msg assert len(result.task_run_ids) == n_answers, err_msg err_msg = "The task_run id is missing in the results array" for tr_id in result.task_run_ids: assert tr_id == task_run.id, err_msg def test_get_by_returns_result(self): """Test get_by method returns a result if exists""" n_answers = 1 task = TaskFactory.create(n_answers=n_answers) task_run = TaskRunFactory.create(task=task) result = self.result_repo.get_by(project_id=1) err_msg = "There should be a result" assert result.project_id == 1, err_msg assert result.task_id == task.id, err_msg assert len(result.task_run_ids) == n_answers, err_msg err_msg = "The task_run id is missing in the results array" for tr_id in result.task_run_ids: assert tr_id == task_run.id, err_msg def test_get_returns_result_after_increasig_redundancy(self): """Test get method returns a result if after increasing redundancy""" n_answers = 1 task = TaskFactory.create(n_answers=n_answers) task_run = TaskRunFactory.create(task=task) result = self.result_repo.filter_by(project_id=1) err_msg = "There should be a result" assert len(result) == 1, err_msg result = result[0] assert result.project_id == 1, err_msg assert result.task_id == task.id, err_msg assert len(result.task_run_ids) == n_answers, err_msg err_msg = "The task_run id is missing in the results array" for tr_id in result.task_run_ids: assert tr_id == task_run.id, err_msg # Increase redundancy tmp = task_repo.get_task(task.id) tmp.n_answers = 2 task_repo.update(task) err_msg = "There should be only one result" results = result_repo.filter_by(project_id=1) assert len(results) == 1, err_msg task_run_2 = TaskRunFactory.create(task=task) err_msg = "There should be 1 results" results = result_repo.filter_by(project_id=1) assert len(results) == 1, err_msg err_msg = "There should be 2 results" results = result_repo.filter_by(project_id=1, last_version=False) assert len(results) == 2, err_msg assert results[1].project_id == 1, err_msg assert results[1].task_id == task.id, err_msg err_msg = "First result should have only one task run ID" assert len(results[0].task_run_ids) == 1, err_msg err_msg = "Second result should have only two task run IDs" assert len(results[1].task_run_ids) == 2, err_msg err_msg = "The task_run id is missing in the results array" for tr_id in results[1].task_run_ids: assert tr_id in [task_run.id, task_run_2.id], err_msg def test_get_returns_no_result(self): """Test get method does not return a result if task not completed""" n_answers = 3 task = TaskFactory.create(n_answers=n_answers) TaskRunFactory.create(task=task) result = self.result_repo.filter_by(project_id=1) err_msg = "There should not be a result" assert len(result) == 0, err_msg def test_update(self): """Test update persists the changes made to the result""" result = self.create_result() result.info = dict(new='value') self.result_repo.update(result) updated_result = self.result_repo.get(result.id) assert updated_result.info['new'] == 'value', updated_result def test_update_fails_if_integrity_error(self): """Test update raises a DBIntegrityError if the instance to be updated lacks a required value""" result = self.create_result() result.project_id = None assert_raises(DBIntegrityError, self.result_repo.update, result) def test_update_only_updates_results(self): """Test update raises a WrongObjectError when an object which is not a Result instance is updated""" bad_object = dict() assert_raises(WrongObjectError, self.result_repo.update, bad_object)