Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rdiscrowd 5851 taskbrowse bookmarks dates #837

Merged
merged 16 commits into from
Apr 26, 2023
44 changes: 33 additions & 11 deletions pybossa/view/account.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
from flask_login import login_required, login_user, logout_user, \
current_user
from rq import Queue
from datetime import datetime

import pybossa.model as model
from flask_babel import gettext
Expand All @@ -57,6 +58,7 @@
from pybossa.core import user_repo, ldap
from pybossa.feed import get_update_feed
from pybossa.messages import *
from pybossa.model import make_timestamp

from pybossa.forms.forms import UserPrefMetadataForm, RegisterFormWithUserPrefMetadata
from pybossa.forms.account_view_forms import *
Expand Down Expand Up @@ -1154,13 +1156,22 @@ def add_metadata(name):
return redirect(url_for('account.profile', name=name))


def _get_bookmarks(user_name, short_name):
def bookmarks_dict_to_array(bookmarks_dict):
bookmarks_array = []
for name, meta in bookmarks_dict.items():
b = {'name': name}
b.update(meta)
bookmarks_array.append(b)
return bookmarks_array


def get_bookmarks(user_name, short_name):
taskbrowse_bookmarks = cached_users.get_taskbrowse_bookmarks(user_name)
proj_bookmarks = taskbrowse_bookmarks.get(short_name, {})
return proj_bookmarks
return bookmarks_dict_to_array(proj_bookmarks)


def _add_bookmark(user_name, short_name, bookmark_name, bookmark_url):
def add_bookmark(user_name, short_name, bookmark_name, bookmark_url):
user = user_repo.get_by_name(name=user_name)
taskbrowse_bookmarks = user.info.get('taskbrowse_bookmarks', {})
proj_bookmarks = taskbrowse_bookmarks.get(short_name, {})
Expand All @@ -1170,22 +1181,33 @@ def _add_bookmark(user_name, short_name, bookmark_name, bookmark_url):
if bookmark_url is None or len(bookmark_url) > MAX_BOOKMARK_URL_LEN:
raise ValueError('Bookmark URL must be between 1-100 characters.')

proj_bookmarks[bookmark_name] = bookmark_url
old_bookmark = proj_bookmarks.get(bookmark_name, None)
if old_bookmark is not None:
created_date = old_bookmark['created']
else:
created_date = model.make_timestamp()
updated_date = model.make_timestamp()
bookmark_data = {
'url': bookmark_url,
'created': created_date,
'updated': updated_date
}
proj_bookmarks[bookmark_name] = bookmark_data
taskbrowse_bookmarks[short_name] = proj_bookmarks
user.info['taskbrowse_bookmarks'] = taskbrowse_bookmarks

user_repo.update(user)
cached_users.delete_taskbrowse_bookmarks(user)
return proj_bookmarks
return bookmarks_dict_to_array(proj_bookmarks)


def _delete_bookmark(user_name, short_name, bookmark_name):
def delete_bookmark(user_name, short_name, bookmark_name):
user = user_repo.get_by_name(name=user_name)
taskbrowse_bookmarks = user.info.get('taskbrowse_bookmarks', {})
proj_bookmarks = taskbrowse_bookmarks.get(short_name, {})

if bookmark_name not in proj_bookmarks:
raise ValueError('Bookmark not found.')
raise ValueError('Bookmark not found')
del proj_bookmarks[bookmark_name]
# if no bookmarks left for this project, delete the mapping entry
if len(proj_bookmarks) == 0:
Expand All @@ -1196,7 +1218,7 @@ def _delete_bookmark(user_name, short_name, bookmark_name):
user.info['taskbrowse_bookmarks'] = taskbrowse_bookmarks
user_repo.update(user)
cached_users.delete_taskbrowse_bookmarks(user)
return proj_bookmarks
return bookmarks_dict_to_array(proj_bookmarks)


@blueprint.route('/<user_name>/taskbrowse_bookmarks/<short_name>', methods=['GET', 'POST', 'DELETE'])
Expand All @@ -1208,14 +1230,14 @@ def taskbrowse_bookmarks(user_name, short_name):

# get bookmarks for project from cache
if request.method == 'GET':
res_bookmarks = _get_bookmarks(user_name, short_name)
res_bookmarks = get_bookmarks(user_name, short_name)

