Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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: 2 additions & 2 deletions qiita_pet/handlers/api_proxy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
artifact_post_req, artifact_get_req,
artifact_status_put_req, artifact_delete_req,
artifact_summary_get_request,
artifact_summary_post_request)
artifact_summary_post_request, artifact_patch_request)
from .ontology import ontology_patch_handler
from .processing import (
list_commands_handler_get_req, process_artifact_handler_get_req,
Expand Down Expand Up @@ -61,4 +61,4 @@
'list_commands_handler_get_req', 'process_artifact_handler_get_req',
'list_options_handler_get_req', 'workflow_handler_post_req',
'workflow_handler_patch_req', 'workflow_run_post_req',
'job_ajax_get_req']
'job_ajax_get_req', 'artifact_patch_request']
58 changes: 58 additions & 0 deletions qiita_pet/handlers/api_proxy/artifact.py
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,64 @@ def artifact_post_req(user_id, filepaths, artifact_type, name,
'artifact': artifact.id}


def artifact_patch_request(user_id, req_op, req_path, req_value=None,
req_from=None):
"""Modifies an attribute of the artifact

Parameters
----------
user_id : str
The id of the user performing the patch operation
req_op : str
The operation to perform on the artifact
req_path : str
The prep information and attribute to patch
req_value : str, optional
The value that needs to be modified
req_from : str, optional
The original path of the element

Returns
-------
dict of {str, str}
A dictionary with the following keys:
- status: str, whether if the request is successful or not
- message: str, if the request is unsuccessful, a human readable error
"""
if req_op == 'replace':
req_path = [v for v in req_path.split('/') if v]
if len(req_path) != 2:
return {'status': 'error',
'message': 'Incorrect path parameter'}

artifact_id = req_path[0]
attribute = req_path[1]

# Check if the user actually has access to the artifact
artifact = Artifact(artifact_id)
access_error = check_access(artifact.study.id, user_id)
if access_error:
return access_error

if not req_value:
return {'status': 'error',
'message': 'A value is required'}

if attribute == 'name':
artifact.name = req_value
return {'status': 'success',
'message': ''}
else:
# We don't understand the attribute so return an error
return {'status': 'error',
'message': 'Attribute "%s" not found. '
'Please, check the path parameter' % attribute}
else:
return {'status': 'error',
'message': 'Operation "%s" not supported. '
'Current supported operations: replace' % req_op}


