diff --git a/knimin/handlers/ag_pulldown.py b/knimin/handlers/ag_pulldown.py index b6190c8..1d66b04 100644 --- a/knimin/handlers/ag_pulldown.py +++ b/knimin/handlers/ag_pulldown.py @@ -1,5 +1,7 @@ from tornado.web import authenticated from future.utils import viewitems +from StringIO import StringIO +import pandas as pd from knimin.handlers.base import BaseHandler from knimin import db @@ -13,17 +15,20 @@ class AGPulldownHandler(BaseHandler): def get(self): surveys = db.list_external_surveys() self.render("ag_pulldown.html", currentuser=self.current_user, - barcodes=[], surveys=surveys, errors='') + barcodes=[], surveys=surveys, errors='', + agsurveys=db.list_ag_surveys(), merged='False') @authenticated def post(self): # Do nothing if no file given if 'barcodes' not in self.request.files: surveys = db.list_external_surveys() + ags = db.list_ag_surveys(map(int, self.get_arguments('agsurveys'))) self.render("ag_pulldown.html", currentuser=self.current_user, barcodes='', blanks='', external='', surveys=surveys, errors="No barcode file given, thus nothing could " - "be pulled down.") + "be pulled down.", agsurveys=ags, + merged=self.get_argument('merged', default='False')) return # Get file information, ignoring commented out lines fileinfo = self.request.files['barcodes'][0]['body'] @@ -41,24 +46,27 @@ def post(self): else: external = '' surveys = db.list_external_surveys() + ags = db.list_ag_surveys(map(int, self.get_arguments('agsurveys'))) self.render("ag_pulldown.html", currentuser=self.current_user, barcodes=",".join(barcodes), blanks=",".join(blanks), - surveys=surveys, external=external, errors='') + surveys=surveys, external=external, errors='', + agsurveys=ags, + merged=self.get_argument('merged', default='False')) @set_access(['Metadata Pulldown']) class AGPulldownDLHandler(BaseHandler): @authenticated def post(self): - barcodes = self.get_argument('barcodes').split(',') - if self.get_argument('blanks'): - blanks = self.get_argument('blanks').split(',') - else: - blanks = [] - if self.get_argument('external'): - external = self.get_argument('external').split(',') - else: - external = [] + barcodes = listify(self.get_arguments('barcodes')) + blanks = listify(self.get_arguments('blanks')) + # query which surveys have been selected by the user + selected_ag_surveys = listify( + self.get_arguments('selected_ag_surveys')) + external = listify(self.get_arguments('external')) + + selected_ag_surveys = list(map(int, selected_ag_surveys)) + # Get metadata and create zip file metadata, failures = db.pulldown(barcodes, blanks, external) @@ -67,8 +75,43 @@ def post(self): failtext = ("The following barcodes were not retrieved " "for any survey:\n%s" % failed) meta_zip.append("failures.txt", failtext) + + # check database about what surveys are available + available_agsurveys = {} + for (_id, name, _) in db.list_ag_surveys(): + available_agsurveys[_id] = name.replace(' ', '_') + + results_as_pd = [] for survey, meta in viewitems(metadata): - meta_zip.append('survey_%s_md.txt' % survey, meta) + # only create files for those surveys that have been selected by + # the user. Note that ids from the DB are negative, in metadata + # they are positive! + # Currently, I (Stefan Janssen) don't have test data for external + # surveys, thus I don't know their 'survey' value. I expect it to + # be the name of the external survey. In order to not block their + # pulldown I check that a skipped survey ID must be in the set of + # all available surveys. + survey = -1 * survey + if (survey in selected_ag_surveys) or \ + (survey not in available_agsurveys): + meta_zip.append('survey_%s_md.txt' % + available_agsurveys[survey], meta) + # transform each survey into a pandas dataframe for later merge + # read all columns as string to avoid unintened conversions, + # like cutting leading zeros of barcodes + pd_meta = pd.read_csv(StringIO(meta), sep="\t", dtype=str) + # reset the index to barcodes = here sample_name + pd_meta.set_index('sample_name', inplace=True) + results_as_pd.append(pd_meta) + + # add the merged table of all selected surveys to the zip archive + if self.get_argument('merged', default='False') == 'True': + pd_all = pd.DataFrame() + if len(results_as_pd) > 0: + pd_all = pd.concat(results_as_pd, join='outer', axis=1) + meta_zip.append('surveys_merged_md.txt', + pd_all.to_csv(sep='\t', + index_label='sample_name')) # write out zip file self.add_header('Content-type', 'application/octet-stream') @@ -92,3 +135,22 @@ def get(self): except Exception as e: msg = 'ERROR: %s' % str(e) self.write(msg) + + +def listify(_list): + """ Returns a flat list of str for either list of str (unchanged) or a one- + element list of str - delimited by ',' - into a list of str. + + Parameters + ---------- + _list : [str] + Input list of str + + Returns + ------- + A list of str. If single element was a comma delimited str with x parts, + the list will contain those x elements.""" + if len(_list) == 1: + if ',' in _list[0]: + _list = _list[0].split(',') + return _list diff --git a/knimin/lib/data_access.py b/knimin/lib/data_access.py index 55f6f48..34a1bdc 100644 --- a/knimin/lib/data_access.py +++ b/knimin/lib/data_access.py @@ -1685,6 +1685,30 @@ def add_external_survey(self, survey, description, url): RETURNING external_survey_id""" return self._con.execute_fetchone(sql, [survey, description, url])[0] + def list_ag_surveys(self, selected=None): + """Returns the list of american gut survey names. + + Parameters + ---------- + selected : list of int + The returned list's third element indicates if a survey has been + "chosen". If selected is None, all surveys will be "chosen", + otherwise only surveys whose ID is in selected are "chosen". + + Returns + ------- + list of (int, str, bool) + first element is the group_order number + second element is the group name + third element is the information if user selected this survey for + pulldown. All surveys are selected if selected is None. + """ + sql = """SELECT group_order, american + FROM ag.survey_group + WHERE group_order < 0""" + return [(id_, name, (selected is None) or (id_ in selected)) + for [id_, name] in self._con.execute_fetchall(sql)] + def list_external_surveys(self): """Returns list of external survey names diff --git a/knimin/lib/mem_zip.py b/knimin/lib/mem_zip.py index 4c4b8a2..66a7fe1 100644 --- a/knimin/lib/mem_zip.py +++ b/knimin/lib/mem_zip.py @@ -61,6 +61,45 @@ def write_to_buffer(self): return self.in_memory_data.getvalue() +def extract_zip(input_zip): + """ Reads all files of a zip file from disk. + + A helper function to read in all files of a zip archive as strings and + return a dict of those strings where the keys are the filenames. + + Parameters + ---------- + input_zip : str + The filename of the archive. + + Returns + ------- + A dict of str: keys = filenames in archive, values = content of files + """ + + input_zip = zipfile.ZipFile(input_zip) + return {name: input_zip.read(name) for name in input_zip.namelist()} + + +def sneak_files(archive, len=1000): + """ Returns the first characters of each file in an zip archive. + + Parameters + ---------- + archive : dict{str : filename, str : filecontents} + The already extracted zip archive in form of a dict, where keys are + filenames and values are the content of the file. + len : int + Number of characters returned from each file. + Default: 1000. + + Returns + ------- + dict{str, str} where the first component is the filename and the second + the first characters of the file.""" + return map(lambda (k, v): {k: v[:len]}, archive.items()) + + if __name__ == "__main__": # Run a test imz = InMemoryZip() diff --git a/knimin/lib/tests/test_data_access.py b/knimin/lib/tests/test_data_access.py index 5f4a73e..a776937 100644 --- a/knimin/lib/tests/test_data_access.py +++ b/knimin/lib/tests/test_data_access.py @@ -272,55 +272,54 @@ def test_get_ag_barcode_details(self): obs = db.get_ag_barcode_details(['000018046']) ag_login_id = '0060a301-e5bf-6a4e-e050-8a800c5d49b7' exp = {'000018046': { - 'ag_kit_barcode_id': '0060a301-e5c1-6a4e-e050-8a800c5d49b7', - 'verification_email_sent': 'n', - 'pass_reset_code': None, - 'vioscreen_status': 3, - 'sample_barcode_file': '000018046.jpg', - 'environment_sampled': None, - 'supplied_kit_id': db.ut_get_supplied_kit_id(ag_login_id), - 'withdrawn': None, - 'kit_verified': 'y', - # 'city': 'REMOVED', - 'ag_kit_id': '0060a301-e5c0-6a4e-e050-8a800c5d49b7', - # 'zip': 'REMOVED', - 'ag_login_id': ag_login_id, - # 'state': 'REMOVED', - 'results_ready': 'Y', - 'moldy': 'N', - # The key 'registered_on' is a time stamp when the database is - # created. It is unique per deployment. - # 'registered_on': datetime.datetime(2016, 8, 17, 10, 47, 2, - # 713292), - # 'kit_password': ('$2a$10$2.6Y9HmBqUFmSvKCjWmBte70WF.zd3h4Vqb' - # 'hLMQK1xP67Aj3rei86'), - # 'deposited': False, - 'sample_date': datetime.date(2014, 8, 13), - # 'email': 'REMOVED', - 'print_results': False, - 'open_humans_token': None, - # 'elevation': 0.0, - 'refunded': None, - # 'other_text': 'REMOVED', - 'barcode': '000018046', - 'swabs_per_kit': 1L, - # 'kit_verification_code': '60260', - # 'latitude': 0.0, - 'cannot_geocode': None, - # 'address': 'REMOVED', - 'date_of_last_email': datetime.date(2014, 8, 15), - 'site_sampled': 'Stool', - # 'name': 'REMOVED', - 'sample_time': datetime.time(11, 15), - # 'notes': 'REMOVED', - 'overloaded': 'N', - # 'longitude': 0.0, - 'pass_reset_time': None, - # 'country': 'REMOVED', - 'survey_id': '084532330aca5885', - 'other': 'N', - 'sample_barcode_file_md5': None - }} + 'ag_kit_barcode_id': '0060a301-e5c1-6a4e-e050-8a800c5d49b7', + 'verification_email_sent': 'n', + 'pass_reset_code': None, + 'vioscreen_status': 3, + 'sample_barcode_file': '000018046.jpg', + 'environment_sampled': None, + 'supplied_kit_id': db.ut_get_supplied_kit_id(ag_login_id), + 'withdrawn': None, + 'kit_verified': 'y', + # 'city': 'REMOVED', + 'ag_kit_id': '0060a301-e5c0-6a4e-e050-8a800c5d49b7', + # 'zip': 'REMOVED', + 'ag_login_id': ag_login_id, + # 'state': 'REMOVED', + 'results_ready': 'Y', + 'moldy': 'N', + # The key 'registered_on' is a time stamp when the database is + # created. It is unique per deployment. + # 'registered_on': datetime.datetime(2016, 8, 17, 10, 47, 2, + # 713292), + # 'kit_password': ('$2a$10$2.6Y9HmBqUFmSvKCjWmBte70WF.zd3h4Vqb' + # 'hLMQK1xP67Aj3rei86'), + # 'deposited': False, + 'sample_date': datetime.date(2014, 8, 13), + # 'email': 'REMOVED', + 'print_results': False, + 'open_humans_token': None, + # 'elevation': 0.0, + 'refunded': None, + # 'other_text': 'REMOVED', + 'barcode': '000018046', + 'swabs_per_kit': 1L, + # 'kit_verification_code': '60260', + # 'latitude': 0.0, + 'cannot_geocode': None, + # 'address': 'REMOVED', + 'date_of_last_email': datetime.date(2014, 8, 15), + 'site_sampled': 'Stool', + # 'name': 'REMOVED', + 'sample_time': datetime.time(11, 15), + # 'notes': 'REMOVED', + 'overloaded': 'N', + # 'longitude': 0.0, + 'pass_reset_time': None, + # 'country': 'REMOVED', + 'survey_id': '084532330aca5885', + 'other': 'N', + 'sample_barcode_file_md5': None}} participant_names = db.ut_get_participant_names_from_ag_login_id( ag_login_id) for key in obs: @@ -329,6 +328,21 @@ def test_get_ag_barcode_details(self): self.assertEqual({k: obs[key][k] for k in exp[key]}, exp[key]) self.assertIn(obs[key]['participant_name'], participant_names) + def test_list_ag_surveys(self): + truth = [(-1, 'Personal Information', True), + (-2, 'Pet Information', True), + (-3, 'Fermented Foods', True), + (-4, 'Surfers', True), + (-5, 'Personal_Microbiome', True)] + self.assertItemsEqual(db.list_ag_surveys(), truth) + + truth = [(-1, 'Personal Information', False), + (-2, 'Pet Information', True), + (-3, 'Fermented Foods', False), + (-4, 'Surfers', True), + (-5, 'Personal_Microbiome', False)] + self.assertItemsEqual(db.list_ag_surveys([-2, -4]), truth) + if __name__ == "__main__": main() diff --git a/knimin/lib/tests/test_mem_zip.py b/knimin/lib/tests/test_mem_zip.py index 5f05978..eb19f6e 100644 --- a/knimin/lib/tests/test_mem_zip.py +++ b/knimin/lib/tests/test_mem_zip.py @@ -1,8 +1,9 @@ import unittest -from knimin.lib.mem_zip import InMemoryZip +from knimin.lib.mem_zip import InMemoryZip, extract_zip, sneak_files import zipfile import os import io +from os.path import join, dirname, realpath class TestInMemoryZip(unittest.TestCase): @@ -50,6 +51,33 @@ def test_write_to_buffer(self): res_contents = zhandle.read(self.test_fname) self.assertEqual(res_contents, exp_contents) + def test_extract_zip(self): + fp_zip = join(dirname(realpath(__file__)), '..', '..', 'tests', 'data', + 'results_multiplesurvey_barcodes.zip') + obs = extract_zip(fp_zip) + exp_filenames = ['failures.txt', 'survey_Fermented_Foods_md.txt', + 'survey_Personal_Information_md.txt', + 'survey_Personal_Microbiome_md.txt', + 'survey_Pet_Information_md.txt', + 'surveys_merged_md.txt', 'survey_Surfers_md.txt'] + # check filenames + self.assertEqual(sorted(obs.keys()), sorted(exp_filenames)) + # check file contents very briefly + self.assertIn('SURF_BOARD_TYPE', obs['survey_Surfers_md.txt']) + + def test_sneak_files(self): + fp_zip = join(dirname(realpath(__file__)), '..', '..', 'tests', 'data', + 'results_multiplesurvey_barcodes.zip') + exp = [{'survey_Personal_Microbiome_md.txt': 'sample_name\tPM_AGE\tP'}, + {'survey_Pet_Information_md.txt': 'sample_name\tALTITUDE'}, + {'failures.txt': 'The following barcod'}, + {'survey_Fermented_Foods_md.txt': 'sample_name\tFERMENTE'}, + {'survey_Surfers_md.txt': 'sample_name\tSURF_BOA'}, + {'survey_Personal_Information_md.txt': 'sample_name\tACNE_MED'}, + {'surveys_merged_md.txt': 'sample_name\tACNE_MED'}] + obs = sneak_files(extract_zip(fp_zip), 20) + self.assertEqual(exp, obs) + if __name__ == '__main__': unittest.main() diff --git a/knimin/templates/ag_pulldown.html b/knimin/templates/ag_pulldown.html index 0f10223..80dd64d 100644 --- a/knimin/templates/ag_pulldown.html +++ b/knimin/templates/ag_pulldown.html @@ -52,6 +52,15 @@ dummy.addParameter('barcodes', '{{barcodes}}'); dummy.addParameter('blanks', '{{blanks}}'); dummy.addParameter('external', '{{external}}'); + slist = [ + {% for s in agsurveys %} + {% if s[2] == True %} + {{s[0]}}, + {% end %} + {% end %} + ]; + dummy.addParameter('selected_ag_surveys', slist); + dummy.addParameter('merged', '{{merged}}'); dummy.send(); {% end %} }); @@ -71,14 +80,34 @@

