-
Notifications
You must be signed in to change notification settings - Fork 79
RESTful sample status #3139
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
RESTful sample status #3139
Changes from 3 commits
000a5d1
510518e
b45a0c4
119d4ed
67b4fd6
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 |
|---|---|---|
|
|
@@ -5,13 +5,107 @@ | |
| # | ||
| # The full license is in the file LICENSE, distributed with this software. | ||
| # ----------------------------------------------------------------------------- | ||
| from collections import defaultdict | ||
|
|
||
| from tornado.escape import json_encode, json_decode | ||
| import pandas as pd | ||
|
|
||
| from qiita_db.handlers.oauth2 import authenticate_oauth | ||
| from .rest_handler import RESTHandler | ||
|
|
||
|
|
||
| def _sample_details(study, samples): | ||
| def detail_maker(**kwargs): | ||
| base = {'sample_id': None, | ||
| 'sample_found': False, | ||
| 'ebi_sample_accession': None, | ||
| 'preparation_id': None, | ||
| 'ebi_experiment_accession': None, | ||
| 'preparation_visibility': None, | ||
| 'preparation_type': None} | ||
|
|
||
| assert set(kwargs).issubset(set(base)), "Unexpected key to set" | ||
|
|
||
| base.update(kwargs) | ||
| return base | ||
|
|
||
| # cache sample detail for lookup | ||
| study_samples = set(study.sample_template.keys()) | ||
| sample_accessions = study.sample_template.ebi_sample_accessions | ||
|
Member
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. Note that ebi_sample_accessions will return all available samples with Nones where there is no accession; in other words, len(sample_accessions) == len(study_samples)
Contributor
Author
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 dont think that impacts the subsequent use |
||
|
|
||
| # cache preparation information that we'll need | ||
|
|
||
| # map of {sample_id: [indices, of, light, prep, info, ...]} | ||
| sample_prep_mapping = defaultdict(list) | ||
| pt_light = [] | ||
| for idx, pt in enumerate(study.prep_templates()): | ||
| pt_light.append((pt.id, pt.ebi_experiment_accessions, | ||
| pt.status, pt.data_type())) | ||
|
|
||
| for ptsample in pt.keys(): | ||
wasade marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| sample_prep_mapping[ptsample].append(idx) | ||
|
|
||
|
||
| details = [] | ||
| for sample in samples: | ||
| if sample in study_samples: | ||
| # if the sample exists | ||
| sample_acc = sample_accessions.get(sample) | ||
|
|
||
| if sample in sample_prep_mapping: | ||
| # if the sample is present in any prep, pull out the detail | ||
| # specific to those preparations | ||
| for pt_idx in sample_prep_mapping[sample]: | ||
| ptid, ptacc, ptstatus, ptdtype = pt_light[pt_idx] | ||
|
|
||
| details.append(detail_maker( | ||
| sample_id=sample, | ||
| sample_found=True, | ||
| ebi_sample_accession=sample_acc, | ||
| preparation_id=ptid, | ||
| ebi_experiment_accession=ptacc.get(sample), | ||
| preparation_visibility=ptstatus, | ||
| preparation_type=ptdtype)) | ||
| else: | ||
| # the sample is not present on any preparations | ||
| details.append(detail_maker( | ||
| sample_id=sample, | ||
| sample_found=True, | ||
|
|
||
| # it would be weird to have an EBI sample accession | ||
| # but not be present on a preparation...? | ||
| ebi_sample_accession=sample_acc)) | ||
| else: | ||
| # the is not present, let's note and move ona | ||
| details.append(detail_maker(sample_id=sample)) | ||
|
|
||
| return details | ||
|
|
||
|
|
||
| class StudySampleDetailHandler(RESTHandler): | ||
| @authenticate_oauth | ||
| def get(self, study_id, sample_id): | ||
| study = self.safe_get_study(study_id) | ||
| sample_detail = _sample_details(study, [sample_id, ]) | ||
| self.write(json_encode(sample_detail)) | ||
| self.finish() | ||
|
|
||
|
|
||
| class StudySamplesDetailHandler(RESTHandler): | ||
| @authenticate_oauth | ||
| def post(self, study_id): | ||
| samples = json_decode(self.request.body) | ||
|
|
||
| if 'sample_ids' not in samples: | ||
| self.fail('Missing sample_id key', 400) | ||
| return | ||
|
|
||
| study = self.safe_get_study(study_id) | ||
| samples_detail = _sample_details(study, samples['sample_ids']) | ||
|
|
||
| self.write(json_encode(samples_detail)) | ||
| self.finish() | ||
|
|
||
|
|
||
| class StudySamplesHandler(RESTHandler): | ||
|
|
||
| @authenticate_oauth | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,141 @@ | ||
| # ----------------------------------------------------------------------------- | ||
| # Copyright (c) 2014--, The Qiita Development Team. | ||
| # | ||
| # Distributed under the terms of the BSD 3-clause License. | ||
| # | ||
| # The full license is in the file LICENSE, distributed with this software. | ||
| # ----------------------------------------------------------------------------- | ||
|
|
||
| from unittest import main, TestCase | ||
|
|
||
| from tornado.escape import json_decode | ||
|
|
||
| import qiita_db | ||
|
|
||
| from qiita_pet.test.rest.test_base import RESTHandlerTestCase | ||
| from qiita_pet.handlers.rest.study_samples import _sample_details | ||
|
|
||
|
|
||
| class SupportTests(TestCase): | ||
| def test_samples_detail(self): | ||
| exp = [{'sample_id': '1.SKD7.640191', | ||
| 'sample_found': True, | ||
| 'ebi_sample_accession': 'ERS000021', | ||
| 'preparation_id': 1, | ||
| 'ebi_experiment_accession': 'ERX0000021', | ||
| 'preparation_visibility': 'private', | ||
| 'preparation_type': '18S'}, | ||
| {'sample_id': '1.SKD7.640191', | ||
| 'sample_found': True, | ||
| 'ebi_sample_accession': 'ERS000021', | ||
| 'preparation_id': 2, | ||
| 'ebi_experiment_accession': 'ERX0000021', | ||
| 'preparation_visibility': 'private', | ||
| 'preparation_type': '18S'}, | ||
| {'sample_id': 'doesnotexist', | ||
| 'sample_found': False, | ||
| 'ebi_sample_accession': None, | ||
| 'preparation_id': None, | ||
| 'ebi_experiment_accession': None, | ||
| 'preparation_visibility': None, | ||
| 'preparation_type': None}] | ||
| obs = _sample_details(qiita_db.study.Study(1), | ||
| ['1.SKD7.640191', 'doesnotexist']) | ||
| self.assertEqual(len(obs), len(exp)) | ||
| self.assertEqual(obs, exp) | ||
|
|
||
|
|
||
| class SampleDetailHandlerTests(RESTHandlerTestCase): | ||
| def test_get_missing_sample(self): | ||
| exp = [{'sample_id': 'doesnotexist', | ||
| 'sample_found': False, | ||
| 'ebi_sample_accession': None, | ||
| 'preparation_id': None, | ||
| 'ebi_experiment_accession': None, | ||
| 'preparation_visibility': None, | ||
| 'preparation_type': None}, ] | ||
|
|
||
| response = self.get('/api/v1/study/1/sample/doesnotexist/status', | ||
| headers=self.headers) | ||
| self.assertEqual(response.code, 200) | ||
| obs = json_decode(response.body) | ||
| self.assertEqual(obs, exp) | ||
|
|
||
| def test_get_valid_sample(self): | ||
| exp = [{'sample_id': '1.SKD7.640191', | ||
| 'sample_found': True, | ||
| 'ebi_sample_accession': 'ERS000021', | ||
| 'preparation_id': 1, | ||
| 'ebi_experiment_accession': 'ERX0000021', | ||
| 'preparation_visibility': 'private', | ||
| 'preparation_type': '18S'}, | ||
| {'sample_id': '1.SKD7.640191', | ||
| 'sample_found': True, | ||
| 'ebi_sample_accession': 'ERS000021', | ||
| 'preparation_id': 2, | ||
| 'ebi_experiment_accession': 'ERX0000021', | ||
| 'preparation_visibility': 'private', | ||
| 'preparation_type': '18S'}] | ||
|
|
||
| response = self.get('/api/v1/study/1/sample/1.SKD7.640191/status', | ||
| headers=self.headers) | ||
| self.assertEqual(response.code, 200) | ||
| obs = json_decode(response.body) | ||
| self.assertEqual(obs, exp) | ||
|
|
||
| def test_post_samples_status_bad_request(self): | ||
| body = {'malformed': 'with garbage'} | ||
| response = self.post('/api/v1/study/1/samples/status', | ||
| headers=self.headers, | ||
| data=body, asjson=True) | ||
| self.assertEqual(response.code, 400) | ||
|
|
||
| def test_post_samples_status(self): | ||
| exp = [{'sample_id': '1.SKD7.640191', | ||
| 'sample_found': True, | ||
| 'ebi_sample_accession': 'ERS000021', | ||
| 'preparation_id': 1, | ||
| 'ebi_experiment_accession': 'ERX0000021', | ||
| 'preparation_visibility': 'private', | ||
| 'preparation_type': '18S'}, | ||
| {'sample_id': '1.SKD7.640191', | ||
| 'sample_found': True, | ||
| 'ebi_sample_accession': 'ERS000021', | ||
| 'preparation_id': 2, | ||
| 'ebi_experiment_accession': 'ERX0000021', | ||
| 'preparation_visibility': 'private', | ||
| 'preparation_type': '18S'}, | ||
| {'sample_id': 'doesnotexist', | ||
| 'sample_found': False, | ||
| 'ebi_sample_accession': None, | ||
| 'preparation_id': None, | ||
| 'ebi_experiment_accession': None, | ||
| 'preparation_visibility': None, | ||
| 'preparation_type': None}, | ||
| {'sample_id': '1.SKM5.640177', | ||
| 'sample_found': True, | ||
| 'ebi_sample_accession': 'ERS000005', | ||
| 'preparation_id': 1, | ||
| 'ebi_experiment_accession': 'ERX0000005', | ||
| 'preparation_visibility': 'private', | ||
| 'preparation_type': '18S'}, | ||
| {'sample_id': '1.SKM5.640177', | ||
| 'sample_found': True, | ||
| 'ebi_sample_accession': 'ERS000005', | ||
| 'preparation_id': 2, | ||
| 'ebi_experiment_accession': 'ERX0000005', | ||
| 'preparation_visibility': 'private', | ||
| 'preparation_type': '18S'}] | ||
|
|
||
| body = {'sample_ids': ['1.SKD7.640191', 'doesnotexist', | ||
| '1.SKM5.640177']} | ||
| response = self.post('/api/v1/study/1/samples/status', | ||
| headers=self.headers, | ||
| data=body, asjson=True) | ||
| self.assertEqual(response.code, 200) | ||
| obs = json_decode(response.body) | ||
| self.assertEqual(obs, exp) | ||
|
|
||
|
|
||
| if __name__ == '__main__': | ||
| main() |
Uh oh!
There was an error while loading. Please reload this page.