diff --git a/pybossa/api/__init__.py b/pybossa/api/__init__.py index 07d612ff5..e8e5b3c82 100644 --- a/pybossa/api/__init__.py +++ b/pybossa/api/__init__.py @@ -919,3 +919,21 @@ def large_language_model(model_name): response = {"Model: ": model_name, "predictions: ": predictions} return Response(json.dumps(response), status=r.status_code, mimetype="application/json") + + +@jsonpify +@blueprint.route('/project//gold_annotations') +@ratelimit(limit=ratelimits.get('LIMIT'), per=ratelimits.get('PER')) +def get_gold_annotations(project_id): + """Obtain all gold tasks under a given project along with consensus built on their annotations + """ + + if not current_user.is_authenticated: + return abort(401) + + project = project_repo.get(project_id) + if not current_user.admin and not (current_user.subadmin and current_user.id in project.owners_ids): + return abort(403) + + tasks = project_repo.get_gold_annotations(project_id) + return Response(json.dumps(tasks), status=200, mimetype="application/json") diff --git a/pybossa/repositories/project_repository.py b/pybossa/repositories/project_repository.py index 04d7bc58f..e67d7e63e 100644 --- a/pybossa/repositories/project_repository.py +++ b/pybossa/repositories/project_repository.py @@ -399,3 +399,20 @@ def get_projects_report(self, base_url): 'kpi', 'product', 'subproduct', 'input_data_classification', 'output_data_classification', 'data_access']] return data + + def get_gold_annotations(self, project_id): + sql = text( + ''' + SELECT + task.id AS task_id, task.gold_answers, + result.info AS consensus_annotations + FROM task INNER JOIN result + ON task.id = result.task_id + WHERE task.project_id=:project_id + AND task.calibration=1 + AND result.last_version=True + ORDER BY task_id; + ''' + ) + rows = self.db.session.execute(sql, dict(project_id=project_id)).fetchall() + return [dict(row) for row in rows] diff --git a/test/test_web.py b/test/test_web.py index f556c56a1..cfd39c87d 100644 --- a/test/test_web.py +++ b/test/test_web.py @@ -10079,6 +10079,49 @@ def test_browse_task_display_info_columns_sort_priority(self): assert "class=\" sort-asc sortable\" data-sort=\"completed_by\"" not in str(res.data), "Unexpected sorted column (sort-asc) indicator on Completed By." assert "class=\" sort-asc sortable\" data-sort=\"priority\"" not in str(res.data), "Missing sorted column indicator (sort-asc) on Priority." + @with_context + def test_gold_annotations_anonymous_access_fails(self): + """Test gold_annotations without login""" + res = self.app_get_json("/api/project/test_proj/gold_annotations") + data = json.loads(res.data) + assert data.get('status_code') == 401, data + + @with_context + def test_gold_annotations_regular_admin_subadmin_users(self): + """Test gold_annotations returns valid response based on user types""" + admin = UserFactory.create() + self.signin_user(admin) + project = ProjectFactory.create(owner=admin) + + regular_user = UserFactory.create(id=999, subadmin=False, admin=False, name="reguser") + regular_user.set_password('1234') + user_repo.save(regular_user) + + project.owners_ids.append(regular_user.id) + project_repo.save(project) + + subadmin_user = UserFactory.create(id=1999, subadmin=True, admin=False, name="subuser") + subadmin_user.set_password('1234') + user_repo.save(subadmin_user) + + # regular user forbidden + self.signin(email=regular_user.email_addr, password='1234') + res = self.app_get_json(f"/api/project/{project.id}/gold_annotations") + data = json.loads(res.data) + assert data.get('status_code') == 403, data + + # subadmin user forbidden as user is not project coowner + self.signin(email=subadmin_user.email_addr, password='1234') + res = self.app_get_json(f"/api/project/{project.id}/gold_annotations") + data = json.loads(res.data) + assert data.get('status_code') == 403, data + + project.owners_ids.append(subadmin_user.id) + project_repo.save(project) + res = self.app_get_json(f"/api/project/{project.id}/gold_annotations") + assert res.status_code == 200, data + + class TestWebUserMetadataUpdate(web.Helper): original = {