-
Couldn't load subscription status.
- Fork 79
Import artifact #1716
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
Import artifact #1716
Changes from 6 commits
d724dfd
25891f8
7014f68
6aed1f3
d92f4ba
8365dce
9325676
00d110a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -234,7 +234,7 @@ def artifact_get_req(user_id, artifact_id): | |
|
|
||
| @execute_as_transaction | ||
| def artifact_post_req(user_id, filepaths, artifact_type, name, | ||
| prep_template_id): | ||
| prep_template_id, artifact_id=None): | ||
| """Creates the initial artifact for the prep template | ||
|
|
||
| Parameters | ||
|
|
@@ -250,6 +250,8 @@ def artifact_post_req(user_id, filepaths, artifact_type, name, | |
| Name to give the artifact | ||
| prep_template_id : int or str castable to int | ||
| Prep template to attach the artifact to | ||
| artifact_id : str, optional | ||
|
||
| The id of the imported artifact | ||
|
|
||
| Returns | ||
| ------- | ||
|
|
@@ -267,41 +269,55 @@ def artifact_post_req(user_id, filepaths, artifact_type, name, | |
| if access_error: | ||
| return access_error | ||
|
|
||
| uploads_path = get_mountpoint('uploads')[0][1] | ||
| path_builder = partial(join, uploads_path, str(study_id)) | ||
| cleaned_filepaths = [] | ||
| for ftype, file_list in viewitems(filepaths): | ||
| # JavaScript sends us this list as a comma-separated list | ||
| for fp in file_list.split(','): | ||
| # JavaScript will send this value as an empty string if the | ||
| # list of files was empty. In such case, the split will generate | ||
| # a single element containing the empty string. Check for that case | ||
| # here and, if fp is not the empty string, proceed to check if | ||
| # the file exists | ||
| if fp: | ||
| # Check if filepath being passed exists for study | ||
| full_fp = path_builder(fp) | ||
| exists = check_fp(study_id, full_fp) | ||
| if exists['status'] != 'success': | ||
| return {'status': 'error', | ||
| 'message': 'File does not exist: %s' % fp} | ||
| cleaned_filepaths.append((full_fp, ftype)) | ||
|
|
||
| # This should never happen, but it doesn't hurt to actually have | ||
| # a explicit check, in case there is something odd with the JS | ||
| if not cleaned_filepaths: | ||
| return {'status': 'error', | ||
| 'message': "Can't create artifact, no files provided."} | ||
| if artifact_id: | ||
| # if the artifact id has been provided, import the artifact | ||
| try: | ||
| artifact = Artifact.copy(Artifact(artifact_id), prep) | ||
| except Exception as e: | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This pattern seems to be replicated in two different locations, the reality of it is that if anything goes wrong, we want the interface to know about that, so perhaps the thing to do here is to follow the decorator pattern and decorate these methods to catch a general exception and return the appropriate dictionary with the encoded information we need? Not blocking, but I thought I would bring this up now. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That is actually a good point - however I think it shouldn't be blocking now and we can fix later. Can you open an issue? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. See: #1717 On (Mar-28-16|16:22), Jose Navas wrote:
|
||
| # We should hit this exception rarely (that's why it is an | ||
| # exception) since at this point we have done multiple checks. | ||
| # However, it can occur in weird cases, so better let the GUI know | ||
| # that this failed | ||
| return {'status': 'error', | ||
| 'message': "Error creating artifact: %s" % str(e)} | ||
|
|
||
| try: | ||
| artifact = Artifact.create(cleaned_filepaths, artifact_type, name=name, | ||
| prep_template=prep) | ||
| except Exception as e: | ||
| # We should hit this exception rarely (that's why it is an exception) | ||
| # since at this point we have done multiple checks. However, it can | ||
| # occur in weird cases, so better let the GUI know that this failed | ||
| return {'status': 'error', | ||
| 'message': "Error creating artifact: %s" % str(e)} | ||
| else: | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just want to confirm that there are tests for the try/except blocks for this section. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I had for the one below, not for the one above - just added. |
||
| uploads_path = get_mountpoint('uploads')[0][1] | ||
| path_builder = partial(join, uploads_path, str(study_id)) | ||
| cleaned_filepaths = [] | ||
| for ftype, file_list in viewitems(filepaths): | ||
| # JavaScript sends us this list as a comma-separated list | ||
| for fp in file_list.split(','): | ||
| # JavaScript will send this value as an empty string if the | ||
| # list of files was empty. In such case, the split will | ||
| # generate a single element containing the empty string. Check | ||
| # for that case here and, if fp is not the empty string, | ||
| # proceed to check if the file exists | ||
| if fp: | ||
| # Check if filepath being passed exists for study | ||
| full_fp = path_builder(fp) | ||
| exists = check_fp(study_id, full_fp) | ||
| if exists['status'] != 'success': | ||
| return {'status': 'error', | ||
| 'message': 'File does not exist: %s' % fp} | ||
| cleaned_filepaths.append((full_fp, ftype)) | ||
|
|
||
| # This should never happen, but it doesn't hurt to actually have | ||
| # a explicit check, in case there is something odd with the JS | ||
| if not cleaned_filepaths: | ||
| return {'status': 'error', | ||
| 'message': "Can't create artifact, no files provided."} | ||
|
|
||
| try: | ||
| artifact = Artifact.create(cleaned_filepaths, artifact_type, | ||
| name=name, prep_template=prep) | ||
| except Exception as e: | ||
| # We should hit this exception rarely (that's why it is an | ||
| # exception) since at this point we have done multiple checks. | ||
| # However, it can occur in weird cases, so better let the GUI know | ||
| # that this failed | ||
| return {'status': 'error', | ||
| 'message': "Error creating artifact: %s" % str(e)} | ||
|
|
||
| return {'status': 'success', | ||
| 'message': '', | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -8,6 +8,8 @@ | |
| from __future__ import division | ||
| from collections import defaultdict | ||
|
|
||
| from future.utils import viewitems | ||
|
|
||
| from qiita_db.user import User | ||
| from qiita_db.study import Study | ||
| from qiita_db.metadata_template.prep_template import PrepTemplate | ||
|
|
@@ -180,7 +182,7 @@ def study_prep_get_req(study_id, user_id): | |
| 'info': prep_info} | ||
|
|
||
|
|
||
| def study_files_get_req(study_id, prep_template_id, artifact_type): | ||
| def study_files_get_req(user_id, study_id, prep_template_id, artifact_type): | ||
| """Returns the uploaded files for the study id categorized by artifact_type | ||
|
|
||
| It retrieves the files uploaded for the given study and tries to do a | ||
|
|
@@ -189,6 +191,8 @@ def study_files_get_req(study_id, prep_template_id, artifact_type): | |
|
|
||
| Parameters | ||
| ---------- | ||
| user_id : str | ||
| The id of the user making the request | ||
| study_id : int | ||
| The study id | ||
| prep_template_id : int | ||
|
|
@@ -242,8 +246,20 @@ def study_files_get_req(study_id, prep_template_id, artifact_type): | |
| # because selected is initialized to the empty list | ||
| file_types.insert(0, (first[0], first[1], selected)) | ||
|
|
||
| # Create a list of artifacts that the user has access to, in case that | ||
| # he wants to import the files from another artifact | ||
| user = User(user_id) | ||
| artifact_options = [] | ||
| for study, artifacts in viewitems(user.user_artifacts( | ||
|
||
| artifact_type=artifact_type)): | ||
| study_label = "%s (%d)" % (study.title, study.id) | ||
| for a in artifacts: | ||
| artifact_options.append( | ||
| (a.id, "%s - %s (%d)" % (study_label, a.name, a.id))) | ||
|
|
||
| return {'status': 'success', | ||
| 'message': '', | ||
| 'remaining': remaining, | ||
| 'file_types': file_types, | ||
| 'num_prefixes': num_prefixes} | ||
| 'num_prefixes': num_prefixes, | ||
| 'artifacts': artifact_options} | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -78,9 +78,6 @@ def get(self): | |
| atype = self.get_argument('artifact_type') | ||
| pt_id = self.get_argument('prep_template_id') | ||
|
|
||
| res = study_files_get_req(study_id, pt_id, atype) | ||
| res = study_files_get_req(self.current_user.id, study_id, pt_id, atype) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was wondering about this, why send the user.id vs. the user object? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For consistency with the other calls - when these calls where put in place, it was assumed that they'll be replaced by REST api calls, hence the idea on just passing the ids. I don't think that is gonna happen, b/c this calls are specialized for the interface but I want to be consistent at this point. |
||
|
|
||
| self.render('study_ajax/artifact_file_selector.html', | ||
| remaining=res['remaining'], | ||
| file_types=res['file_types'], | ||
| num_prefixes=res['num_prefixes']) | ||
| self.render('study_ajax/artifact_file_selector.html', **res) | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Question: incorrect artifact types here will go unnoticed i.e.
>>> .....user_artifacts(artifact_type='B10M'), this will return an empty dictionary, would we want to alert consumers that this is actually an artifact type not recognized by the system or is that not at all expected/supported.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see your point. This has been added mainly for the needs in the interface. I don't think though that an empty dict is a bad answer. What do others think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think is fine to return an empty dict. This is also happening in other sections of the code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good.
On (Mar-28-16|16:22), Antonio Gonzalez wrote: