def study_get_tags_request(user_id, study_id): """Retrieve available study tags for study_id Parameters ---------- user_id : int The id of the user performing the operation study_id : int The id of the study on which we will be performing the operation 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 - tags: [value, ..., ...] """ access_error = check_access(study_id, user_id) if access_error: return access_error study = Study(study_id) return {'status': 'success', 'message': '', 'tags': study.tags}
def study_delete_req(study_id, user_id): """Delete a given study Parameters ---------- study_id : int Study id to delete user_id : str User requesting the deletion Returns ------- dict Status of deletion, in the format {status: status, message: message} """ access_error = check_access(study_id, user_id) if access_error: return access_error status = 'success' try: Study.delete(int(study_id)) msg = '' except Exception as e: status = 'error' msg = 'Unable to delete study: %s' % str(e) return {'status': status, 'message': msg}
def artifact_delete_req(artifact_id, user_id): """Deletes the artifact Parameters ---------- artifact_id : int Artifact being acted on user_id : str The user requesting the action Returns ------- dict Status of action, in the form {'status': status, 'message': msg} status: status of the action, either success or error message: Human readable message for status """ pd = Artifact(int(artifact_id)) pt_id = pd.prep_templates[0].id access_error = check_access(pd.study.id, user_id) if access_error: return access_error job_id = safe_submit(user_id, delete_artifact, artifact_id) r_client.set(PREP_TEMPLATE_KEY_FORMAT % pt_id, dumps({'job_id': job_id})) return {'status': 'success', 'message': ''}
def sample_template_meta_cats_get_req(samp_id, user_id): """Returns list of metadata categories in the sample template Parameters ---------- samp_id : int or str typecastable to int SampleTemplate id to get info for user_id : str User requesting the sample template info Returns ------- dict Returns information in the form {'status': str, 'message': str, 'categories': list of str} samples is list of metadata categories in the template """ exists = _check_sample_template_exists(int(samp_id)) if exists['status'] != 'success': return exists access_error = check_access(samp_id, user_id) if access_error: return access_error return { 'status': 'success', 'message': '', 'categories': sorted(SampleTemplate(int(samp_id)).categories()) }
def artifact_get_prep_req(user_id, artifact_ids): """Returns all prep info sample ids for the given artifact_ids Parameters ---------- user_id : str user making the request artifact_ids : list of int list of artifact ids Returns ------- dict of objects A dictionary containing the artifact information {'status': status, 'message': message, 'data': {artifact_id: [prep info sample ids]} """ samples = {} for aid in sorted(artifact_ids): artifact = Artifact(aid) access_error = check_access(artifact.study.id, user_id) if access_error: return access_error samples[aid] = list(chain( *[sorted(pt.keys()) for pt in Artifact(aid).prep_templates])) return {'status': 'success', 'msg': '', 'data': samples}
def _check_permissions(self, sid): # Check general access to study study_info = check_access(sid, self.current_user.id) if study_info: raise HTTPError(405, reason="%s: %s, %s" % ( study_info['message'], self.current_user.email, sid)) return Study(sid)
def sample_template_samples_get_req(samp_id, user_id): """Returns list of samples in the sample template Parameters ---------- samp_id : int or str typecastable to int SampleTemplate id to get info for user_id : str User requesting the sample template info Returns ------- dict Returns summary information in the form {'status': str, 'message': str, 'samples': list of str} samples is list of samples in the template """ exists = _check_sample_template_exists(int(samp_id)) if exists['status'] != 'success': return exists access_error = check_access(samp_id, user_id) if access_error: return access_error return {'status': 'success', 'message': '', 'samples': sorted(x for x in SampleTemplate(int(samp_id))) }
def sample_template_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 == 'remove': req_path = [v for v in req_path.split('/') if v] if len(req_path) != 3: return {'status': 'error', 'message': 'Incorrect path parameter'} st_id = req_path[0] attribute = req_path[1] attr_id = req_path[2] # Check if the user actually has access to the template st = SampleTemplate(st_id) access_error = check_access(st.study_id, user_id) if access_error: return access_error # Offload the deletion of the sample or column to the cluster job_id = safe_submit(user_id, delete_sample_or_column, SampleTemplate, int(st_id), attribute, attr_id) # Store the job id attaching it to the sample template id r_client.set(SAMPLE_TEMPLATE_KEY_FORMAT % st_id, dumps({'job_id': job_id})) return {'status': 'success', 'message': ''} else: return { 'status': 'error', 'message': 'Operation "%s" not supported. ' 'Current supported operations: remove' % req_op }
def prep_template_filepaths_get_req(prep_id, user_id): """Returns all filepaths attached to a prep template Parameters ---------- prep_id : int The current prep template id user_id : int The current user object id Returns ------- dict of objects {'status': status, 'message': message, 'filepaths': [(filepath_id, filepath), ...]} """ exists = _check_prep_template_exists(int(prep_id)) if exists['status'] != 'success': return exists prep = PrepTemplate(int(prep_id)) access_error = check_access(prep.study_id, user_id) if access_error: return access_error return {'status': 'success', 'message': '', 'filepaths': prep.get_filepaths() }
def prep_template_delete_req(prep_id, user_id): """Delete the prep template Parameters ---------- prep_id : int The prep template to update user_id : str The current user object id Returns ------- dict of str {'status': status, 'message': message} """ exists = _check_prep_template_exists(int(prep_id)) if exists['status'] != 'success': return exists prep = PrepTemplate(int(prep_id)) access_error = check_access(prep.study_id, user_id) if access_error: return access_error msg = '' status = 'success' try: PrepTemplate.delete(prep.id) except Exception as e: msg = ("Couldn't remove prep template: %s" % str(e)) status = 'error' return {'status': status, 'message': msg}
def test_check_access_no_access(self): obs = check_access(1, '*****@*****.**') exp = { 'status': 'error', 'message': 'User does not have access to study' } self.assertEqual(obs, exp)
def prep_template_get_req(prep_id, user_id): """Gets the json of the full prep template Parameters ---------- prep_id : int PrepTemplate id to get info for user_id : str User requesting the sample template info Returns ------- dict of objects {'status': status, 'message': message, 'template': {sample: {column: value, ...}, ...} """ exists = _check_prep_template_exists(int(prep_id)) if exists['status'] != 'success': return exists prep = PrepTemplate(int(prep_id)) access_error = check_access(prep.study_id, user_id) if access_error: return access_error df = prep.to_dataframe() return {'status': 'success', 'message': '', 'template': df.to_dict(orient='index')}
def prep_template_jobs_get_req(prep_id, user_id): """Returns graph of all artifacts created from the prep base artifact Parameters ---------- prep_id : int Prep template ID to get graph for user_id : str User making the request Returns ------- dict with the jobs information Notes ----- Nodes are identified by the corresponding Artifact ID. """ prep = PrepTemplate(int(prep_id)) access_error = check_access(prep.study_id, user_id) if access_error: return access_error job_info = r_client.get(PREP_TEMPLATE_KEY_FORMAT % prep_id) result = {} if job_info: job_info = defaultdict(lambda: '', loads(job_info)) job_id = job_info['job_id'] job = ProcessingJob(job_id) result[job.id] = {'status': job.status, 'step': job.step, 'error': job.log.msg if job.log else ""} return result
def study_delete_req(study_id, user_id): """Delete a given study Parameters ---------- study_id : int Study id to delete user_id : str User requesting the deletion Returns ------- dict Status of deletion, in the format {status: status, message: message} """ access_error = check_access(study_id, user_id) if access_error: return access_error qiita_plugin = Software.from_name_and_version('Qiita', 'alpha') cmd = qiita_plugin.get_command('delete_study') params = Parameters.load(cmd, values_dict={'study': study_id}) job = ProcessingJob.create(User(user_id), params, True) # Store the job id attaching it to the sample template id r_client.set(STUDY_KEY_FORMAT % study_id, dumps({'job_id': job.id})) job.submit() return {'status': 'success', 'message': ''}
def study_delete_req(study_id, user_id): """Delete a given study Parameters ---------- study_id : int Study id to delete user_id : str User requesting the deletion Returns ------- dict Status of deletion, in the format {status: status, message: message} """ access_error = check_access(study_id, user_id) if access_error: return access_error status = 'success' try: Study.delete(int(study_id)) msg = '' except Exception as e: status = 'error' msg = 'Unable to delete study: %s' % str(e) return { 'status': status, 'message': msg }
def prep_template_delete_req(prep_id, user_id): """Delete the prep template Parameters ---------- prep_id : int The prep template to update user_id : str The current user object id Returns ------- dict of str {'status': status, 'message': message} """ exists = _check_prep_template_exists(int(prep_id)) if exists['status'] != 'success': return exists prep = PrepTemplate(int(prep_id)) access_error = check_access(prep.study_id, user_id) if access_error: return access_error msg = '' status = 'success' try: PrepTemplate.delete(prep.id) except Exception as e: msg = str(e) status = 'error' return {'status': status, 'message': msg}
def sample_template_delete_req(study_id, user_id): """Deletes the sample template attached to the study Parameters ---------- study_id : int The current study object id user_id : str The current user object id Returns ------- dict results dictonary in the format {'status': status, 'message': msg} status can be success, warning, or error depending on result message has the warnings or errors """ exists = _check_sample_template_exists(int(study_id)) if exists['status'] != 'success': return exists access_error = check_access(int(study_id), user_id) if access_error: return access_error # Offload the deletion of the sample template to the cluster job_id = safe_submit(user_id, delete_sample_template, int(study_id)) # Store the job id attaching it to the sample template id r_client.set(SAMPLE_TEMPLATE_KEY_FORMAT % study_id, dumps({'job_id': job_id})) return {'status': 'success', 'message': ''}
def artifact_get_prep_req(user_id, artifact_ids): """Returns all prep info sample ids for the given artifact_ids Parameters ---------- user_id : str user making the request artifact_ids : list of int list of artifact ids Returns ------- dict of objects A dictionary containing the artifact information {'status': status, 'message': message, 'data': {artifact_id: [prep info sample ids]} """ samples = {} for aid in sorted(artifact_ids): artifact = Artifact(aid) access_error = check_access(artifact.study.id, user_id) if access_error: return access_error samples[aid] = list( chain(*[sorted(pt.keys()) for pt in Artifact(aid).prep_templates])) return {'status': 'success', 'msg': '', 'data': samples}
def study_get_req(study_id, user_id): """Returns information available for the given study Parameters ---------- study_id : int Study id to get prep template info for user_id : str User requesting the info Returns ------- dict Data types information in the form {'status': status, 'message': message, 'info': dict of objects status can be success, warning, or error depending on result message has the warnings or errors info contains study information seperated by data type, in the form {col_name: value, ...} with value being a string, int, or list of strings or ints """ access_error = check_access(study_id, user_id) if access_error: return access_error # Can only pass ids over API, so need to instantiate object study = Study(study_id) study_info = study.info # Add needed info that is not part of the initial info pull study_info['publications'] = study.publications study_info['study_id'] = study.id study_info['study_title'] = study.title study_info['shared_with'] = [s.id for s in study.shared_with] study_info['status'] = study.status study_info['ebi_study_accession'] = study.ebi_study_accession study_info['ebi_submission_status'] = study.ebi_submission_status # Clean up StudyPerson objects to string for display pi = study_info['principal_investigator'] study_info['principal_investigator'] = { 'name': pi.name, 'email': pi.email, 'affiliation': pi.affiliation} lab_person = study_info['lab_person'] if lab_person: study_info['lab_person'] = { 'name': lab_person.name, 'email': lab_person.email, 'affiliation': lab_person.affiliation} samples = study.sample_template study_info['num_samples'] = 0 if samples is None else len(list(samples)) study_info['owner'] = study.owner.id return {'status': 'success', 'message': '', 'study_info': study_info, 'editable': study.can_edit(User(user_id))}
def prep_template_samples_get_req(prep_id, user_id): """Returns list of samples in the prep template Parameters ---------- prep_id : int or str typecastable to int PrepTemplate id to get info for user_id : str User requesting the prep template info Returns ------- dict Returns summary information in the form {'status': str, 'message': str, 'samples': list of str} samples is list of samples in the template """ exists = _check_prep_template_exists(int(prep_id)) if exists['status'] != 'success': return exists prep = PrepTemplate(int(prep_id)) access_error = check_access(prep.study_id, user_id) if access_error: return access_error return {'status': 'success', 'message': '', 'samples': sorted(x for x in PrepTemplate(int(prep_id))) }
def artifact_delete_req(artifact_id, user_id): """Deletes the artifact Parameters ---------- artifact_id : int Artifact being acted on user_id : str The user requesting the action Returns ------- dict Status of action, in the form {'status': status, 'message': msg} status: status of the action, either success or error message: Human readable message for status """ pd = Artifact(int(artifact_id)) access_error = check_access(pd.study.id, user_id) if access_error: return access_error try: Artifact.delete(int(artifact_id)) except QiitaDBArtifactDeletionError as e: return {'status': 'error', 'message': str(e)} return {'status': 'success', 'message': ''}
def analyses_associated_with_study(study_id, user_id): """Returns all available analyses in study_id Parameters ---------- study_id : int or str typecastable to int Study id to get info for user_id : str User requesting the sample template info Returns ------- dict Returns information in the form {'status': str, 'message': str, 'values': list of [qiita_db.analysis.Analysis, prep_ids for this study]} """ access_error = check_access(study_id, user_id) if access_error: return access_error values = generate_analyses_list_per_study(study_id) return {'status': 'success', 'message': '', 'values': values}
def prep_template_graph_get_req(prep_id, user_id): """Returns graph of all artifacts created from the prep base artifact Parameters ---------- prep_id : int Prep template ID to get graph for user_id : str User making the request Returns ------- dict of lists of tuples A dictionary containing the edge list representation of the graph, and the node labels. Formatted as: {'status': status, 'message': message, 'edge_list': [(0, 1), (0, 2)...], 'node_labels': [(0, 'label0'), (1, 'label1'), ...]} Notes ----- Nodes are identified by the corresponding Artifact ID. """ exists = _check_prep_template_exists(int(prep_id)) if exists['status'] != 'success': return exists prep = PrepTemplate(int(prep_id)) access_error = check_access(prep.study_id, user_id) if access_error: return access_error # We should filter for only the public artifacts if the user # doesn't have full access to the study full_access = Study(prep.study_id).can_edit(User(user_id)) artifact = prep.artifact if artifact is None: return {'edges': [], 'nodes': [], 'status': 'success', 'message': ''} G = artifact.descendants_with_jobs nodes, edges, wf_id = get_network_nodes_edges(G, full_access) # nodes returns [node_type, node_name, element_id]; here we are looking # for the node_type == artifact, and check by the element/artifact_id if # it's being deleted artifacts_being_deleted = [a[2] for a in nodes if a[0] == 'artifact' and Artifact(a[2]).being_deleted_by is not None] return {'edges': edges, 'nodes': nodes, 'workflow': wf_id, 'status': 'success', 'artifacts_being_deleted': artifacts_being_deleted, 'message': ''}
def sample_template_post_req(study_id, user_id, data_type, sample_template): """Creates the sample template from the given file Parameters ---------- study_id : int The current study object id user_id : str The current user object id data_type : str Data type for the sample template sample_template : str filename to use for creation Returns ------- dict results dictonary in the format {'status': status, 'message': msg, 'file': sample_template} status can be success, warning, or error depending on result message has the warnings or errors file has the file name """ access_error = check_access(int(study_id), user_id) if access_error: return access_error fp_rsp = check_fp(study_id, sample_template) if fp_rsp['status'] != 'success': # Unknown filepath, so return the error message return fp_rsp fp_rsp = fp_rsp['file'] # Define here the message and message level in case of success msg = '' status = 'success' is_mapping_file = looks_like_qiime_mapping_file(fp_rsp) if is_mapping_file and not data_type: return {'status': 'error', 'message': 'Please, choose a data type if uploading a ' 'QIIME mapping file', 'file': sample_template} study = Study(int(study_id)) # Offload the creation of the sample template to the cluster job_id = safe_submit(user_id, create_sample_template, fp_rsp, study, is_mapping_file, data_type) # Store the job id attaching it to the sample template id r_client.set(SAMPLE_TEMPLATE_KEY_FORMAT % study.id, dumps({'job_id': job_id})) return {'status': status, 'message': msg, 'file': sample_template}
def sample_template_put_req(study_id, user_id, sample_template): """Updates a sample template using the given file Parameters ---------- study_id : int The current study object id user_id : str The current user object id sample_template : str filename to use for updating Returns ------- dict results dictonary in the format {'status': status, 'message': msg, 'file': sample_template} status can be success, warning, or error depending on result message has the warnings or errors file has the file name """ exists = _check_sample_template_exists(int(study_id)) if exists['status'] != 'success': return exists access_error = check_access(int(study_id), user_id) if access_error: return access_error fp_rsp = check_fp(study_id, sample_template) if fp_rsp['status'] != 'success': # Unknown filepath, so return the error message return fp_rsp fp_rsp = fp_rsp['file'] msg = '' status = 'success' # Offload the update of the sample template to the cluster qiita_plugin = Software.from_name_and_version('Qiita', 'alpha') cmd = qiita_plugin.get_command('update_sample_template') params = Parameters.load(cmd, values_dict={ 'study': int(study_id), 'template_fp': fp_rsp }) job = ProcessingJob.create(User(user_id), params) # Store the job id attaching it to the sample template id r_client.set(SAMPLE_TEMPLATE_KEY_FORMAT % study_id, dumps({'job_id': job.id})) job.submit() return {'status': status, 'message': msg, 'file': sample_template}
def sample_template_post_req(study_id, user_id, data_type, sample_template): """Creates the sample template from the given file Parameters ---------- study_id : int The current study object id user_id : str The current user object id data_type : str Data type for the sample template sample_template : str filename to use for creation Returns ------- dict results dictonary in the format {'status': status, 'message': msg, 'file': sample_template} status can be success, warning, or error depending on result message has the warnings or errors file has the file name """ access_error = check_access(int(study_id), user_id) if access_error: return access_error fp_rsp = check_fp(study_id, sample_template) if fp_rsp['status'] != 'success': # Unknown filepath, so return the error message return fp_rsp fp_rsp = fp_rsp['file'] # Define here the message and message level in case of success msg = '' status = 'success' is_mapping_file = looks_like_qiime_mapping_file(fp_rsp) if is_mapping_file and not data_type: return { 'status': 'error', 'message': 'Please, choose a data type if uploading a ' 'QIIME mapping file', 'file': sample_template } study = Study(int(study_id)) # Offload the creation of the sample template to the cluster job_id = safe_submit(user_id, create_sample_template, fp_rsp, study, is_mapping_file, data_type) # Store the job id attaching it to the sample template id r_client.set(SAMPLE_TEMPLATE_KEY_FORMAT % study.id, dumps({'job_id': job_id})) return {'status': status, 'message': msg, 'file': sample_template}
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 sample_template_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 == 'remove': req_path = [v for v in req_path.split('/') if v] if len(req_path) != 3: return {'status': 'error', 'message': 'Incorrect path parameter'} st_id = req_path[0] attribute = req_path[1] attr_id = req_path[2] # Check if the user actually has access to the template st = SampleTemplate(st_id) access_error = check_access(st.study_id, user_id) if access_error: return access_error # Offload the deletion of the sample or column to the cluster job_id = safe_submit(user_id, delete_sample_or_column, SampleTemplate, int(st_id), attribute, attr_id) # Store the job id attaching it to the sample template id r_client.set(SAMPLE_TEMPLATE_KEY_FORMAT % st_id, dumps({'job_id': job_id})) return {'status': 'success', 'message': ''} else: return {'status': 'error', 'message': 'Operation "%s" not supported. ' 'Current supported operations: remove' % req_op}
def study_tags_patch_request(user_id, study_id, req_op, req_path, req_value=None, req_from=None): """Modifies an attribute of the artifact Parameters ---------- user_id : int The id of the user performing the patch operation study_id : int The id of the study on which we will be performing the patch operation req_op : str The operation to perform on the study req_path : str The 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) != 1: return {'status': 'error', 'message': 'Incorrect path parameter'} attribute = req_path[0] # Check if the user actually has access to the study access_error = check_access(study_id, user_id) if access_error: return access_error study = Study(study_id) if attribute == 'tags': message = study.update_tags(User(user_id), req_value) return {'status': 'success', 'message': 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 study_prep_get_req(study_id, user_id): """Gives a summary of each prep template attached to the study Parameters ---------- study_id : int Study id to get prep template info for user_id : str User id requesting the prep templates Returns ------- dict of list of dict prep template information seperated by data type, in the form {data_type: [{prep 1 info dict}, ....], ...} """ access_error = check_access(study_id, user_id) if access_error: return access_error # Can only pass ids over API, so need to instantiate object study = Study(int(study_id)) prep_info = defaultdict(list) editable = study.can_edit(User(user_id)) for dtype in study.data_types: for prep in study.prep_templates(dtype): if prep.status != 'public' and not editable: continue start_artifact = prep.artifact info = { 'name': 'PREP %d NAME' % prep.id, 'id': prep.id, 'status': prep.status, } if start_artifact is not None: youngest_artifact = prep.artifact.youngest_artifact info['start_artifact'] = start_artifact.artifact_type info['start_artifact_id'] = start_artifact.id info['youngest_artifact'] = '%s - %s' % ( youngest_artifact.name, youngest_artifact.artifact_type) info['ebi_experiment'] = bool( [v for _, v in viewitems(prep.ebi_experiment_accessions) if v is not None]) else: info['start_artifact'] = None info['start_artifact_id'] = None info['youngest_artifact'] = None info['ebi_experiment'] = False prep_info[dtype].append(info) return {'status': 'success', 'message': '', 'info': prep_info}
def prep_template_graph_get_req(prep_id, user_id): """Returns graph of all artifacts created from the prep base artifact Parameters ---------- prep_id : int Prep template ID to get graph for user_id : str User making the request Returns ------- dict of lists of tuples A dictionary containing the edge list representation of the graph, and the node labels. Formatted as: {'status': status, 'message': message, 'edge_list': [(0, 1), (0, 2)...], 'node_labels': [(0, 'label0'), (1, 'label1'), ...]} Notes ----- Nodes are identified by the corresponding Artifact ID. """ exists = _check_prep_template_exists(int(prep_id)) if exists['status'] != 'success': return exists prep = PrepTemplate(int(prep_id)) access_error = check_access(prep.study_id, user_id) if access_error: return access_error # We should filter for only the public artifacts if the user # doesn't have full access to the study full_access = Study(prep.study_id).can_edit(User(user_id)) artifact = prep.artifact if artifact is None: return {'edges': [], 'nodes': [], 'status': 'success', 'message': ''} G = artifact.descendants_with_jobs nodes, edges, wf_id = get_network_nodes_edges(G, full_access) return {'edges': edges, 'nodes': nodes, 'workflow': wf_id, 'status': 'success', 'message': ''}
def sample_template_get_req(samp_id, user_id): """Gets the json of the full sample template Parameters ---------- samp_id : int or int castable string SampleTemplate id to get info for user_id : str User requesting the sample template info Returns ------- dict of objects {'status': status, 'message': msg, 'template': dict of {str: {str: object, ...}, ...} template is dictionary where the keys access_error the metadata samples and the values are a dictionary of column and value. Format {sample: {column: value, ...}, ...} """ exists = _check_sample_template_exists(int(samp_id)) if exists['status'] != 'success': return exists access_error = check_access(int(samp_id), user_id) if access_error: return access_error template = SampleTemplate(int(samp_id)) access_error = check_access(template.study_id, user_id) if access_error: return access_error df = template.to_dataframe() return { 'status': 'success', 'message': '', 'template': df.to_dict(orient='index') }
def sample_template_put_req(study_id, user_id, sample_template): """Updates a sample template using the given file Parameters ---------- study_id : int The current study object id user_id : str The current user object id sample_template : str filename to use for updating Returns ------- dict results dictonary in the format {'status': status, 'message': msg, 'file': sample_template} status can be success, warning, or error depending on result message has the warnings or errors file has the file name """ exists = _check_sample_template_exists(int(study_id)) if exists['status'] != 'success': return exists access_error = check_access(int(study_id), user_id) if access_error: return access_error fp_rsp = check_fp(study_id, sample_template) if fp_rsp['status'] != 'success': # Unknown filepath, so return the error message return fp_rsp fp_rsp = fp_rsp['file'] msg = '' status = 'success' # Offload the update of the sample template to the cluster job_id = safe_submit(user_id, update_sample_template, int(study_id), fp_rsp) # Store the job id attaching it to the sample template id r_client.set(SAMPLE_TEMPLATE_KEY_FORMAT % study_id, dumps({'job_id': job_id})) return {'status': status, 'message': msg, 'file': sample_template}
def prep_template_graph_get_req(prep_id, user_id): """Returns graph of all artifacts created from the prep base artifact Parameters ---------- prep_id : int Prep template ID to get graph for user_id : str User making the request Returns ------- dict of lists of tuples A dictionary containing the edge list representation of the graph, and the node labels. Formatted as: {'status': status, 'message': message, 'edge_list': [(0, 1), (0, 2)...], 'node_labels': [(0, 'label0'), (1, 'label1'), ...]} Notes ----- Nodes are identified by the corresponding Artifact ID. """ exists = _check_prep_template_exists(int(prep_id)) if exists['status'] != 'success': return exists prep = PrepTemplate(int(prep_id)) access_error = check_access(prep.study_id, user_id) if access_error: return access_error # We should filter for only the public artifacts if the user # doesn't have full access to the study full_access = Study(prep.study_id).can_edit(User(user_id)) G = prep.artifact.descendants node_labels = [(n.id, ' - '.join([n.name, n.artifact_type])) for n in G.nodes() if full_access or n.visibility == 'public'] node_ids = [id_ for id_, label in node_labels] edge_list = [(n.id, m.id) for n, m in G.edges() if n.id in node_ids and m.id in node_ids] return { 'status': 'success', 'message': '', 'edge_list': edge_list, 'node_labels': node_labels }
def artifact_status_put_req(artifact_id, user_id, visibility): """Set the status of the artifact given Parameters ---------- artifact_id : int Artifact being acted on user_id : str The user requesting the action visibility : {'sandbox', 'awaiting_approval', 'private', 'public'} What to change the visibility to Returns ------- dict Status of action, in the form {'status': status, 'message': msg} status: status of the action, either success or error message: Human readable message for status """ if visibility not in get_visibilities(): return {'status': 'error', 'message': 'Unknown visibility value: %s' % visibility} pd = Artifact(int(artifact_id)) sid = pd.study.id access_error = check_access(sid, user_id) if access_error: return access_error user = User(str(user_id)) status = 'success' msg = 'Artifact visibility changed to %s' % visibility # Set the approval to private if needs approval and admin if visibility == 'private': if not qiita_config.require_approval: pd.visibility = 'private' # Set the approval to private if approval not required elif user.level == 'admin': pd.visibility = 'private' # Trying to set approval without admin privileges else: status = 'error' msg = 'User does not have permissions to approve change' else: pd.visibility = visibility LogEntry.create('Warning', '%s changed artifact %s (study %d) to %s' % ( user_id, artifact_id, sid, visibility)) return {'status': status, 'message': msg}
def sample_template_get_req(samp_id, user_id): """Gets the json of the full sample template Parameters ---------- samp_id : int or int castable string SampleTemplate id to get info for user_id : str User requesting the sample template info Returns ------- dict of objects {'status': status, 'message': msg, 'template': dict of {str: {str: object, ...}, ...} template is dictionary where the keys access_error the metadata samples and the values are a dictionary of column and value. Format {sample: {column: value, ...}, ...} """ exists = _check_sample_template_exists(int(samp_id)) if exists['status'] != 'success': return exists access_error = check_access(int(samp_id), user_id) if access_error: return access_error template = SampleTemplate(int(samp_id)) access_error = check_access(template.study_id, user_id) if access_error: return access_error df = template.to_dataframe() return {'status': 'success', 'message': '', 'template': df.to_dict(orient='index')}
def prep_template_graph_get_req(prep_id, user_id): """Returns graph of all artifacts created from the prep base artifact Parameters ---------- prep_id : int Prep template ID to get graph for user_id : str User making the request Returns ------- dict of lists of tuples A dictionary containing the edge list representation of the graph, and the node labels. Formatted as: {'status': status, 'message': message, 'edge_list': [(0, 1), (0, 2)...], 'node_labels': [(0, 'label0'), (1, 'label1'), ...]} Notes ----- Nodes are identified by the corresponding Artifact ID. """ exists = _check_prep_template_exists(int(prep_id)) if exists['status'] != 'success': return exists prep = PrepTemplate(int(prep_id)) access_error = check_access(prep.study_id, user_id) if access_error: return access_error # We should filter for only the public artifacts if the user # doesn't have full access to the study full_access = Study(prep.study_id).can_edit(User(user_id)) G = prep.artifact.descendants node_labels = [(n.id, ' - '.join([n.name, n.artifact_type])) for n in G.nodes() if full_access or n.visibility == 'public'] node_ids = [id_ for id_, label in node_labels] edge_list = [(n.id, m.id) for n, m in G.edges() if n.id in node_ids and m.id in node_ids] return {'status': 'success', 'message': '', 'edge_list': edge_list, 'node_labels': node_labels}
def prep_template_summary_get_req(prep_id, user_id): """Get the summarized prep template data for each metadata column Parameters ---------- prep_id : int PrepTemplate id to get info for user_id : str User requesting the sample template info Returns ------- dict of objects Dictionary object where the keys are the metadata categories and the values are list of tuples. Each tuple is an observed value in the category and the number of times its seen. Format {'status': status, 'message': message, 'num_samples': value, 'category': [(val1, count1), (val2, count2), ...], 'editable': bool} """ exists = _check_prep_template_exists(int(prep_id)) if exists['status'] != 'success': return exists prep = PrepTemplate(int(prep_id)) access_error = check_access(prep.study_id, user_id) if access_error: return access_error editable = Study(prep.study_id).can_edit(User(user_id)) df = prep.to_dataframe() out = { 'num_samples': df.shape[0], 'summary': [], 'status': 'success', 'message': '', 'editable': editable } cols = sorted(list(df.columns)) for column in cols: counts = df[column].value_counts(dropna=False) out['summary'].append( (str(column), [(str(key), counts[key]) for key in natsorted(counts.index)])) return out
def artifact_get_req(user_id, artifact_id): """Returns all base information about an artifact Parameters ---------- user_id : str user making the request artifact_id : int or str coercable to int Atrtifact to get information for Returns ------- dict of objects A dictionary containing the artifact information {'status': status, 'message': message, 'artifact': {info key: val, ...}} """ artifact_id = int(artifact_id) artifact = Artifact(artifact_id) access_error = check_access(artifact.study.id, user_id) if access_error: return access_error can_submit_ebi = artifact.can_be_submitted_to_ebi ebi_run_accessions = (artifact.ebi_run_accessions if can_submit_ebi else None) can_submit_vamps = artifact.can_be_submitted_to_vamps is_submitted_vamps = (artifact.is_submitted_to_vamps if can_submit_vamps else False) return { 'id': artifact_id, 'timestamp': artifact.timestamp, 'processing_parameters': artifact.processing_parameters, 'visibility': artifact.visibility, 'type': artifact.artifact_type, 'data_type': artifact.data_type, 'filepaths': artifact.filepaths, 'parents': [a.id for a in artifact.parents], 'study': artifact.study.id if artifact.study else None, 'can_submit_ebi': can_submit_ebi, 'ebi_run_accessions': ebi_run_accessions, 'can_submit_vamps': can_submit_vamps, 'is_submitted_vamps': is_submitted_vamps }
def artifact_summary_post_request(user_id, artifact_id): """Launches the HTML summary generation and returns the job information Parameters ---------- user_id : str The user making the request artifact_id : int or str The artifact id Returns ------- dict of objects A dictionary containing the artifact summary information {'status': str, 'message': str, 'job': list of [str, str, str]} """ artifact_id = int(artifact_id) artifact = Artifact(artifact_id) access_error = check_access(artifact.study.id, user_id) if access_error: return access_error # Check if the summary is being generated or has been already generated command = Command.get_html_generator(artifact.artifact_type) jobs = artifact.jobs(cmd=command) jobs = [j for j in jobs if j.status in ['queued', 'running', 'success']] if jobs: # The HTML summary is either being generated or already generated. # Return the information of that job so we only generate the HTML # once job = jobs[0] else: # Create a new job to generate the HTML summary and return the newly # created job information job = ProcessingJob.create( User(user_id), Parameters.load(command, values_dict={'input_data': artifact_id})) job.submit() return { 'status': 'success', 'message': '', 'job': [job.id, job.status, job.step] }
def artifact_graph_get_req(artifact_id, direction, user_id): """Creates graphs of ancestor or descendant artifacts from given one Parameters ---------- artifact_id : int Artifact ID to get graph for direction : {'ancestors', 'descendants'} What direction to get the graph in Returns ------- dict of lists of tuples A dictionary containing the edge list representation of the graph, and the node labels. Formatted as: {'status': status, 'message': message, 'edge_list': [(0, 1), (0, 2)...], 'node_labels': [(0, 'label0'), (1, 'label1'), ...]} Notes ----- Nodes are identified by the corresponding Artifact ID. """ access_error = check_access(Artifact(artifact_id).study.id, user_id) if access_error: return access_error if direction == 'descendants': G = Artifact(int(artifact_id)).descendants elif direction == 'ancestors': G = Artifact(int(artifact_id)).ancestors else: return { 'status': 'error', 'message': 'Unknown directon %s' % direction } node_labels = [(n.id, ' - '.join([n.name, n.artifact_type])) for n in G.nodes()] return { 'edge_list': [(n.id, m.id) for n, m in G.edges()], 'node_labels': node_labels, 'status': 'success', 'message': '' }
def prep_template_summary_get_req(prep_id, user_id): """Get the summarized prep template data for each metadata column Parameters ---------- prep_id : int PrepTemplate id to get info for user_id : str User requesting the sample template info Returns ------- dict of objects Dictionary object where the keys are the metadata categories and the values are list of tuples. Each tuple is an observed value in the category and the number of times its seen. Format {'status': status, 'message': message, 'num_samples': value, 'category': [(val1, count1), (val2, count2), ...], 'editable': bool} """ exists = _check_prep_template_exists(int(prep_id)) if exists['status'] != 'success': return exists prep = PrepTemplate(int(prep_id)) access_error = check_access(prep.study_id, user_id) if access_error: return access_error editable = Study(prep.study_id).can_edit(User(user_id)) df = prep.to_dataframe() out = {'num_samples': df.shape[0], 'summary': [], 'status': 'success', 'message': '', 'editable': editable} cols = sorted(list(df.columns)) for column in cols: counts = df[column].value_counts() out['summary'].append( (str(column), [(str(key), counts[key]) for key in natsorted(counts.index)])) return out
def artifact_status_put_req(artifact_id, user_id, visibility): """Set the status of the artifact given Parameters ---------- artifact_id : int Artifact being acted on user_id : str The user requesting the action visibility : {'sandbox', 'awaiting_approval', 'private', 'public'} What to change the visibility to Returns ------- dict Status of action, in the form {'status': status, 'message': msg} status: status of the action, either success or error message: Human readable message for status """ if visibility not in get_visibilities(): return {'status': 'error', 'message': 'Unknown visiblity value: %s' % visibility} pd = Artifact(int(artifact_id)) access_error = check_access(pd.study.id, user_id) if access_error: return access_error user = User(str(user_id)) status = 'success' msg = 'Artifact visibility changed to %s' % visibility # Set the approval to private if needs approval and admin if visibility == 'private': if not qiita_config.require_approval: pd.visibility = 'private' # Set the approval to private if approval not required elif user.level == 'admin': pd.visibility = 'private' # Trying to set approval without admin privileges else: status = 'error' msg = 'User does not have permissions to approve change' else: pd.visibility = visibility return {'status': status, 'message': msg}
def artifact_summary_post_request(user_id, artifact_id): """Launches the HTML summary generation and returns the job information Parameters ---------- user_id : str The user making the request artifact_id : int or str The artifact id Returns ------- dict of objects A dictionary containing the artifact summary information {'status': str, 'message': str, 'job': list of [str, str, str]} """ artifact_id = int(artifact_id) artifact = Artifact(artifact_id) access_error = check_access(artifact.study.id, user_id) if access_error: return access_error # Check if the summary is being generated or has been already generated command = Command.get_html_generator(artifact.artifact_type) jobs = artifact.jobs(cmd=command) jobs = [j for j in jobs if j.status in ['queued', 'running', 'success']] if jobs: # The HTML summary is either being generated or already generated. # Return the information of that job so we only generate the HTML # once job = jobs[0] else: # Create a new job to generate the HTML summary and return the newly # created job information job = ProcessingJob.create( User(user_id), Parameters.load(command, values_dict={'input_data': artifact_id})) job.submit() return {'status': 'success', 'message': '', 'job': [job.id, job.status, job.step]}
def artifact_get_req(user_id, artifact_id): """Returns all base information about an artifact Parameters ---------- user_id : str user making the request artifact_id : int or str coercable to int Atrtifact to get information for Returns ------- dict of objects A dictionary containing the artifact information {'status': status, 'message': message, 'artifact': {info key: val, ...}} """ artifact_id = int(artifact_id) artifact = Artifact(artifact_id) access_error = check_access(artifact.study.id, user_id) if access_error: return access_error can_submit_ebi = artifact.can_be_submitted_to_ebi ebi_run_accessions = (artifact.ebi_run_accessions if can_submit_ebi else None) can_submit_vamps = artifact.can_be_submitted_to_vamps is_submitted_vamps = (artifact.is_submitted_to_vamps if can_submit_vamps else False) return {'id': artifact_id, 'timestamp': artifact.timestamp, 'processing_parameters': artifact.processing_parameters, 'visibility': artifact.visibility, 'type': artifact.artifact_type, 'data_type': artifact.data_type, 'filepaths': artifact.filepaths, 'parents': [a.id for a in artifact.parents], 'study': artifact.study.id if artifact.study else None, 'can_submit_ebi': can_submit_ebi, 'ebi_run_accessions': ebi_run_accessions, 'can_submit_vamps': can_submit_vamps, 'is_submitted_vamps': is_submitted_vamps}
def artifact_graph_get_req(artifact_id, direction, user_id): """Creates graphs of ancestor or descendant artifacts from given one Parameters ---------- artifact_id : int Artifact ID to get graph for direction : {'ancestors', 'descendants'} What direction to get the graph in Returns ------- dict of lists of tuples A dictionary containing the edge list representation of the graph, and the node labels. Formatted as: {'status': status, 'message': message, 'edge_list': [(0, 1), (0, 2)...], 'node_labels': [(0, 'label0'), (1, 'label1'), ...]} Notes ----- Nodes are identified by the corresponding Artifact ID. """ access_error = check_access(Artifact(artifact_id).study.id, user_id) if access_error: return access_error if direction == 'descendants': G = Artifact(int(artifact_id)).descendants elif direction == 'ancestors': G = Artifact(int(artifact_id)).ancestors else: return { 'status': 'error', 'message': 'Unknown directon %s' % direction } node_labels = [(n.id, ' - '.join([n.name, n.artifact_type])) for n in G.nodes()] return {'edge_list': [(n.id, m.id) for n, m in G.edges()], 'node_labels': node_labels, 'status': 'success', 'message': ''}
def sample_template_filepaths_get_req(study_id, user_id): """Returns all the filepaths attached to the sample template Parameters ---------- study_id : int The current study object id user_id : str The current user object id Returns ------- dict Filepaths in the form {'status': status, 'message': msg, 'filepaths': filepaths} status can be success, warning, or error depending on result message has the warnings or errors filepaths is a list of tuple of int and str All files in the sample template, as [(id, URL), ...] """ exists = _check_sample_template_exists(int(study_id)) if exists['status'] != 'success': return exists access_error = check_access(study_id, user_id) if access_error: return access_error try: template = SampleTemplate(int(study_id)) except QiitaDBUnknownIDError as e: return {'status': 'error', 'message': str(e)} return {'status': 'success', 'message': '', 'filepaths': template.get_filepaths() }
def sample_template_category_get_req(category, samp_id, user_id): """Returns dict of values for each sample in the given category Parameters ---------- category : str Metadata category to get values for samp_id : int or str typecastable to int SampleTemplate id to get info for user_id : str User requesting the sample template info Returns ------- dict Returns information in the form {'status': str, 'message': str, 'values': dict of {str: object}} """ exists = _check_sample_template_exists(int(samp_id)) if exists['status'] != 'success': return exists access_error = check_access(samp_id, user_id) if access_error: return access_error st = SampleTemplate(int(samp_id)) try: values = st.get_category(category) except QiitaDBColumnError: return {'status': 'error', 'message': 'Category %s does not exist in sample template' % category} return {'status': 'success', 'message': '', 'values': values}