def artifact_types_get_req():
"""Gets artifact types and descriptions available

Expand Down
44 changes: 43 additions & 1 deletion qiita_pet/handlers/api_proxy/tests/test_artifact.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@
from qiita_pet.handlers.api_proxy.artifact import (
artifact_get_req, artifact_status_put_req, artifact_graph_get_req,
artifact_delete_req, artifact_types_get_req, artifact_post_req,
artifact_summary_get_request, artifact_summary_post_request)
artifact_summary_get_request, artifact_summary_post_request,
artifact_patch_request)


class TestArtifactAPIReadOnly(TestCase):
Expand Down Expand Up @@ -243,6 +244,47 @@ def test_artifact_summary_post_request(self):
'job': [job.id, 'queued', None]}
self.assertEqual(obs, exp)

def test_artifact_patch_request(self):
obs = artifact_patch_request('test@foo.bar', 'replace', '/1/name/',
req_value='NEW_NAME')
exp = {'status': 'success', 'message': ''}
self.assertEqual(obs, exp)

self.assertEqual(Artifact(1).name, 'NEW_NAME')

def test_artifact_patch_request_errors(self):
# No access to the study
obs = artifact_patch_request('demo@microbio.me', 'replace',
'/1/name/', req_value='NEW_NAME')
exp = {'status': 'error',
'message': 'User does not have access to study'}
self.assertEqual(obs, exp)
# Incorrect path parameter
obs = artifact_patch_request('test@foo.bar', 'replace',
'/1/name/oops/', req_value='NEW_NAME')
exp = {'status': 'error',
'message': 'Incorrect path parameter'}
self.assertEqual(obs, exp)
# Missing value
obs = artifact_patch_request('test@foo.bar', 'replace', '/1/name/')
exp = {'status': 'error',
'message': 'A value is required'}
self.assertEqual(obs, exp)
# Wrong attribute
obs = artifact_patch_request('test@foo.bar', 'replace', '/1/oops/',
req_value='NEW_NAME')
exp = {'status': 'error',
'message': 'Attribute "oops" not found. Please, check the '
'path parameter'}
self.assertEqual(obs, exp)
# Wrong operation
obs = artifact_patch_request('test@foo.bar', 'add', '/1/name/',
req_value='NEW_NAME')
exp = {'status': 'error',
'message': 'Operation "add" not supported. Current supported '
'operations: replace'}
self.assertEqual(obs, exp)

def test_artifact_delete_req(self):
obs = artifact_delete_req(3, 'test@foo.bar')
exp = {'status': 'success', 'message': ''}
Expand Down
26 changes: 25 additions & 1 deletion qiita_pet/handlers/study_handlers/artifact.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
from qiita_pet.handlers.api_proxy import (
artifact_graph_get_req, artifact_types_get_req, artifact_post_req,
artifact_status_put_req, artifact_get_req, artifact_delete_req,
artifact_summary_get_request, artifact_summary_post_request)
artifact_summary_get_request, artifact_summary_post_request,
artifact_patch_request)
from qiita_core.util import execute_as_transaction
from qiita_core.qiita_settings import qiita_config

Expand Down Expand Up @@ -57,30 +58,52 @@ def post(self):


class ArtifactSummaryAJAX(BaseHandler):
@authenticated
def get(self):
artifact_id = self.get_argument('artifact_id')
res = artifact_summary_get_request(self.current_user.id, artifact_id)
res['artifact_id'] = artifact_id
self.render("study_ajax/artifact_summary.html", **res)

@authenticated
def post(self):
artifact_id = self.get_argument('artifact_id')
res = artifact_summary_post_request(self.current_user.id, artifact_id)
self.write(res)


class ArtifactAJAX(BaseHandler):
@authenticated
def get(self):
artifact_id = to_int(self.get_argument('artifact_id'))
name = artifact_get_req(self.current_user.id, artifact_id)['name']
self.write(name)

@authenticated
def post(self):
artifact_id = to_int(self.get_argument('artifact_id'))
self.write(artifact_delete_req(artifact_id, self.current_user.id))

@authenticated
def patch(self):
"""Patches a prep template in the system

