Skip to content

Commit

Permalink
Merge b047a86 into dcc65fa
Browse files Browse the repository at this point in the history
  • Loading branch information
kbecker42 committed Jun 18, 2024
2 parents dcc65fa + b047a86 commit ef50b62
Show file tree
Hide file tree
Showing 6 changed files with 258 additions and 14 deletions.
2 changes: 2 additions & 0 deletions pybossa/api/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@
from pybossa.jobs import send_mail
from pybossa.api.project_by_name import ProjectByNameAPI, project_name_to_oid
from pybossa.api.project_details import ProjectDetailsAPI
from pybossa.api.project_locks import ProjectLocksAPI
from pybossa.api.pwd_manager import get_pwd_manager
from pybossa.data_access import data_access_levels
from pybossa.task_creator_helper import set_gold_answers
Expand Down Expand Up @@ -165,6 +166,7 @@ def register_api(view, endpoint, url, pk='id', pk_type='int'):
register_api(CompletedTaskRunAPI, 'api_completedtaskrun', '/completedtaskrun', pk='oid', pk_type='int')
register_api(ProjectByNameAPI, 'api_projectbyname', '/projectbyname', pk='key', pk_type='string')
register_api(ProjectDetailsAPI, 'api_projectdetails', '/projectdetails', pk='oid', pk_type='int')
register_api(ProjectLocksAPI, 'api_projectlocks', '/locks', pk='oid', pk_type='int')
register_api(PerformanceStatsAPI, 'api_performancestats', '/performancestats', pk='oid', pk_type='int')
register_api(BulkTasksAPI, 'api_bulktasks', '/bulktasks', pk='oid', pk_type='int')

Expand Down
73 changes: 73 additions & 0 deletions pybossa/api/project_locks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# -*- coding: utf8 -*-
# This file is part of PYBOSSA.
#
# Copyright (C) 2017 Scifabric LTD.
#
# PYBOSSA is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# PYBOSSA is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with PYBOSSA. If not, see <http://www.gnu.org/licenses/>.

import json
from flask import request, abort
from flask_login import current_user
from werkzeug.exceptions import BadRequest, Unauthorized, Forbidden
from pybossa.api.api_base import APIBase
from pybossa.model.project import Project
from pybossa.model import DomainObject
from pybossa.view.projects import project_by_shortname, get_locked_tasks

class ProjectLocksAPI(APIBase):
"""
Class for retreiving active locks in projects.
"""
__class__ = Project

def _filter_query(self, repo_info, limit, offset, orderby):
if (len(request.args.keys()) == 0 or
(len(request.args.keys()) == 1 and "api_key" in request.args.keys())):
return []
if (not current_user.is_authenticated or
(not current_user.admin and not current_user.subadmin)):
raise Unauthorized("User not authorized for request")

return APIBase._filter_query(self, repo_info, limit, offset, orderby)

def _create_json_response(self, query_result, oid):
if len(query_result) == 1 and query_result[0] is None:
raise abort(404)
items = []
for result in query_result:
try:
item = result
datum = self._create_dict_from_model(item)
items.append(datum)
except Exception: # pragma: no cover
raise
if oid is not None:
self._sign_item(items[0])
items = items[0]
return json.dumps(items)


def _select_attributes(self, data):
# Get the project.
project, owner, ps = project_by_shortname(data.get('short_name'))
task_id = ''

tmp = {}
tmp['id'] = data.get('id')
tmp['short_name'] = data.get('short_name')
tmp['created'] = data.get('created')
tmp['locks'] = get_locked_tasks(project, task_id)

return tmp
26 changes: 14 additions & 12 deletions pybossa/settings_upref_mdata.py.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ def upref_languages():
return langs