# add a bookmark
elif request.method == 'POST':
bookmark_name = request.json.get('name', None)
bookmark_url = request.json.get('url', None)
try:
res_bookmarks = _add_bookmark(user_name, short_name, bookmark_name, bookmark_url)
res_bookmarks = add_bookmark(user_name, short_name, bookmark_name, bookmark_url)
except ValueError as e:
error_msg = str(e)
current_app.logger.exception(f'Bad request: {error_msg}, project: {short_name}, bookmark_name:{bookmark_name}')
Expand All @@ -1225,7 +1247,7 @@ def taskbrowse_bookmarks(user_name, short_name):
elif request.method == 'DELETE':
bookmark_name = request.json.get('name', None)
try:
res_bookmarks = _delete_bookmark(user_name, short_name, bookmark_name)
res_bookmarks = delete_bookmark(user_name, short_name, bookmark_name)
except ValueError as e:
error_msg = str(e)
current_app.logger.exception(f'Bad request: {error_msg}, project: {short_name}, bookmark_name:{bookmark_name}')
Expand Down
1 change: 1 addition & 0 deletions pybossa/view/projects.py
Original file line number Diff line number Diff line change
Expand Up @@ -1814,6 +1814,7 @@ def get_users_completed(task):
regular_user=regular_user,
admin_subadmin_coowner=admin_subadmin_coowner)


return handle_content_type(data)

def get_users_fullname(page_tasks, get_users_func, result_field):
Expand Down
153 changes: 91 additions & 62 deletions test/test_web.py
Original file line number Diff line number Diff line change
Expand Up @@ -10612,33 +10612,58 @@ def test_user_has_partial_answer_without_auth(self):
resp = self.app_get_json(url)
assert resp.status_code == 401, resp

def generate_sample_bookmarks(self, target_project="project1"):
bookmark_1_data = {
"created": "2019-01-01T14:37:30.642119",
"updated": "2019-01-01T14:37:30.642119",
"url": "https://gigwork.net/project/testproject66/tasks/browse/1/10?changed=true&display_columns=%5B%22task_id%22%2C%22priority%22%2C%22pcomplete%22%2C%22created%22%2C%22finish_time%22%2C%22gold_task%22%2C%22actions%22%2C%22lock_status%22%5D&order_by=task_id+asc&pcomplete_from=46&pcomplete_to=100&priority_from=0.45&priority_to=1.00&display_info_columns=%5B%5D"
}
bookmark_2_data = {
"created": "2019-01-01T14:37:30.642119",
"updated": "2023-01-01T14:37:30.642119",
"url": "https://gigwork.net/project/testproject66/tasks/browse"
}
bookmark_3_data = {
"created": "2022-01-01T14:37:30.642119",
"updated": "2022-01-01T14:37:30.642119",
"url": "https://gigwork.net/project/project2/tasks/browse"
}
bookmarks = {
target_project : {
"bookmark 1" : bookmark_1_data,
"bookmark 2" : bookmark_2_data
},
"project2" : {
"bookmark 3" : bookmark_3_data
}
}
return (bookmark_1_data, bookmark_2_data, bookmark_3_data, bookmarks)


@with_context
def test_get_taskbrowse_bookmarks(self):
"""Test get taskbrowse_bookmark works"""
data = self.original
target_project = "project1"
bookmarks = {
target_project : {
"bookmark 1" : "https://gigwork.net/project/testproject66/tasks/browse/1/10?changed=true&display_columns=%5B%22task_id%22%2C%22priority%22%2C%22pcomplete%22%2C%22created%22%2C%22finish_time%22%2C%22gold_task%22%2C%22actions%22%2C%22lock_status%22%5D&order_by=task_id+asc&pcomplete_from=46&pcomplete_to=100&priority_from=0.45&priority_to=1.00&display_info_columns=%5B%5D",
"bookmark 2" : "https://gigwork.net/project/testproject66/tasks/browse"
},
"project2" : {
"bookmark 3" : "https://gigwork.net/project/project2/tasks/browse"
}

}
bookmark_1_data, bookmark_2_data, bookmark_3_data, bookmarks = self.generate_sample_bookmarks(target_project)
info = {
'taskbrowse_bookmarks' : bookmarks
}
}
user = UserFactory.create(info=info)
self.signin_user(user)
url = f"/account/{user.name}/taskbrowse_bookmarks/{target_project}"
res = self.app.get(url)

assert res.status_code == 200, res.status_code
data = json.loads(res.data)
assert str(data) == str(bookmarks[target_project])

assert data[0]['url'] == bookmark_1_data['url']
assert data[0]['created'] == bookmark_1_data['created']
assert data[0]['updated'] == bookmark_1_data['updated']

assert data[1]['url'] == bookmark_2_data['url']
assert data[1]['created'] == bookmark_2_data['created']
assert data[1]['updated'] == bookmark_2_data['updated']