Follows the JSON PATCH specification:
https://tools.ietf.org/html/rfc6902
"""
req_op = self.get_argument('op')
req_path = self.get_argument('path')
req_value = self.get_argument('value', None)
req_from = self.get_argument('from', None)

response = artifact_patch_request(
self.current_user.id, req_op, req_path, req_value, req_from)

self.write(response)


class ArtifactAdminAJAX(BaseHandler):
@authenticated
def get(self):
artifact_id = to_int(self.get_argument('artifact_id'))
info = artifact_get_req(self.current_user.id, artifact_id)
Expand Down Expand Up @@ -130,6 +153,7 @@ def get(self):

self.write(' '.join(buttons))

@authenticated
def post(self):
visibility = self.get_argument('visibility')
artifact_id = int(self.get_argument('artifact_id'))
Expand Down
61 changes: 60 additions & 1 deletion qiita_pet/templates/study_ajax/artifact_summary.html
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,52 @@
load_artifact_summary(aid);
});
}

/*
*
* Function to update the artifact name
*
* @param artifact_id int the artifact to be changed
* @param new_name str the new artifact name
*
* This function executes an AJAX call to update the artifact name
* and updates the interface accordingly
*
*/
function change_artifact_name(artifact_id, new_name) {
$.ajax({
url: '/artifact/',
type: 'PATCH',
data: {'op': 'replace', 'path': '/' + artifact_id + '/name/', 'value': new_name, 'artifact_id': artifact_id},
success: function(data) {
$("#update-artifact-name").modal('hide');
if(data.status == 'error') {
bootstrapAlert(data.message, "danger");
}
else {
$("#summary-title").text('Summary: ' + new_name + ' (ID: ' + artifact_id + ')');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we really need Summary: name, (ID: bla!)? What about name (ID: bla)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

load_template_graph();
}
}
});
console.log('clicked ' + artifact_id + ' ' + new_name);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rm?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Opps done

}

/*
*
* Allows updating the artifact name if the new artifact name is not empty
*
*/
function validate_new_name() {
$("#update-name-btn").prop('disabled', $("#new-artifact-name").val() === "");
}
</script>
<div class='row'>
<div class='col-md-12'>
<h4>
<i>Summary: {{name}} (ID: {{artifact_id}})</i>
<i id='summary-title'>Summary: {{name}} (ID: {{artifact_id}})</i>
{% if editable %}
<a class="btn btn-default btn-sm" data-toggle="modal" data-target="#update-artifact-name"><span class="glyphicon glyphicon-pencil"></span> Edit</a>
<a class="btn btn-default btn-sm" onclick="populate_main_div('/study/process/', {artifact_id: {{artifact_id}} });"><span class="glyphicon glyphicon-play"></span> Process</a>
{% end %}
</h4>
Expand Down Expand Up @@ -104,3 +144,22 @@ <h4>
{% end %}
</div>
</div>

<!-- Modal to update the artifact name -->
<div class="modal fade update-artifact-name" tabindex="-1" role="dialog" id="update-artifact-name">
<div class="modal-dialog modal-md">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h3>Updating artifact {{artifact_id}} name</h3>
</div>
<div class="modal-body">
Introduce the new name:<br/>
<input type="text" name="new-artifact-name" id="new-artifact-name" size="35" maxlength="35" onkeyup="validate_new_name();">
<button id="update-name-btn" class="btn btn-default" onclick="change_artifact_name({{artifact_id}}, $('#new-artifact-name').val());" disabled>Update</button>
</div>
<div class="modal-footer">
</div>
</div>
</div>
</div>
10 changes: 5 additions & 5 deletions qiita_pet/templates/study_base.html
Original file line number Diff line number Diff line change
Expand Up @@ -215,12 +215,12 @@
{% block content %}
<div class="row">
<div class="col-md-3">
<button class="btn btn-info btn-block" onclick="populate_main_div('/study/description/baseinfo/', { study_id: {{study_info['study_id']}} })"><span class="glyphicon glyphicon-info-sign"></span> Study Information</button>
<button class="btn btn-info btn-block" onclick="populate_main_div('/study/description/sample_template/', { study_id: {{study_info['study_id']}} })"><span class="glyphicon glyphicon-info-sign"></span> Sample Template</button>
<button class="btn btn-info btn-block" onclick="populate_main_div('/study/description/sample_summary/', { study_id: {{study_info['study_id']}} })" id="sample-summary-btn"><span class="glyphicon glyphicon-th-list"></span> Sample Summary</button>
<button class="btn btn-default btn-block" onclick="populate_main_div('/study/description/baseinfo/', { study_id: {{study_info['study_id']}} })"><span class="glyphicon glyphicon-info-sign"></span> Study Information</button>
<button class="btn btn-default btn-block" onclick="populate_main_div('/study/description/sample_template/', { study_id: {{study_info['study_id']}} })"><span class="glyphicon glyphicon-info-sign"></span> Sample Template</button>
<button class="btn btn-default btn-block" onclick="populate_main_div('/study/description/sample_summary/', { study_id: {{study_info['study_id']}} })" id="sample-summary-btn"><span class="glyphicon glyphicon-th-list"></span> Sample Summary</button>
{% if editable %}
<a class="btn btn-info btn-block" href="/study/upload/{{study_info['study_id']}}"><span class="glyphicon glyphicon-upload"></span> Upload Files</a>
<button class="btn btn-info btn-block" onclick="populate_main_div('/study/new_prep_template/', { study_id: {{study_info['study_id']}} })"><span class="glyphicon glyphicon-plus-sign"></span> Add New Preparation</button>
<a class="btn btn-default btn-block" href="/study/upload/{{study_info['study_id']}}"><span class="glyphicon glyphicon-upload"></span> Upload Files</a>
<button class="btn btn-default btn-block" onclick="populate_main_div('/study/new_prep_template/', { study_id: {{study_info['study_id']}} })"><span class="glyphicon glyphicon-plus-sign"></span> Add New Preparation</button>
{% end %}

<div id="data-types-menu"></div>
Expand Down