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-5627: Disable edit submission based on instance type #819

Merged
merged 2 commits into from
Feb 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion pybossa/forms/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,9 @@ class ProjectUpdateForm(ProjectForm):
short_name = TextField(label=None, widget=HiddenInput())
long_description = TextAreaField(lazy_gettext('Long Description'))
allow_anonymous_contributors = BooleanField(lazy_gettext('Allow Anonymous Contributors'))
allow_taskrun_edit = BooleanField(lazy_gettext('Allow Editing of Task Submissions'))
private_instance = data_access.data_access_levels.get("valid_access_levels", []) == ["L1", "L2", "L3", "L4"]
if not private_instance:
allow_taskrun_edit = BooleanField(lazy_gettext('Allow Editing of Task Submissions'))
zip_download = BooleanField(lazy_gettext('Allow ZIP data download'))
category_id = SelectField(lazy_gettext('Category'), coerce=int)
hidden = BooleanField(lazy_gettext('Hide?'))
Expand Down
70 changes: 41 additions & 29 deletions pybossa/view/projects.py
Original file line number Diff line number Diff line change
Expand Up @@ -823,7 +823,8 @@ def handle_valid_form(form):
'input_data': form.input_data_class.data,
'output_data': form.output_data_class.data
}
new_project.info["allow_taskrun_edit"] = form.allow_taskrun_edit.data
if "allow_taskrun_edit" in form:
new_project.info["allow_taskrun_edit"] = form.allow_taskrun_edit.data

project_repo.update(new_project)
auditlogger.add_log_entry(old_project, new_project, current_user)
Expand Down Expand Up @@ -854,7 +855,7 @@ def handle_valid_form(form):
project.kpi = project.info.get('kpi')
project.input_data_class = project.info.get('data_classification', {}).get('input_data')
project.output_data_class = project.info.get('data_classification', {}).get('output_data')
project.allow_taskrun_edit = project.info.get("allow_taskrun_edit")
project.allow_taskrun_edit = project.info.get("allow_taskrun_edit", False)
ensure_amp_config_applied_to_project(project, project.info.get('annotation_config', {}))
form = dynamic_project_form(ProjectUpdateForm, None, data_access_levels, obj=project,
products=prodsubprods, data_classes=data_classes)
Expand Down Expand Up @@ -1349,40 +1350,51 @@ def task_presenter(short_name, task_id, task_submitter_id=None):
tp_code = process_table_component(tp_code, user_response, task)
template_args["project"]["info"]["task_presenter"] = tp_code

# with edit submission, pass task run id so that taskrun can be updated
if request.args.get("mode") == "edit_submission":
template_args["taskrun_id"] = taskruns[0].id
template_args["taskrun_user_id"] = taskruns[0].user_id

def respond(tmpl):
response = dict(template=tmpl, **template_args)
return handle_content_type(response)

if not (task.project_id == project.id):
return respond('/projects/task/wrong.html')

guard = ContributionsGuard(sentinel.master,
timeout=project.info.get('timeout'))
guard.stamp(task, get_user_id_or_ip())

# Verify the worker has an unexpired lock on the task. Otherwise, task will fail to submit.
timeout, ttl = fetch_lock_for_user(task.project_id, task_id, user_id)
remaining_time = float(ttl) - time.time() if ttl else None
if (not remaining_time or remaining_time <= 0) and mode != 'read_only':
current_app.logger.info("unable to lock task or task expired. \
project %s, task %s, user %s, remaining time %s, mode %s",
task.project_id, task_id, user_id, remaining_time, mode)
flash(gettext("Unable to lock task or task expired. Please cancel and begin a new task."), "error")
else:
if mode != 'read_only':
# Set the original timeout seconds to display in the message.
template_args['project']['original_timeout'] = timeout
# Set the seconds remaining to display in the message.
template_args['project']['timeout'] = remaining_time
current_app.logger.info("User %s present task %s, remaining time %s, original timeout %s",
user_id, task_id, remaining_time, timeout)

if not guard.check_task_presented_timestamp(task, get_user_id_or_ip()):
guard.stamp_presented_time(task, get_user_id_or_ip())

if has_no_presenter(project):
flash(gettext("Sorry, but this project is still a draft and does "
"not have a task presenter."), "error")
# bypass lock check for the submitter when submitter can edit their response
bypass_lock_check = request.args.get("mode") == "edit_submission" and \
project.info.get("allow_taskrun_edit") and \
current_user.id == task_submitter_id

if not bypass_lock_check:
guard = ContributionsGuard(sentinel.master,
timeout=project.info.get('timeout'))
guard.stamp(task, get_user_id_or_ip())

# Verify the worker has an unexpired lock on the task. Otherwise, task will fail to submit.
timeout, ttl = fetch_lock_for_user(task.project_id, task_id, user_id)
remaining_time = float(ttl) - time.time() if ttl else None
if (not remaining_time or remaining_time <= 0) and mode != 'read_only':
current_app.logger.info("unable to lock task or task expired. \
project %s, task %s, user %s, remaining time %s, mode %s",
task.project_id, task_id, user_id, remaining_time, mode)
flash(gettext("Unable to lock task or task expired. Please cancel and begin a new task."), "error")
else:
if mode != 'read_only':
# Set the original timeout seconds to display in the message.
template_args['project']['original_timeout'] = timeout
# Set the seconds remaining to display in the message.
template_args['project']['timeout'] = remaining_time
current_app.logger.info("User %s present task %s, remaining time %s, original timeout %s",
user_id, task_id, remaining_time, timeout)

if not guard.check_task_presented_timestamp(task, get_user_id_or_ip()):
guard.stamp_presented_time(task, get_user_id_or_ip())

if has_no_presenter(project):
flash(gettext("Sorry, but this project is still a draft and does "
"not have a task presenter."), "error")

return respond('/projects/presenter.html')

Expand Down
30 changes: 30 additions & 0 deletions test/test_web.py
Original file line number Diff line number Diff line change
Expand Up @@ -3461,6 +3461,36 @@ def test_task_presenter_with_allow_taskrun_edit_raises_forbidden(self):
res = self.app.get('/project/%s/task/%s' % (project_short_name, task.id))
assert res.status_code == 403, res.status_code

@with_context
def test_task_presenter_with_allow_taskrun_edit_allows_submission(self):
"""Test WEB with taskrun edit is permitted with task_submitter_id passed"""
self.register()
self.signin()
self.create()
project = db.session.query(Project).get(1)
project.info = dict(allow_taskrun_edit=True)
db.session.commit()
self.new_task(project.id)
project_short_name = project.short_name

task = db.session.query(Task).filter(Task.project_id == 1).first()
# user = db.session.query(User).first()
regular_user = UserFactory.create(id=999, subadmin=False, admin=False)
regular_user.set_password('1234')
user_repo.save(regular_user)
self.signin(email=regular_user.email_addr, password='1234')
task_run = TaskRun(project_id=project.id, task_id=task.id,
info={'answer': 1,
'odfoa': {'version': 1, 'source-uri': 'http://fake.com', 'odf': {}, 'oa': {}},
'fake': {'b': 27}},
user_id=regular_user.id)
db.session.add(task_run)
db.session.commit()

# task_submitter_id is passed to fetch task response recorded by the user
res = self.app.get('/project/%s/task/%s/%s?mode=edit_submission' % (project_short_name, task.id, regular_user.id))
assert res.status_code == 200, res.status_code

@with_context
@patch('pybossa.view.projects.uploader.upload_file', return_value=True)
def test_25_get_wrong_task_app(self, mock):
Expand Down