country_to_country_code = {
country_name_to_country_code = {
"Andorra": "AD",
"United Arab Emirates": "AE",
"Afghanistan": "AF",
Expand Down Expand Up @@ -313,24 +313,23 @@ country_to_country_code = {
"Zimbabwe": "ZW",
}

country_code_to_country_name = dict((reversed(item) for item in country_name_to_country_code.items()))

country_code_to_country = dict((reversed(item) for item in country_to_country_code.items()))

def get_country_code_by_country_name(country):
return country_name_to_country_code.get(country)

def get_country_code_by_country(country):
return country_to_country_code.get(country)
def get_country_name_by_country_code(country_code):
return country_code_to_country_name.get(country_code)

def upref_country_names():
return [(cn, cn) for cn in country_name_to_country_code.keys()]

def get_country_by_country_code(country_code):
return country_code_to_country.get(country_code)

def upref_country_codes():
return [(cc, cc) for cc in country_code_to_country_name.keys()]

def upref_locations():
cts = []
for cc, cn in country_code_to_country.items():
cts.append((cc,cc))
cts.append((cn, cn))
return sorted(cts)
return sorted(upref_country_names() + upref_country_codes())


def mdata_user_types():
Expand Down Expand Up @@ -381,10 +380,13 @@ def get_upref_mdata_choices():
upref_mdata_choices = dict(
languages=upref_languages(),
locations=upref_locations(),
country_codes=upref_country_codes(),
country_names=upref_country_names(),
timezones=mdata_timezones(),
user_types=mdata_user_types())
return upref_mdata_choices


def get_valid_user_preferences():
if not valid_user_preferences:
upref_mdata_choices = get_upref_mdata_choices()
Expand Down
1 change: 0 additions & 1 deletion pybossa/view/projects.py
Original file line number Diff line number Diff line change
Expand Up @@ -4264,7 +4264,6 @@ def get_locked_tasks(project, task_id=None):
@blueprint.route('/<short_name>/locks/', methods=['GET'], defaults={'task_id': ''})
@blueprint.route('/<short_name>/locks/<int:task_id>/', methods=['GET'])
@login_required
@csrf.exempt
@admin_or_subadmin_required
def locks(short_name, task_id):
"""View locked task(s) for a project."""
Expand Down
1 change: 0 additions & 1 deletion test/test_api/test_project_details_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -223,4 +223,3 @@ def test_project_details_multiple_params(self):
assert res.status_code == 200, data
assert data[0]['product'] == 'test_product', data
assert data[0]['subproduct'] == 'test_subproduct1', data

169 changes: 169 additions & 0 deletions test/test_api/test_project_locks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
# -*- coding: utf8 -*-
# This file is part of PYBOSSA.
#
# Copyright (C) 2015 Scifabric LTD.
#
# PYBOSSA is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# PYBOSSA is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with PYBOSSA. If not, see <http://www.gnu.org/licenses/>.
import json
from unittest.mock import patch, call

from nose.tools import assert_equal

from pybossa.model.project import Project
from test import db, with_context
from test.factories import (ProjectFactory, UserFactory)
from test.test_api import TestAPI



class TestProjectAPI(TestAPI):

def setUp(self):
super(TestProjectAPI, self).setUp()
db.session.query(Project).delete()

def setupProjects(self):
project = ProjectFactory.create(
updated='2015-01-01T14:37:30.642119',
short_name='test-app1',
info={
'total': 150,
'task_presenter': 'foo',
'data_classification': dict(input_data="L4 - public", output_data="L4 - public"),
'product' : 'test_product',
'subproduct': 'test_subproduct1'
})

projects = ProjectFactory.create_batch(5,
info={
'total': 150,
'task_presenter': 'foo',
'data_classification': dict(input_data="L4 - public", output_data="L4 - public"),
'product' : 'test_product',
'subproduct': 'test_subproduct2'
})
return project

@with_context
def test_project_locks_user_not_logged_in(self):
""" Test should return 401 if the user is not logged in"""
project = self.setupProjects()
project_id = str(project.id)

res = self.app.get('/api/locks/' + project_id)
err = json.loads(res.data)
assert res.status_code == 401, err
assert err['status'] == 'failed', err
assert err['target'] == 'project', err
assert err['exception_cls'] == 'Unauthorized', err
assert err['action'] == 'GET', err

@with_context
def test_project_locks_user_worker(self):
""" Test API should return 401 if user is worker"""
admin = UserFactory.create(admin=True)
worker = UserFactory.create(admin=False, subadmin=False)

project = self.setupProjects()
project_id = str(project.id)

res = self.app.get('/api/locks?id=' + project_id + '&api_key=' + worker.api_key + '&all=1')
err = json.loads(res.data)
assert res.status_code == 401, err
assert err['status'] == 'failed', err
assert err['target'] == 'project', err
assert err['exception_cls'] == 'Unauthorized', err
assert err['action'] == 'GET', err

@with_context
def test_project_locks_user_subadmin(self):
""" Test API should work if user is subadmin"""
admin = UserFactory.create(admin=True)
subadmin = UserFactory.create(admin=False, subadmin=True)

project = self.setupProjects()
project_id = str(project.id)

res = self.app.get('/api/locks?id=' + project_id + '&api_key=' + subadmin.api_key + '&all=1')
data = json.loads(res.data)
assert res.status_code == 200, data
assert data[0]['short_name'] == 'test-app1', data

@with_context
def test_project_locks_user_admin(self):
""" Test API should work if user is admin"""
admin = UserFactory.create(admin=True)

project = self.setupProjects()
project_id = str(project.id)

res = self.app.get('/api/locks?id=' + project_id + '&api_key=' + admin.api_key + '&all=1')
data = json.loads(res.data)
assert res.status_code == 200, data
assert data[0]['short_name'] == 'test-app1', data

@with_context
def test_project_locks_get_by_id(self):
""" Test get locks by project id when project id exists"""
admin = UserFactory.create(admin=True)
project1 = self.setupProjects()

# Test get by id
res = self.app.get('/api/locks?id=' + str(project1.id) + '&api_key=' + admin.api_key + '&all=1')
data = json.loads(res.data)
assert res.status_code == 200, data
assert len(data) == 1, data
assert data[0]['short_name'] == 'test-app1', data
assert data[0]['locks'] == [], data

@with_context
def test_project_locks_no_params(self):
""" Test API project query when no search params"""
admin = UserFactory.create(admin=True)
project1 = self.setupProjects()

# Test no params
res = self.app.get('/api/locks?api_key=' + admin.api_key)
data = json.loads(res.data)
assert res.status_code == 200, data
assert len(data) == 0, data

@with_context
def test_project_locks_value_does_not_match(self):
""" Test API project query when search value does not match"""
admin = UserFactory.create(admin=True)
project1 = self.setupProjects()

# Test value DNE
res = self.app.get('/api/locks?id=' + '9999' + '&api_key=' + admin.api_key + '&all=1')
data = json.loads(res.data)
assert res.status_code == 200, data
assert len(data) == 0, data


@with_context
def test_project_locks_param_does_not_exist(self):
""" Test API project query when search value does not match"""
admin = UserFactory.create(admin=True)
project1 = self.setupProjects()

# Test bad param
res = self.app.get('/api/locks?fakeparam=product::' + project1.info['product'] + '&api_key=' + admin.api_key + '&all=1')
err = json.loads(res.data)
assert res.status_code == 415, data
assert err['status'] == 'failed', err
assert err['target'] == 'project', err
assert err['exception_cls'] == 'AttributeError', err
assert err['action'] == 'GET', err

0 comments on commit ef50b62

Please sign in to comment.