@with_context
Expand All @@ -10654,24 +10679,15 @@ def test_get_taskbrowse_bookmarks_no_bookmarks(self):

assert res.status_code == 200, res.status_code
data = json.loads(res.data)
assert str(data) == str({})
assert len(data) == 0


@with_context
def test_get_taskbrowse_bookmarks_no_saved_bookmarks_for_project(self):
"""Test get taskbrowse_bookmark works when no saved bookmarks for project"""
data = self.original
target_project = "project1"
bookmarks = {
"project600" : {
"bookmark 1" : "https://gigwork.net/project/testproject66/tasks/browse/1/10?changed=true&display_columns=%5B%22task_id%22%2C%22priority%22%2C%22pcomplete%22%2C%22created%22%2C%22finish_time%22%2C%22gold_task%22%2C%22actions%22%2C%22lock_status%22%5D&order_by=task_id+asc&pcomplete_from=46&pcomplete_to=100&priority_from=0.45&priority_to=1.00&display_info_columns=%5B%5D",
"bookmark 2" : "https://gigwork.net/project/testproject66/tasks/browse"
},
"project2" : {
"bookmark 3" : "https://gigwork.net/project/project2/tasks/browse"
}

}
target_project = "project_with_no_bookmarks"
_, _, _, bookmarks = self.generate_sample_bookmarks()
info = {
'taskbrowse_bookmarks' : bookmarks
}
Expand All @@ -10682,7 +10698,7 @@ def test_get_taskbrowse_bookmarks_no_saved_bookmarks_for_project(self):

assert res.status_code == 200, res.status_code
data = json.loads(res.data)
assert str(data) == str({})
assert len(data) == 0


@with_context
Expand All @@ -10692,9 +10708,9 @@ def test_post_taskbrowse_bookmarks(self):
target_project = "project1"

url1 = "https://gigwork.net/project/testproject66/tasks/browse"
url2 = "https://gigwork.net/project/testproject66/tasks/browse/1/10?changed=true&display_columns=%5B%22task_id%22%2C%22priority%22%2C%22pcomplete%22%2C%22created%22%2C%22finish_time%22%2C%22gold_task%22%2C%22actions%22%2C%22lock_status%22%5D&order_by=task_id+asc&pcomplete_from=46&pcomplete_to=100&priority_from=0.45&priority_to=1.00&display_info_columns=%5B%5D"
name1 = "bookmark1"
name2 = "bookmark2"
url2 = "https://gigwork.net/project/testproject66/tasks/browse/1/10?changed=true&display_columns=%5B%22task_id%22%2C%22priority%22%2C%22pcomplete%22%2C%22created%22%2C%22finish_time&order_by=task_id"
name1 = "bookmark 1"
name2 = "bookmark 2"

user = UserFactory.create()
self.signin_user(user)
Expand All @@ -10704,32 +10720,59 @@ def test_post_taskbrowse_bookmarks(self):
res = self.app.post(url, json={"name":name1, "url":url1})
assert res.status_code == 200, res.status_code
data = json.loads(res.data)
assert str(data) == str({name1:url1})
assert data[0]['url'] == url1
assert 'created' in data[0]
assert 'updated' in data[0]

# test if new url is appended correctly
# test second insertion
res = self.app.post(url, json={"name":name2, "url":url2})
assert res.status_code == 200, res.status_code
data = json.loads(res.data)
assert str(data) == str({name1:url1, name2:url2})
assert data[0]['url'] == url1
assert data[1]['url'] == url2

# test if data is saved in db
res = self.app.get(url)
assert res.status_code == 200, res.status_code
data = json.loads(res.data)
assert str(data) == str({name1:url1, name2:url2})
assert data[0]['url'] == url1
assert data[1]['url'] == url2

# test adding bookmark for a different project
new_url = f"/account/{user.name}/taskbrowse_bookmarks/project2"
res = self.app.post(new_url, json={"name":name2, "url":url2})
res = self.app.post(new_url, json={"name":name2, "url":url1})
assert res.status_code == 200, res.status_code
data = json.loads(res.data)
assert str(data) == str({name2:url2})
assert data[0]['url'] == url1

# test new post does not affect old data
res = self.app.get(url)
assert res.status_code == 200, res.status_code
data = json.loads(res.data)
assert str(data) == str({name1:url1, name2:url2})
assert data[0]['url'] == url1
assert data[1]['url'] == url2