Metadata Pulldown

Upload qiime mapping file, or other file with first line header and one barcode per line

Barcodes File

+

American Gut Surveys: +

Select which American Gut surveys shall be included in the pulldown archive.
Add 'all in one file' if you also want to have one additional file
that contains all available columns from all American Gut surveys.
+ +

+ +

External Survey +

Select which external surveys should be included in the pulldown archive.

+{% if merged == 'True' %} +Add file containing columns of all selected surveys
+{% else %} +Add a file "surveys_merged_md.txt" that will contain columns of all selected surveys.
+{% end %}

{% raw errors %}
-{% end %} \ No newline at end of file +{% end %} diff --git a/knimin/tests/data/barcodes.txt b/knimin/tests/data/barcodes.txt index 373babe..87f9b65 100644 --- a/knimin/tests/data/barcodes.txt +++ b/knimin/tests/data/barcodes.txt @@ -14,9 +14,7 @@ BLANK100001453 notInDB 000015297 000015298 000015299 -000015300 000016280 000016281 -000016282 000016283 000016284 diff --git a/knimin/tests/data/multiplesurvey_barcodes.txt b/knimin/tests/data/multiplesurvey_barcodes.txt new file mode 100644 index 0000000..1c0c032 --- /dev/null +++ b/knimin/tests/data/multiplesurvey_barcodes.txt @@ -0,0 +1,20 @@ +000001000 infos about barcode survey_group: -1 survey_id: 48c83b76031b7580 +000001001 infos about barcode survey_group: -1 survey_id: 0b0832aafb76068d +000001002 infos about barcode survey_group: -1 survey_id: 2d19912f1784a4bf +000037583 infos about barcode survey_group: -2 survey_id: 0696e13141fb4d13 +000066526 infos about barcode survey_group: -2 survey_id: 0c42c7b89d21d814 +000031568 infos about barcode survey_group: -2 survey_id: 0d4d925e045f9353 +000037555 infos about barcode survey_group: -3 survey_id: 05109e6adfa45c70 +000065893 infos about barcode survey_group: -3 survey_id: 051117c37ffd75d7 +000067690 infos about barcode survey_group: -3 survey_id: 061b62bb33a480d6 +000049932 infos about barcode survey_group: -4 survey_id: 005be6bd1a714085 +000063380 infos about barcode survey_group: -4 survey_id: 0d57b2d00370a907 +000063381 infos about barcode survey_group: -4 survey_id: 0d57b2d00370a907 +000006616 infos about barcode survey_group: -5 survey_id: 864923798da97c70 +000030821 infos about barcode survey_group: -1 survey_id: 37af4d8102234cfc +000030822 infos about barcode survey_group: -1 survey_id: 37af4d8102234cfc +000030823 infos about barcode survey_group: -1 survey_id: 37af4d8102234cfc +000069020 infos about barcode survey_group: -4 survey_id: e60a2eb5001e48c6 +000069021 infos about barcode survey_group: -4 survey_id: e60a2eb5001e48c6 +000069022 infos about barcode survey_group: -4 survey_id: e60a2eb5001e48c6 + diff --git a/knimin/tests/data/results_barcodes.zip b/knimin/tests/data/results_barcodes.zip new file mode 100644 index 0000000..fed5b25 Binary files /dev/null and b/knimin/tests/data/results_barcodes.zip differ diff --git a/knimin/tests/data/results_multiplesurvey_barcodes.zip b/knimin/tests/data/results_multiplesurvey_barcodes.zip new file mode 100644 index 0000000..fa4bde5 Binary files /dev/null and b/knimin/tests/data/results_multiplesurvey_barcodes.zip differ diff --git a/knimin/tests/test_ag_pulldown.py b/knimin/tests/test_ag_pulldown.py index a168648..99f95b9 100644 --- a/knimin/tests/test_ag_pulldown.py +++ b/knimin/tests/test_ag_pulldown.py @@ -1,11 +1,14 @@ from unittest import main import os from os.path import dirname, realpath, join +from tempfile import NamedTemporaryFile from tornado.escape import url_escape from knimin.tests.tornado_test_base import TestHandlerBase from knimin import db +from knimin.lib.mem_zip import extract_zip, sneak_files +from knimin.handlers.ag_pulldown import listify class testUpdateEBIStatusHandler(TestHandlerBase): @@ -53,6 +56,13 @@ def test_get(self): self.assertIn("" % (survey, survey), response.body) self.assertNotIn('', response.body) + for (_id, name, selected) in db.list_ag_surveys(): + if selected: + self.assertIn("" % + (_id, name), response.body) + else: + self.assertIn("" % + (_id, name), response.body) def test_post(self): self.mock_login_admin() @@ -89,50 +99,110 @@ def test_get_not_authed(self): def test_post(self): self.mock_login_admin() response = self.post('/ag_pulldown/download/', - {'barcodes': ['000001445', - '000001446', - '000001447', - '000001448', - '100001449', - '000016180', - '000015296', - '000015297', - '000015298', - '000015299', - '000015300', - '000016280', - '000016281', - '000016282', - '000016283', + {'barcodes': ['000001448', '000001447', + '100001449', '000001445', + '000015296', '000015297', + '000015298', '000015299', + '000016180', '000016280', + '000016281', '000016283', '000016284'], - 'blanks': ['BLANK000001449', - 'BLANK000001453', + 'blanks': ['BLANK000001449', 'BLANK000001453', 'BLANK100001453'], - 'external': db.list_external_surveys()[:1]}) + 'external': db.list_external_surveys()[:1], + 'selected_ag_surveys': [-1, -2, -3, -4, -5], + 'merged': 'True'}) self.assertEqual(response.headers['Content-Disposition'], 'attachment; filename=metadata.zip') self.assertIn('failures.txt', response.body) - self.assertIn('survey_1_md.txt', response.body) + self.assertIn('survey_Personal_Information_md.txt', response.body) + + # store the resulting zip archive to disc ... + tmpfile = NamedTemporaryFile(mode='w', delete=False, + prefix='metadata_pulldown_single_', + suffix='.zip') + tmpfile.write(response.body) + tmpfile.close() + # ... and read the content as dict of strings + result = extract_zip(tmpfile.name) + os.remove(tmpfile.name) + + # read in the true content from data dir for comparison + truth = extract_zip(join(dirname(realpath(__file__)), 'data', + 'results_barcodes.zip')) + self.maxDiff = None + self.assertEqual(sneak_files(result), sneak_files(truth)) + + def test_post_multiple_surverys(self): + self.mock_login_admin() + response = self.post('/ag_pulldown/download/', + {'barcodes': ['000001000', '000001001', + '000001002', '000037583', + '000066526', '000031568', + '000037555', '000065893', + '000067690', '000049932', + '000063380', '000063381', + '000006616', '000030821', + '000030822', '000030823', + '000069020', '000069021' + '000069022'], + 'blanks': [], + 'external': [], + 'selected_ag_surveys': [-1, -2, -3, -4, -5], + 'merged': 'True'}) + self.assertEqual(response.headers['Content-Disposition'], + 'attachment; filename=metadata.zip') + + # store the resulting zip archive to disc ... + tmpfile = NamedTemporaryFile(mode='w', delete=False, + prefix='metadata_pulldown_multiple_', + suffix='.zip') + tmpfile.write(response.body) + tmpfile.close() + # ... and read the content as dict of strings + result = extract_zip(tmpfile.name) + os.remove(tmpfile.name) + + # read in the true content from data dir for comparison + truth = extract_zip(join(dirname(realpath(__file__)), 'data', + 'results_multiplesurvey_barcodes.zip')) + + self.assertEqual(sneak_files(result), sneak_files(truth)) + + def test_post_select_surveys(self): + self.mock_login_admin() + response = self.post('/ag_pulldown/download/', + {'barcodes': ['000037555', '000065893', + '000067690', '000037583', + '000066526', '000031568'], + 'blanks': [], + 'external': [], + 'selected_ag_surveys': [-2, -3, -8], + 'merged': 'False'}) + # store the resulting zip archive to disc ... + tmpfile = NamedTemporaryFile(mode='w', delete=False, + prefix='metadata_pulldown_multiple_sel_', + suffix='.zip') + tmpfile.write(response.body) + tmpfile.close() + # ... and read the content as dict of strings + result = extract_zip(tmpfile.name) + self.assertItemsEqual(result.keys(), + ['failures.txt', + 'survey_Fermented_Foods_md.txt', + 'survey_Pet_Information_md.txt']) + os.remove(tmpfile.name) # no blanks response = self.post('/ag_pulldown/download/', - {'barcodes': ['000001445', - '000001446', - '000001447', - '000001448', - '100001449', - '000016180', - '000015296', - '000015297', - '000015298', - '000015299', - '000015300', - '000016280', - '000016281', - '000016282', - '000016283', - '000016284'], - 'blanks': '', + {'barcodes': ('000001445', '000001446', + '000001447', '000001448', + '100001449', '000016180', + '000015296', '000015297', + '000015298', '000015299', + '000015300', '000016280', + '000016281', '000016282', + '000016283', '000016284'), + 'blanks': [], 'external': db.list_external_surveys()[:1]}) self.assertEqual(response.code, 200) self.assertEqual(response.headers['Content-Disposition'], @@ -159,11 +229,66 @@ def test_post(self): 'blanks': ['BLANK000001449', 'BLANK000001453', 'BLANK100001453'], - 'external': ''}) + 'external': []}) self.assertEqual(response.code, 200) self.assertEqual(response.headers['Content-Disposition'], 'attachment; filename=metadata.zip') + def test_post_select_surveys_htmlencoding(self): + # the interface html page provides arguments as one concatenated string + # which is not a list of strings. Here, I test if this other behaviour + # also works. + self.mock_login_admin() + + response = self.post('/ag_pulldown/download/', + {'barcodes': ('000037555,000065893,000067690,' + '000037583,000066526,000031568'), + 'blanks': '', + 'external': '', + 'selected_ag_surveys': '-2,-3,-8', + 'merged': 'False'}) + # store the resulting zip archive to disc ... + tmpfile = NamedTemporaryFile(mode='w', delete=False, + prefix='metadata_pulldown_multiple_sel_', + suffix='.zip') + tmpfile.write(response.body) + tmpfile.close() + # ... and read the content as dict of strings + result = extract_zip(tmpfile.name) + self.assertItemsEqual(result.keys(), + ['failures.txt', + 'survey_Fermented_Foods_md.txt', + 'survey_Pet_Information_md.txt']) + os.remove(tmpfile.name) + + response = self.post('/ag_pulldown/download/', + {'barcodes': ('000037555,000065893,000067690,' + '000037583,000066526,000031568'), + 'blanks': '', + 'external': '', + 'selected_ag_surveys': '-3', + 'merged': 'False'}) + # store the resulting zip archive to disc ... + tmpfile = NamedTemporaryFile(mode='w', delete=False, + prefix='metadata_pulldown_multiple_sel_', + suffix='.zip') + tmpfile.write(response.body) + tmpfile.close() + # ... and read the content as dict of strings + result = extract_zip(tmpfile.name) + self.assertItemsEqual(result.keys(), + ['failures.txt', + 'survey_Fermented_Foods_md.txt']) + os.remove(tmpfile.name) + + def test_listify(self): + obs = listify(['a', 'b', 'c']) + exp = ['a', 'b', 'c'] + self.assertItemsEqual(obs, exp) + + obs = listify(['a,b,c']) + self.assertItemsEqual(obs, exp) + if __name__ == "__main__": main()