@with_context
def test_update_taskbrowse_bookmark(self):
"""Test update taskbrowse_bookmark via POST works"""
data = self.original
target_project = "project1"
bookmark_1_data, _, _, bookmarks = self.generate_sample_bookmarks(target_project)
info = {
'taskbrowse_bookmarks' : bookmarks
}
user = UserFactory.create(info=info)
self.signin_user(user)
url = f"/account/{user.name}/taskbrowse_bookmarks/{target_project}"

# update a bookmark that already exists
res = self.app.post(url, json={"name":"bookmark 1", "url":"www.google.com"})
assert res.status_code == 200, res.status_code
data = json.loads(res.data)
assert data[0]["updated"] != bookmark_1_data["updated"]
assert data[0]["created"] == bookmark_1_data["created"]
assert data[0]["url"] == "www.google.com"


@with_context
def test_post_taskbrowse_bookmarks_missing_arguments(self):
Expand Down Expand Up @@ -10758,56 +10801,42 @@ def test_delete_taskbrowse_bookmarks(self):
"""Test deleting taskbrowse bookmarks"""
data = self.original
target_project = "project1"
bookmarks = {
target_project : {
"bookmark1" : "https://gigwork.net/project/testproject66/tasks/browse/1/10?changed=true&display_columns=%5B%22task_id%22%2C%22priority%22%2C%22pcomplete%22%2C%22created%22%2C%22finish_time%22%2C%22gold_task%22%2C%22actions%22%2C%22lock_status%22%5D&order_by=task_id+asc&pcomplete_from=46&pcomplete_to=100&priority_from=0.45&priority_to=1.00&display_info_columns=%5B%5D",
"bookmark2" : "https://gigwork.net/project/testproject66/tasks/browse"
},
"project2" : {
"bookmark3" : "https://gigwork.net/project/project2/tasks/browse"
}

}
bookmark_1_data, bookmark_2_data ,_, bookmarks = self.generate_sample_bookmarks(target_project)
info = {
'taskbrowse_bookmarks' : bookmarks
}

user = UserFactory.create(info=info)
self.signin_user(user)
url = f"/account/{user.name}/taskbrowse_bookmarks/{target_project}"
data = {"name": "bookmark1"}
data = {"name": "bookmark 1"}
res = self.app.delete(url, json=data)

expected_res = {"bookmark2" : "https://gigwork.net/project/testproject66/tasks/browse"}
expected_res = {"bookmark 2" : "https://gigwork.net/project/testproject66/tasks/browse"}
assert res.status_code == 200, res.status_code
data = json.loads(res.data)
assert str(data) == str(expected_res)
print(data)
assert len(data) == 1
assert data[0]['url'] == bookmark_2_data['url']
assert data[0]['created'] == bookmark_2_data['created']
assert data[0]['updated'] == bookmark_2_data['updated']

# ensure deleting last bookmark does not result in error
url = f"/account/{user.name}/taskbrowse_bookmarks/{target_project}"
data = {"name" : "bookmark2"}
data = {"name" : "bookmark 2"}
res = self.app.delete(url, json=data)

expected_res = {}
assert res.status_code == 200, res.status_code
data = json.loads(res.data)
assert str(data) == str(expected_res)
assert len(data) == 0

@with_context
def test_delete_taskbrowse_bookmarks_invalid_bookmark_name(self):
def test_delete_taskbrowse_bookmarks_bookmark_not_found(self):
"""Test calling delete taskbrowse bookmarks with an invalid bookmark name"""
data = self.original
target_project = "project1"
bookmarks = {
target_project : {
"bookmark1" : "https://gigwork.net/project/testproject66/tasks/browse/1/10?changed=true&display_columns=%5B%22task_id%22%2C%22priority%22%2C%22pcomplete%22%2C%22created%22%2C%22finish_time%22%2C%22gold_task%22%2C%22actions%22%2C%22lock_status%22%5D&order_by=task_id+asc&pcomplete_from=46&pcomplete_to=100&priority_from=0.45&priority_to=1.00&display_info_columns=%5B%5D",
"bookmark2" : "https://gigwork.net/project/testproject66/tasks/browse"
},
"project2" : {
"bookmark3" : "https://gigwork.net/project/project2/tasks/browse"
}

}
_,_,_, bookmarks = self.generate_sample_bookmarks(target_project)
info = {
'taskbrowse_bookmarks' : bookmarks
}
Expand All @@ -10820,7 +10849,7 @@ def test_delete_taskbrowse_bookmarks_invalid_bookmark_name(self):


@with_context
def test_get_taskbrowse_bookmarks_user_errors(self):
def test_get_taskbrowse_bookmarks_user_auth_errors(self):
"""Test retrive taskbrowse bookmarks returns errors"""
data = self.original
target_project = "project1"
Expand Down