Repository 'ncbi_eutils_esearch'
hg clone https://toolshed.g2.bx.psu.edu/repos/iuc/ncbi_eutils_esearch

Changeset 3:e267701c187b (2020-09-23)
Previous changeset 2:c6096cd97120 (2020-03-11) Next changeset 4:e88866c4f3e7 (2021-01-04)
Commit message:
"planemo upload for repository https://github.com/galaxyproject/tools-iuc/tree/master/tools/ncbi_entrez_eutils commit dae34e5e182b4cceb808d7353080f14aa9a78ca9"
modified:
__efetch_build_options.py
ecitmatch.py
efetch.py
egquery.py
einfo.py
elink.py
epost.py
esearch.py
esearch.xml
esummary.py
eutils.py
macros.xml
added:
generate_macros_xml.pl
test-data/efetchin.tabular
test-data/einfo.dblist.xml
test-data/elink.elinkin_1link.json
test-data/elink.elinkin_1link_hist.json
test-data/elink.elinkin_1link_id.tabular
test-data/elink.elinkin_allalllinks_id.xml
test-data/elink.elinkin_alllinks_id.xml
test-data/elink.esearch_in_xmlid.xml
test-data/elink.esearch_in_xmlid_1link.tabular
test-data/elink.esearchin_id.json
test-data/elink.esearchin_id.tabular
test-data/elink.esearchin_id_1link.xml
test-data/elink.esearchin_id_alllinks.json
test-data/esearch.gene.hist.json
test-data/esearch.gene.hist.xml
test-data/esearch.gene.json
test-data/esearch.gene.tabular
test-data/esearch.gene.xml
b
diff -r c6096cd97120 -r e267701c187b __efetch_build_options.py
--- a/__efetch_build_options.py Wed Mar 11 04:03:14 2020 -0400
+++ b/__efetch_build_options.py Wed Sep 23 09:48:26 2020 +0000
b
@@ -53,7 +53,7 @@
 <DbName>sra</DbName>
 <DbName>structure</DbName>
 <DbName>taxonomy</DbName>
-<DbName>unigene</DbName>'''.replace( "<DbName>", "").replace( "</DbName>", "").split("\n")
+<DbName>unigene</DbName>'''.replace("<DbName>", "").replace("</DbName>", "").split("\n")
 
 
 help = '''  (all)
b
diff -r c6096cd97120 -r e267701c187b ecitmatch.py
--- a/ecitmatch.py Wed Mar 11 04:03:14 2020 -0400
+++ b/ecitmatch.py Wed Sep 23 09:48:26 2020 +0000
b
@@ -1,5 +1,4 @@
 #!/usr/bin/env python
-from __future__ import print_function
 
 import argparse
 
@@ -17,6 +16,8 @@
     parser.add_argument('--first_page', nargs='*', help='First Page')
     parser.add_argument('--author_name', nargs='*', help='Author name')
 
+    parser.add_argument('--version', action='version', version=eutils.Client.getVersion(), help='Version (reports Biopython version)')
+
     # Emails
     parser.add_argument('--user_email', help="User email")
     parser.add_argument('--admin_email', help="Admin email")
b
diff -r c6096cd97120 -r e267701c187b efetch.py
--- a/efetch.py Wed Mar 11 04:03:14 2020 -0400
+++ b/efetch.py Wed Sep 23 09:48:26 2020 +0000
[
@@ -1,36 +1,111 @@
 #!/usr/bin/env python
+
 import argparse
+import glob
+import json
+import logging
+import os
+
 
 import eutils
 
 
+logging.basicConfig(level=logging.INFO)
+
+
+def handleEfetchException(e, db, payload):
+    logging.error('No results returned. This could either be due to no records matching the supplied IDs for the query database or it could be an error due to invalid parameters.  The reported exception was "%s".\n\nPayload used for the efetch query to database "%s"\n\n%s', e, db, json.dumps(payload, indent=4))
+
+    # Create a file in the downloads folder so that the user can access run information
+    current_directory = os.getcwd()
+    final_directory = os.path.join(current_directory, r'downloads')
+    if not os.path.exists(final_directory):
+        os.makedirs(final_directory)
+
+    print('The following files were downloaded:')
+    print(os.listdir(final_directory))
+
+    file_path = os.path.join('downloads', 'no_results.txt')
+    with open(file_path, 'w') as handle:
+        handle.write('No results')
+
+
+def localFetch(db, gformat, newname, **payload):
+    problem = None
+    try:
+        c.fetch(db, **payload)
+
+        for chunk, file in enumerate(glob.glob('downloads/EFetch *')):
+            os.rename(file, '%s%s.%s' % (newname, chunk + 1, gformat))
+
+    except Exception as e:
+        problem = e
+        handleEfetchException(e, db, payload)
+    else:
+        print('The following files were downloaded:')
+        print(os.listdir('downloads'))
+
+    return problem
+
+
 if __name__ == '__main__':
     parser = argparse.ArgumentParser(description='EFetch', epilog='')
     parser.add_argument('db', help='Database to use')
     parser.add_argument('--user_email', help="User email")
     parser.add_argument('--admin_email', help="Admin email")
 
+    parser.add_argument('--version', action='version', version=eutils.Client.getVersion(), help='Version (reports Biopython version)')
+
     # ID source
+    parser.add_argument('--id_json', help='list of ids in a json file as returned by esearch or elink')
+    parser.add_argument('--id_xml', help='list of ids in an xml file as returned by esearch or elink')
     parser.add_argument('--id_list', help='list of ids')
     parser.add_argument('--id', help='Comma separated individual IDs')
-    parser.add_argument('--history_file', help='Fetch results from previous query')
+    parser.add_argument('--history_file', help='Fetch results from previous query (JSON)')
+    parser.add_argument('--history_xml', help='Fetch results from previous query (XML)')
 
     # Output
     parser.add_argument('--retmode', help='Retmode')
     parser.add_argument('--rettype', help='Rettype')
+    parser.add_argument('--galaxy_format', help='Galaxy format')
     args = parser.parse_args()
 
     c = eutils.Client(history_file=args.history_file, user_email=args.user_email, admin_email=args.admin_email)
-    merged_ids = c.parse_ids(args.id_list, args.id, args.history_file)
 
     payload = {}
-    if args.history_file is not None:
-        payload.update(c.get_history())
-    else:
-        payload['id'] = ','.join(merged_ids)
-
     for attr in ('retmode', 'rettype'):
         if getattr(args, attr, None) is not None:
             payload[attr] = getattr(args, attr)
 
-    c.fetch(args.db, ftype=args.retmode, **payload)
+    if args.history_file is not None or args.history_xml is not None:
+        if args.history_file is not None:
+            input_histories = c.get_histories()
+        else:
+            input_histories = c.extract_histories_from_xml_file(args.history_xml)
+
+        problem = None
+        for hist in input_histories:
+            qkey = hist['query_key']
+            tmp_payload = payload
+            tmp_payload.update(hist)
+            newname = 'downloads/EFetch-%s-%s-querykey%s-chunk' % (args.rettype, args.retmode, qkey)
+            problem = localFetch(args.db, args.galaxy_format, newname, **tmp_payload)
+
+            if os.path.exists('downloads'):
+                os.rename('downloads', 'downloads-qkey%s' % (qkey))
+
+        if not os.path.exists('downloads'):
+            os.makedirs('downloads')
+
+        for relpath in glob.glob('downloads-qkey*/*'):
+            file = os.path.basename(relpath)
+            os.rename(relpath, 'downloads/%s' % (file))
+
+        if problem is not None:
+            raise(problem)
+
+    else:
+        merged_ids = c.parse_ids(args.id_list, args.id, args.history_file, args.id_xml, args.id_json)
+        payload['id'] = ','.join(merged_ids)
+        newname = 'downloads/EFetch-%s-%s-chunk' % (args.rettype, args.retmode)
+        localFetch(args.db, args.galaxy_format, newname, **payload)
b
diff -r c6096cd97120 -r e267701c187b egquery.py
--- a/egquery.py Wed Mar 11 04:03:14 2020 -0400
+++ b/egquery.py Wed Sep 23 09:48:26 2020 +0000
b
@@ -1,5 +1,4 @@
 #!/usr/bin/env python
-from __future__ import print_function
 
 import argparse
 
@@ -9,9 +8,12 @@
 if __name__ == '__main__':
     parser = argparse.ArgumentParser(description='EGQuery', epilog='')
     parser.add_argument('term', help='Query')
-    #
+
     parser.add_argument('--user_email', help="User email")
     parser.add_argument('--admin_email', help="Admin email")
+
+    parser.add_argument('--version', action='version', version=eutils.Client.getVersion(), help='Version (reports Biopython version)')
+
     args = parser.parse_args()
 
     c = eutils.Client(user_email=args.user_email, admin_email=args.admin_email)
b
diff -r c6096cd97120 -r e267701c187b einfo.py
--- a/einfo.py Wed Mar 11 04:03:14 2020 -0400
+++ b/einfo.py Wed Sep 23 09:48:26 2020 +0000
b
@@ -1,5 +1,4 @@
 #!/usr/bin/env python
-from __future__ import print_function
 
 import argparse
 
@@ -11,6 +10,7 @@
     parser.add_argument('--db', help='Database to use')
     parser.add_argument('--user_email', help="User email")
     parser.add_argument('--admin_email', help="Admin email")
+    parser.add_argument('--version', action='version', version=eutils.Client.getVersion(), help='Version (reports Biopython version)')
     args = parser.parse_args()
 
     c = eutils.Client(user_email=args.user_email, admin_email=args.admin_email)
b
diff -r c6096cd97120 -r e267701c187b elink.py
--- a/elink.py Wed Mar 11 04:03:14 2020 -0400
+++ b/elink.py Wed Sep 23 09:48:26 2020 +0000
[
@@ -1,12 +1,16 @@
 #!/usr/bin/env python
-from __future__ import print_function
 
 import argparse
 import json
+import logging
+import os
 
 import eutils
 
 
+logging.basicConfig(level=logging.INFO)
+
+
 if __name__ == '__main__':
     parser = argparse.ArgumentParser(description='EFetch', epilog='')
     parser.add_argument('db', help='Database to use, sometimes "none" (e.g. *check)')
@@ -15,16 +19,23 @@
                                         'neighbor_history', 'acheck', 'ncheck', 'lcheck',
                                         'llinks', 'llinkslib', 'prlinks'],
                         help='ELink command mode')
-    # Only used in case of neighbor_history
-    parser.add_argument('--history_out', type=argparse.FileType('w'),
-                        help='Output history file', default='-')
+
+    parser.add_argument('--version', action='version', version=eutils.Client.getVersion(), help='Version (reports Biopython version)')
 
     parser.add_argument('--user_email', help="User email")
     parser.add_argument('--admin_email', help="Admin email")
+
     # ID Sources
+    parser.add_argument('--id_xml', help='list of ids in an xml file as returned by esearch or elink')
+    parser.add_argument('--id_json', help='list of ids in a json file as returned by esearch or elink')
     parser.add_argument('--id_list', help='list of ids')
     parser.add_argument('--id', help='Comma separated individual IDs')
     parser.add_argument('--history_file', help='Fetch results from previous query')
+    parser.add_argument('--history_xml', help='Fetch results from previous query')
+
+    # Optional
+    parser.add_argument('--linkname', help='Restrict results to a specific link source')
+    parser.add_argument('--retmode', choices=['xml', 'json', 'uilist'], help='Output format')
 
     # TODO: dates, linkname, term, holding
     # neighbor or neighbor_history and dbfrom is pubmed
@@ -37,25 +48,91 @@
     args = parser.parse_args()
 
     c = eutils.Client(history_file=args.history_file, user_email=args.user_email, admin_email=args.admin_email)
-    merged_ids = c.parse_ids(args.id_list, args.id, args.history_file)
 
     payload = {
         'dbfrom': args.dbfrom,
         'cmd': args.cmd,
     }
-    if args.history_file is not None:
-        payload.update(c.get_history())
-    else:
-        payload['id'] = ','.join(merged_ids)
 
     # DB can be 'none' in a few cases.
     if args.db != "none":
         payload['db'] = args.db
 
-    results = c.link(**payload)
+    if args.linkname is not None:
+        payload['linkname'] = args.linkname
+
+    results = []
+    qkeys = []
+    if args.history_file is not None or args.history_xml is not None:
+        payload['retmode'] = args.retmode
+        if args.history_file is not None:
+            input_histories = c.get_histories()
+        else:
+            input_histories = c.extract_histories_from_xml_file(args.history_xml)
+        for hist in input_histories:
+            qkeys += [hist['query_key']]
+            tmp_payload = payload
+            tmp_payload.update(hist)
+            results += [c.link(**tmp_payload)]
+    else:
+        # There is no uilist retmode
+        if args.retmode == "uilist":
+            payload['retmode'] = 'xml'
+        else:
+            payload['retmode'] = args.retmode
+        merged_ids = c.parse_ids(args.id_list, args.id, args.history_file, args.id_xml, args.id_json)
+        payload['id'] = ','.join(merged_ids)
+        qkeys += [1]
+        results += [c.link(**payload)]
+
+    # There could be multiple sets of results if a history was supplied
+    if args.history_file is not None or args.history_xml is not None:
+        # Multiple result sets can be returned
+        # Create a directory for the output files
+        current_directory = os.getcwd()
+        final_directory = os.path.join(current_directory, r'downloads')
+        if not os.path.exists(final_directory):
+            os.makedirs(final_directory)
 
-    if args.cmd == "neighbor_history":
-        history = c.extract_history(results)
-        args.history_out.write(json.dumps(history, indent=4))
-
-    print(results)
+        logging.info("Writing files:")
+        # When rettype is uilist, convert to text format (which elink does not do)
+        count = 0
+        if args.retmode == 'uilist':
+            for result in results:
+                qkey = qkeys[count]
+                count += 1
+                ids = c.xmlstring2UIlist(result)
+                file_path = os.path.join('downloads', '%s-querykey%s.tabular' % (args.db, qkey))
+                logging.info('%s.tabular' % (args.db))
+                with open(file_path, 'w') as handle:
+                    for id in ids:
+                        handle.write(id)
+                        handle.write(os.linesep)
+        elif args.retmode == 'json':
+            for result in results:
+                qkey = qkeys[count]
+                count += 1
+                file_path = os.path.join('downloads', '%s-querykey%s.json' % (args.db, qkey))
+                logging.info('%s-link%s.json' % (args.db, count))
+                with open(file_path, 'w') as handle:
+                    json_data = c.jsonstring2jsondata(result)
+                    handle.write(json.dumps(json_data, indent=4))
+        else:
+            for result in results:
+                qkey = qkeys[count]
+                count += 1
+                file_path = os.path.join('downloads', '%s-querykey%s.xml' % (args.db, qkey))
+                logging.info('%s-link%s.xml' % (args.db, count))
+                with open(file_path, 'w') as handle:
+                    handle.write(result)
+    else:
+        # When rettype is uilist, convert to text format (which elink does not do)
+        if args.retmode == 'uilist':
+            ids = c.xmlstring2UIlist(results[0])
+            for id in ids:
+                print(id)
+        elif args.retmode == 'json':
+            json_data = c.jsonstring2jsondata(results[0])
+            print(json.dumps(json_data, indent=4))
+        else:
+            print(results[0])
b
diff -r c6096cd97120 -r e267701c187b epost.py
--- a/epost.py Wed Mar 11 04:03:14 2020 -0400
+++ b/epost.py Wed Sep 23 09:48:26 2020 +0000
[
@@ -1,5 +1,4 @@
 #!/usr/bin/env python
-from __future__ import print_function
 
 import argparse
 
@@ -9,22 +8,37 @@
 if __name__ == '__main__':
     parser = argparse.ArgumentParser(description='EPost', epilog='')
     parser.add_argument('db', help='Database to use')
+    parser.add_argument('--user_email', help="User email")
+    parser.add_argument('--admin_email', help="Admin email")
+
+    parser.add_argument('--version', action='version', version=eutils.Client.getVersion(), help='Version (reports Biopython version)')
+
+    # ID source
     parser.add_argument('--id_list', help='list of ids')
     parser.add_argument('--id', help='Comma separated individual IDs')
-    parser.add_argument('--history_file', help='Post to new QueryKey in an existing WebEnv')
-    parser.add_argument('--user_email', help="User email")
-    parser.add_argument('--admin_email', help="Admin email")
+    parser.add_argument('--id_json', help='list of ids in a json file as returned by esearch or elink')
+    parser.add_argument('--id_xml', help='list of ids in an xml file as returned by esearch or elink')
+
+    # Target history
+    parser.add_argument('--history_xml', help='Post to new QueryKey in an existing WebEnv (XML)')
+    parser.add_argument('--history_file', help='Post to new QueryKey in an existing WebEnv (JSON)')
+    parser.add_argument('--webenv', help='Post to new WebEnv (History ID)')
 
     args = parser.parse_args()
 
     c = eutils.Client(history_file=args.history_file, user_email=args.user_email, admin_email=args.admin_email)
-    merged_ids = c.parse_ids(args.id_list, args.id, args.history_file)
 
     payload = {}
     if args.history_file is not None:
-        payload.update(c.get_history())
-    else:
-        payload['id'] = ','.join(merged_ids)
-        payload['WebEnv'] = ''
+        hist = c.get_history()
+        payload['WebEnv'] = hist['WebEnv']
+    elif args.history_xml is not None:
+        hist = c.extract_history_from_xml_file(args.history_xml)
+        payload['WebEnv'] = hist['WebEnv']
+    elif args.webenv is not None:
+        payload['WebEnv'] = args.webenv
+
+    merged_ids = c.parse_ids(args.id_list, args.id, None, args.id_xml, args.id_json)
+    payload['id'] = ','.join(merged_ids)
 
     print(c.post(args.db, **payload))
b
diff -r c6096cd97120 -r e267701c187b esearch.py
--- a/esearch.py Wed Mar 11 04:03:14 2020 -0400
+++ b/esearch.py Wed Sep 23 09:48:26 2020 +0000
[
@@ -1,12 +1,16 @@
 #!/usr/bin/env python
-from __future__ import print_function
 
 import argparse
 import json
+import logging
+
 
 import eutils
 
 
+logging.basicConfig(level=logging.INFO)
+
+
 if __name__ == '__main__':
     parser = argparse.ArgumentParser(description='ESearch', epilog='')
     parser.add_argument('db', help='Database to use')
@@ -17,34 +21,54 @@
     parser.add_argument('--mindate', help='Minimum date')
     parser.add_argument('--maxdate', help='maximum date')
     # History
-    parser.add_argument('--history_out', type=argparse.FileType('w'),
-                        help='Output history file')
+    parser.add_argument('--history_out', action="store_true", help='Output history file')
     parser.add_argument('--user_email', help="User email")
     parser.add_argument('--admin_email', help="Admin email")
+
+    parser.add_argument('--version', action='version', version=eutils.Client.getVersion(), help='Version (reports Biopython version)')
+
+    # Output
+    parser.add_argument('--retmode', help='Retmode')
+    parser.add_argument('--rettype', help='Rettype')
+    parser.add_argument('--retstart', type=int, default=0, help='Retstart - Starting rec number (0)')
+    parser.add_argument('--retmax', type=int, default=20, help='Retmax - max number of recs returned (20, max 100000)')
+
     args = parser.parse_args()
 
     c = eutils.Client(history_file=args.history_file, user_email=args.user_email, admin_email=args.admin_email)
 
+    max_retmax = 100000
+    min_retmax = 1
+    max = max(min(args.retmax, max_retmax), min_retmax)
+
     payload = {
         'db': args.db,
         'term': args.term,
-        'retstart': 0,
-        'retmax': 20,
-        # hmmm @ retmax
     }
     if args.history_file is not None:
         payload.update(c.get_history())
-    if args.history_out is not None:
+
+    # if args.history_out is not None:
+    if args.history_out:
         payload['usehistory'] = 'y'
 
-    for attr in ('datetype', 'reldate', 'mindate', 'maxdate'):
+    payload['retmode'] = args.retmode
+
+    for attr in ('datetype', 'reldate', 'mindate', 'maxdate', 'rettype', 'retmax', 'retstart'):
         if getattr(args, attr, None) is not None:
             payload[attr] = getattr(args, attr)
 
+    logging.info("Payload used for query:" + json.dumps(payload, indent=4))
+
     results = c.search(**payload)
 
-    if args.history_out is not None:
-        history = c.extract_history(results)
-        args.history_out.write(json.dumps(history, indent=4))
-
-    print(results)
+    # We're going to infer that rettype being uilist means convert to text format (which esearch does not do)
+    if args.retmode == 'text':
+        ids = c.xmlstring2UIlist(results)
+        for id in ids:
+            print(id)
+    elif args.retmode == 'json':
+        json_data = c.jsonstring2jsondata(results)
+        print(json.dumps(json_data, indent=4))
+    else:
+        print(results)
b
diff -r c6096cd97120 -r e267701c187b esearch.xml
--- a/esearch.xml Wed Mar 11 04:03:14 2020 -0400
+++ b/esearch.xml Wed Sep 23 09:48:26 2020 +0000
[
@@ -6,14 +6,21 @@
   </macros>
   <expand macro="requirements"/>
   <version_command>python esearch.py --version</version_command>
-  <command detect_errors="aggressive"><![CDATA[
-python '$__tool_directory__/esearch.py'
-$db_select
-"$term"
+  <command detect_errors="aggressive">
+    <![CDATA[
+
+      ##Doing replacement here so that dataset label doesn't have slashes
+      #set saniterm = $term.replace('"','\\"')
+
+      python '$__tool_directory__/esearch.py'
 
-#if $history_file and $history_file is not None:
-    --history_file '$history_file'
-#end if
+      $db_select
+
+      "$saniterm"
+
+      #if $history_file and $history_file is not None:
+        --history_file '$history_file'
+      #end if
 
 #if $date.enabled == 'True'
     --datetype $date.datetype
@@ -31,12 +38,34 @@
     #end if
 #end if
 
-#if $use_history:
-    --history_out $history
-#end if
+      #if $retstart is not None:
+        --retstart '$retstart'
+      #end if
+
+      #if $retmax is not None:
+        --retmax '$retmax'
+      #end if
 
-@EMAIL_ARGUMENTS@
-> $default]]></command>
+      #if $output_format == 'history_xml':
+        --history_out
+        --retmode xml
+      #elif $output_format == 'history_json':
+        --history_out
+        --retmode json
+      #elif $output_format == 'id_xml':
+        --retmode xml
+      #elif $output_format == 'id_json':
+        --retmode json
+      #elif $output_format == 'id_text':
+        --retmode text
+      #end if
+
+      @EMAIL_ARGUMENTS@
+
+      > $default
+
+    ]]>
+  </command>
   <inputs>
     <expand macro="dbselect"/>
     <param name="term" type="text" label="Search term">
@@ -45,11 +74,18 @@
           <add value="'"/>
           <add value="["/>
           <add value="]"/>
+          <add value='"'/>
         </valid>
       </sanitizer>
     </param>
     <param name="history_file" type="data" format="json" optional="true" label="Filter existing history" />
-    <param name="use_history" type="boolean" truevalue="--use_history" falsevalue="" checked="false" label="Store results to history server" />
+    <param name="output_format" type="select" label="Output Format">
+      <option value="history_json">History File (json)</option>
+      <option value="history_xml">History File (xml)</option>
+      <option value="id_xml">ID File (xml)</option>
+      <option value="id_json">ID File (json)</option>
+      <option value="id_text" selected="true">ID File (tabular)</option>
+    </param>
     <conditional name="date">
       <param name="enabled" type="select" label="Filter by date">
         <option value="False">No</option>
@@ -67,32 +103,73 @@
       </when>
       <when value="False"/>
     </conditional>
+    <param name="retstart" type="integer" value="0" min="0" max="99999" label="Starting record to return (--retstart)" />
+    <param name="retmax" type="integer" value="100000" min="1" max="100000" label="Maximum number of records to return (--retmax)" />
   </inputs>
   <outputs>
-    <data name="default" format="json" label="ESearch results for $term">
+    <data name="default" format="xml" label="ESearch results for $db_select database query: $term">
       <change_format>
-        <when input="use_history" value="" format="xml"/>
+        <when input="output_format" value="history_json" format="json" />
+        <when input="output_format" value="history_xml" format="xml" />
+        <when input="output_format" value="id_xml" format="xml" />
+        <when input="output_format" value="id_json" format="json" />
+        <when input="output_format" value="id_text" format="tabular" />
       </change_format>
     </data>
-    <expand macro="history_out">
-      <filter>use_history</filter>
-    </expand>
   </outputs>
   <tests>
     <test>
       <param name="db_select" value="pubmed"/>
       <param name="term" value="(PNAS[ta] AND 97[vi])"/>
+      <param name="retstart" value="0"/>
+      <param name="retmax" value="20"/>
+      <param name="output_format" value="id_xml"/>
       <output name="default" file="esearch.pubmed.xml" ftype="xml" lines_diff="2"/>
     </test>
     <test>
       <param name="db_select" value="pubmed"/>
       <param name="term" value="PNAS[ta]"/>
+      <param name="retstart" value="0"/>
+      <param name="retmax" value="20"/>
+      <param name="output_format" value="id_xml"/>
       <param name="enabled" value="True"/>
       <param name="datetype" value="PDAT"/>
       <param name="mindate" value="2014/01/01"/>
       <param name="maxdate" value="2014/02/01"/>
       <output name="default" file="esearch.pubmed.2014-01-pnas.xml" ftype="xml" lines_diff="2"/>
     </test>
+    <test>
+      <param name="db_select" value="gene"/>
+      <param name="term" value="&quot;genetype rrna&quot;[Properties] AND &quot;Homo sapiens&quot;[Organism] AND (&quot;srcdb refseq&quot;[Properties] AND alive[prop])"/>
+      <param name="retstart" value="2"/>
+      <param name="retmax" value="22"/>
+      <param name="output_format" value="id_text"/>
+      <output name="default" file="esearch.gene.tabular" ftype="tabular" lines_diff="2"/>
+    </test>
+    <test>
+      <param name="db_select" value="gene"/>
+      <param name="term" value="118502329"/>
+      <param name="retstart" value="0"/>
+      <param name="retmax" value="1"/>
+      <param name="output_format" value="id_json"/>
+      <output name="default" file="esearch.gene.json" ftype="json" lines_diff="2"/>
+    </test>
+    <test>
+      <param name="db_select" value="gene"/>
+      <param name="term" value="118502329"/>
+      <param name="retstart" value="0"/>
+      <param name="retmax" value="1"/>
+      <param name="output_format" value="history_json"/>
+      <output name="default" file="esearch.gene.hist.json" ftype="json" lines_diff="2"/>
+    </test>
+    <test>
+      <param name="db_select" value="gene"/>
+      <param name="term" value="118502329"/>
+      <param name="retstart" value="0"/>
+      <param name="retmax" value="1"/>
+      <param name="output_format" value="history_xml"/>
+      <output name="default" file="esearch.gene.hist.xml" ftype="xml" lines_diff="2"/>
+    </test>
   </tests>
   <help><![CDATA[
 NCBI Entrez ESearch
b
diff -r c6096cd97120 -r e267701c187b esummary.py
--- a/esummary.py Wed Mar 11 04:03:14 2020 -0400
+++ b/esummary.py Wed Sep 23 09:48:26 2020 +0000
[
@@ -1,32 +1,106 @@
 #!/usr/bin/env python
-from __future__ import print_function
 
 import argparse
+import json
+import logging
+import os
 
 import eutils
 
 
+logging.basicConfig(level=logging.INFO)
+
+
 if __name__ == '__main__':
     parser = argparse.ArgumentParser(description='ESummary', epilog='')
     parser.add_argument('db', help='Database to use')
+    parser.add_argument('--user_email', help="User email")
+    parser.add_argument('--admin_email', help="Admin email")
+
+    parser.add_argument('--version', action='version', version=eutils.Client.getVersion(), help='Version (reports Biopython version)')
+
+    # ID source
+    parser.add_argument('--id_xml', help='list of ids in an xml file as returned by esearch or elink')
+    parser.add_argument('--id_json', help='list of ids in a json file as returned by esearch or elink')
     parser.add_argument('--id_list', help='list of ids')
     parser.add_argument('--id', help='Comma separated individual IDs')
-    parser.add_argument('--history_file', help='Filter existing history')
-    parser.add_argument('--user_email', help="User email")
-    parser.add_argument('--admin_email', help="Admin email")
+    parser.add_argument('--history_file', help='Fetch results from previous query')
+    parser.add_argument('--history_xml', help='Fetch results from previous query')
+
+    # Output
+    parser.add_argument('--retmode', help='Retmode')
+    parser.add_argument('--retstart', type=int, default=0, help='Retstart - Starting rec number (0)')
+    parser.add_argument('--retmax', type=int, default=20, help='Retmax - max number of recs returned (20, max 100000')
+
     args = parser.parse_args()
 
     c = eutils.Client(history_file=args.history_file, user_email=args.user_email, admin_email=args.admin_email)
 
-    merged_ids = c.parse_ids(args.id_list, args.id, args.history_file)
-
     payload = {
         'db': args.db,
     }
 
-    if args.history_file is not None:
-        payload.update(c.get_history())
+    for attr in ('retmode', 'retmax', 'retstart'):
+        if getattr(args, attr, None) is not None:
+            payload[attr] = getattr(args, attr)
+
+    results = []
+    qkeys = []
+    if args.history_file is not None or args.history_xml is not None:
+        payload['retmode'] = args.retmode
+        if args.history_file is not None:
+            input_histories = c.get_histories()
+        else:
+            input_histories = c.extract_histories_from_xml_file(args.history_xml)
+
+        for hist in input_histories:
+            qkeys += [hist['query_key']]
+            tmp_payload = payload
+            tmp_payload.update(hist)
+            results += [c.summary(**tmp_payload)]
     else:
+        # There is no uilist retmode
+        if args.retmode == "uilist":
+            payload['retmode'] = 'xml'
+        else:
+            payload['retmode'] = args.retmode
+        merged_ids = c.parse_ids(args.id_list, args.id, args.history_file, args.id_xml, args.id_json)
         payload['id'] = ','.join(merged_ids)
+        qkeys += [1]
+        results += [c.summary(**payload)]
+
+    # There could be multiple sets of results if a history was supplied
+    if args.history_file is not None or args.history_xml is not None:
+        # Multiple result sets can be returned
+        # Create a directory for the output files
+        current_directory = os.getcwd()
+        final_directory = os.path.join(current_directory, r'downloads')
+        if not os.path.exists(final_directory):
+            os.makedirs(final_directory)
 
-    print(c.summary(**payload))
+        logging.info("Writing files:")
+        count = 0
+        if args.retmode == 'json':
+            for result in results:
+                qkey = qkeys[count]
+                count += 1
+                file_path = os.path.join('downloads', '%s-querykey%s.json' % (args.db, qkey))
+                logging.info('%s-link%s.json' % (args.db, count))
+                with open(file_path, 'w') as handle:
+                    json_data = c.jsonstring2jsondata(result)
+                    handle.write(json.dumps(json_data, indent=4))
+        else:
+            for result in results:
+                qkey = qkeys[count]
+                count += 1
+                file_path = os.path.join('downloads', '%s-querykey%s.xml' % (args.db, qkey))
+                logging.info('%s-link%s.xml' % (args.db, count))
+                with open(file_path, 'w') as handle:
+                    handle.write(result)
+    else:
+        # When rettype is uilist, convert to text format (which elink does not do)
+        if args.retmode == 'json':
+            json_data = c.jsonstring2jsondata(results[0])
+            print(json.dumps(json_data, indent=4))
+        else:
+            print(results[0])
b
diff -r c6096cd97120 -r e267701c187b eutils.py
--- a/eutils.py Wed Mar 11 04:03:14 2020 -0400
+++ b/eutils.py Wed Sep 23 09:48:26 2020 +0000
[
b'@@ -12,6 +12,7 @@\n \n     def __init__(self, history_file=None, user_email=None, admin_email=None):\n         self.using_history = False\n+        self.using_parsedids = False\n \n         if user_email is not None and admin_email is not None:\n             Entrez.email = \';\'.join((admin_email, user_email))\n@@ -29,18 +30,69 @@\n         if history_file is not None:\n             with open(history_file, \'r\') as handle:\n                 data = json.loads(handle.read())\n-                self.query_key = data[\'QueryKey\']\n-                self.webenv = data[\'WebEnv\']\n-                self.using_history = True\n+                # esearch\n+                if \'QueryKey\' in data:\n+                    self.query_key = data[\'QueryKey\']\n+                    self.webenv = data[\'WebEnv\']\n+                    self.query_keys = []\n+                    self.query_keys += [data[\'QueryKey\']]\n+                    self.using_history = True\n+                elif \'query_key\' in data:\n+                    self.query_key = data[\'query_key\']\n+                    self.webenv = data[\'WebEnv\']\n+                    self.query_keys = []\n+                    self.query_keys += [data[\'query_key\']]\n+                    self.using_history = True\n+                elif \'esearchresult\' in data:\n+                    self.query_key = data[\'esearchresult\'][\'querykey\']\n+                    self.webenv = data[\'esearchresult\'][\'webenv\']\n+                    self.query_keys = []\n+                    self.query_keys += [data[\'esearchresult\'][\'querykey\']]\n+                    self.using_history = True\n+                # elink\n+                elif \'linksets\' in data:\n+                    # elink for cmd=neighbor_history\n+                    if \'linksetdbhistories\' in data[\'linksets\'][0]:\n+                        self.webenv = data[\'linksets\'][0][\'webenv\']\n+                        self.query_key = data[\'linksets\'][0][\'linksetdbhistories\'][0][\'querykey\']\n+                        self.using_history = True\n+                    # elink for cmd=neighbor|neighbor_score\n+                    elif \'linksetdbs\' in data[\'linksets\'][0]:\n+                        self.using_parsedids = True\n+                        # elink for neighbor\n+                        if isinstance(data[\'linksets\'][0][\'linksetdbs\'][0][\'links\'][0], str):\n+                            self.idstr = \',\'.join(data[\'linksets\'][0][\'linksetdbs\'][0][\'links\'])\n+                        # elink for neighbor_score\n+                        else:\n+                            self.idstr = \',\'.join(map(lambda x: x[\'id\'], data[\'linksets\'][0][\'linksetdbs\'][0][\'links\']))\n+                    if \'linksetdbhistories\' in data[\'linksets\'][0]:\n+                        self.webenv = data[\'linksets\'][0][\'webenv\']\n+                        self.query_keys = []\n+                        for query in data[\'linksets\'][0][\'linksetdbhistories\']:\n+                            if \'querykey\' in query:\n+                                self.query_keys += [query[\'querykey\']]\n+                else:\n+                    print("No match")\n+                    print(data)\n \n     def get_history(self):\n-        if not self.using_history:\n-            return {}\n-        else:\n+        if self.using_history:\n             return {\n                 \'query_key\': self.query_key,\n                 \'WebEnv\': self.webenv,\n             }\n+        elif self.using_parsedids:\n+            return {\n+                \'id\': self.idstr,\n+            }\n+        else:\n+            return {}\n+\n+    def get_histories(self):\n+        histories = []\n+        for key in self.query_keys:\n+            histories += [{\'WebEnv\': self.webenv, \'query_key\': key}]\n+        return histories\n \n     def post(self, database, **payload):\n         return json.dumps(Entrez.read(Entrez.epost(database, **payload)), indent=4)\n@@ -50,8 +102,10 @@\n \n         if \'id\' in payload:\n             summary = self.id_summary(db, payload[\'id\'])\n+        elif \'WebEnv\' not in payload or \'query_key\' not in payload:\n+         '..b'_ids(cls, id_list, id, history_file):\n+    def jsonstring2jsondata(cls, json_str):\n+        json_handle = StringIO(json_str)\n+        json_data = json.loads(json_handle.read())\n+        return json_data\n+\n+    @classmethod\n+    def jsonfile2UIlist(cls, json_file):\n+        merged_ids = []\n+        with open(json_file, \'r\') as handle:\n+            json_data = json.loads(handle.read())\n+            for id in cls.jsondata2UIlist(json_data):\n+                merged_ids += [id]\n+        return merged_ids\n+\n+    @classmethod\n+    def jsondata2UIlist(cls, json_data):\n+        merged_ids = []\n+\n+        # Always prioritize the result links as opposed to the search links\n+        # elink - retrieves linked IDs for cmd=neighbor|neighbor_score only\n+        if \'linksets\' in json_data:\n+            for lnk in json_data[\'linksets\'][0][\'linksetdbs\']:\n+                if \'links\' in lnk:\n+                    for id in lnk[\'links\']:\n+                        # elink for neighbor\n+                        if isinstance(id, str):\n+                            merged_ids.append(id)\n+                        # elink for neighbor_score\n+                        else:\n+                            merged_ids.append(id[\'id\'])\n+        # esearch\n+        elif \'esearchresult\' in json_data:\n+            for id in json_data[\'esearchresult\'][\'idlist\']:\n+                merged_ids += [id]\n+\n+        return merged_ids\n+\n+    @classmethod\n+    def xmlfile2UIlist(cls, xml_file):\n+        merged_ids = []\n+        with open(xml_file, \'r\') as handle:\n+            xml_data = Entrez.read(handle)\n+            for id in cls.xmldata2UIlist(xml_data):\n+                merged_ids += [id]\n+        return merged_ids\n+\n+    @classmethod\n+    def xmlstring2UIlist(cls, xml_str):\n+        merged_ids = []\n+        xml_data = Entrez.read(StringIO(xml_str))\n+        for id in cls.xmldata2UIlist(xml_data):\n+            merged_ids += [id]\n+        return merged_ids\n+\n+    @classmethod\n+    def xmldata2UIlist(cls, xml_data):\n+        merged_ids = []\n+\n+        try:\n+            # Always prioritize the result links as opposed to the search links\n+            # elink - retrieves linked IDs for cmd=neighbor|neighbor_score only\n+            if \'LinkSetDb\' in xml_data[0]:\n+                for lnk in xml_data[0][\'LinkSetDb\'][0][\'Link\']:\n+                    # elink for neighbor\n+                    if isinstance(lnk, str):\n+                        merged_ids.append(lnk)\n+                    # elink for neighbor_score\n+                    else:\n+                        merged_ids.append(lnk[\'Id\'])\n+            # esearch\n+            elif \'IdList\' in xml_data:\n+                for id in xml_data[\'IdList\']:\n+                    merged_ids += [id]\n+        # If it was not elink output, we will end up here\n+        except Exception:\n+            # esearch\n+            if \'IdList\' in xml_data:\n+                for id in xml_data[\'IdList\']:\n+                    merged_ids += [id]\n+\n+        return merged_ids\n+\n+    @classmethod\n+    def parse_ids(cls, id_list, id, history_file, xml_file, json_file):\n         """Parse IDs passed on --cli or in a file passed to the cli\n         """\n         merged_ids = []\n@@ -122,8 +334,21 @@\n             with open(id_list, \'r\') as handle:\n                 merged_ids += [x.strip() for x in handle.readlines()]\n \n-        # Exception hanlded here for uniformity\n-        if len(merged_ids) == 0 and history_file is None:\n-            raise Exception("Must provide history file or IDs")\n+        if xml_file is not None:\n+            tmp_ids = cls.xmlfile2UIlist(xml_file)\n+            for id in tmp_ids:\n+                merged_ids += [id]\n+\n+        if json_file is not None:\n+            tmp_ids = cls.jsonfile2UIlist(json_file)\n+            for id in tmp_ids:\n+                merged_ids += [id]\n \n         return merged_ids\n+\n+    @classmethod\n+    def getVersion(cls):\n+        """Return the biopython version\n+        """\n+        import Bio\n+        return Bio.__version__\n'
b
diff -r c6096cd97120 -r e267701c187b generate_macros_xml.pl
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/generate_macros_xml.pl Wed Sep 23 09:48:26 2020 +0000
[
b'@@ -0,0 +1,841 @@\n+#!/usr/bin/env perl\n+\n+#Usage: perl generate_macros_xml.pl > macros.xml\n+\n+#Note, this script uses einfo.py to get database info.  It also uses manually compiled data stored at the bottom of this script that is based on: https://www.ncbi.nlm.nih.gov/books/NBK25499/table/chapter4.T._valid_values_of__retmode_and/?report=objectonly\n+#The data in the table on that page was manipulated to replace nulls with \'none\', remove duplicates, and add missing formats based on correspondence with MLN.\n+\n+##\n+## use einfo to retrieve all the valid databases\n+##\n+\n+print STDERR "Retrieving database list\\n";\n+\n+my $dbxml = `python einfo.py --user_email "planemo@galaxyproject.org" --admin_email "planemo@galaxyproject.org;test@bx.psu.edu"`;\n+\n+my(@dblist);\n+my $dbs     = {};\n+my $dbfroms = {};\n+my $dbnames = {};\n+foreach(split(/\\n/,$dbxml))\n+  {\n+    if(/<DbName>(.+)<\\/DbName>/)\n+      {\n+        my $db = $1;\n+        push(@dblist,$db);\n+        $dbs->{$db}     = 0;\n+        $dbfroms->{$db} = 0;\n+        $dbnames->{$db} = $_;\n+      }\n+  }\n+\n+##\n+## Use einfo to retrieve all the valid links for each database (Note: some databases are not linked)\n+##\n+\n+my $h = {};\n+foreach my $db (sort {$dbnames->{$a} cmp $dbnames->{$b}} @dblist)\n+  {\n+    sleep(2);\n+\n+    print STDERR "Retrieving info for $db\\n";\n+\n+    my $response = `python einfo.py --db $db --user_email "planemo\\@galaxyproject.org" --admin_email "planemo\\@galaxyproject.org;test\\@bx.psu.edu"`;\n+\n+    my $dolinks = 0;\n+    my $link    = "";\n+    my $name    = "";\n+\n+    foreach(split(/\\n/,$response))\n+      {\n+        if(/<LinkList>/)\n+          {\n+            $dolinks = 1;\n+            #Save whether there exist links from this database\n+            $dbfroms->{$db} = 1;\n+          }\n+        elsif(!$dolinks)\n+          {\n+            if(/<MenuName>(.+)<\\/MenuName>/)\n+              {$dbnames->{$db} = "$1 ($db)"}\n+          }\n+        elsif($dolinks)\n+          {\n+            if(/<Name>(.+)<\\/Name>/)\n+              {$link=$1}\n+            elsif(/<Menu>(.*)<\\/Menu>/)\n+              {$name=$1}\n+            elsif(/<DbTo>(.+)<\\/DbTo>/)\n+              {\n+                $dbto=$1;\n+                push(@{$h->{$db}->{$dbto}},[$link,$name]);\n+                $link="";\n+                $name="";\n+              }\n+          }\n+      }\n+  }\n+\n+my @sorted_dblist = sort {$dbnames->{$a} cmp $dbnames->{$b}} @dblist;\n+\n+##\n+## Generate XML to govern the valid databases to use with efetch\n+##\n+\n+my $efetch_dbhash = {}; #->{efetch-compatible-db}->{rettype-retmode-galaxy_format} = format_name (galaxy_format)\n+while(<DATA>)\n+  {\n+    chomp;\n+    my($db,$galaxy_format,$retmode,$rettype,$format_name) = split(/\\t/,$_);\n+    $efetch_dbhash->{$db}->{"$rettype-$retmode-$galaxy_format"} =\n+      "$format_name ($galaxy_format)";\n+  }\n+\n+#EFetch database select list\n+\n+print << \'EOXML\';\n+  <xml name="dbselect_efetch" token_name="db_select" token_label="NCBI Database to Query">\n+    <param name="@NAME@" type="select" label="@LABEL@">\n+EOXML\n+\n+foreach my $db (grep {exists($dbs->{$_})}\n+                sort {$dbnames->{$a} cmp $dbnames->{$b}}\n+                keys(%$efetch_dbhash))\n+  {\n+    my $selected = \'\';\n+    if($db eq \'pubmed\')\n+      {$selected = \' selected="True"\'}\n+    print << "    EOXML";\n+      <option value="$db"$selected>$dbnames->{$db}</option>\n+    EOXML\n+  }\n+\n+print << \'EOXML\';\n+    </param>\n+  </xml>\n+EOXML\n+\n+#EFetch output formats\n+\n+print << \'EOXML\';\n+  <xml name="efetchdb">\n+    <conditional name="db">\n+      <expand macro="dbselect_efetch" />\n+EOXML\n+\n+foreach my $db (grep {exists($dbs->{$_})}\n+                sort {$dbnames->{$a} cmp $dbnames->{$b}}\n+                keys(%$efetch_dbhash))\n+  {\n+    print << "    EOXML";\n+      <when value="$db">\n+        <param name="output_format" type="select" label="Output Format">\n+    EOXML\n+\n+    foreach my $eutils_format (sort {$efetch_dbhash->{$db}->{$a} cmp\n+                                       $efetch_dbhash->{$db}->{$b}}\n+              '..b'FASTA\n+nuccore\tfasta\ttext\tfasta_cds_na\tCDS nucleotide FASTA\n+nuccore\tgenbank\ttext\tgb\tGenBank flat file\n+nuccore\tgenbank\ttext\tgbwithparts\tGenBank flat file with full sequence (contigs)\n+nuccore\ttabular\ttext\tacc\tAccession number(s)\n+nuccore\ttxt\ttext\tft\tFeature table\n+nuccore\ttabular\ttext\tseqid\tSeqID string\n+nuccore\ttabular\ttext\tuilist\tList of UIDs\n+nuccore\ttxt\ttext\tnone\ttext ASN.1\n+nuccore\txml\txml\tdocsum\tDocument summary\n+nuccore\txml\txml\tfasta\tTinySeq\n+nuccore\txml\txml\tgb\tGBSeq\n+nuccore\txml\txml\tgbc\tINSDSeq\n+nuccore\txml\txml\tnative\tFull record\n+nuccore\txml\txml\tuilist\tList of UIDs\n+nucest\tbinary\tasn.1\tnone\tbinary ASN.1\n+nucest\tfasta\ttext\tfasta\tFASTA\n+nucest\tgenbank\ttext\tgb\tGenBank flat file\n+nucest\ttabular\ttext\tacc\tAccession number(s)\n+nucest\ttabular\ttext\tseqid\tSeqID string\n+nucest\ttabular\ttext\tuilist\tList of UIDs\n+nucest\ttxt\ttext\test\tEST report\n+nucest\ttxt\ttext\tnone\ttext ASN.1\n+nucest\txml\txml\tdocsum\tDocument summary\n+nucest\txml\txml\tfasta\tTinySeq\n+nucest\txml\txml\tgb\tGBSeq\n+nucest\txml\txml\tgbc\tINSDSeq\n+nucest\txml\txml\tnative\tFull record\n+nucest\txml\txml\tuilist\tList of UIDs\n+nucgss\tbinary\tasn.1\tnone\tbinary ASN.1\n+nucgss\tfasta\ttext\tfasta\tFASTA\n+nucgss\tgenbank\ttext\tgb\tGenBank flat file\n+nucgss\ttabular\ttext\tacc\tAccession number(s)\n+nucgss\ttabular\ttext\tseqid\tSeqID string\n+nucgss\ttabular\ttext\tuilist\tList of UIDs\n+nucgss\ttxt\ttext\tgss\tGSS report\n+nucgss\ttxt\ttext\tnone\ttext ASN.1\n+nucgss\txml\txml\tdocsum\tDocument summary\n+nucgss\txml\txml\tfasta\tTinySeq\n+nucgss\txml\txml\tgb\tGBSeq\n+nucgss\txml\txml\tgbc\tINSDSeq\n+nucgss\txml\txml\tnative\tFull record\n+nucgss\txml\txml\tuilist\tList of UIDs\n+pmc\ttabular\ttext\tuilist\tList of UIDs\n+pmc\ttxt\ttext\tmedline\tMEDLINE\n+pmc\txml\txml\tdocsum\tDocument summary\n+pmc\txml\txml\tnone\tFULL\n+pmc\txml\txml\tuilist\tList of UIDs\n+popset\tbinary\tasn.1\tnone\tbinary ASN.1\n+popset\tfasta\ttext\tfasta\tFASTA\n+popset\tgenbank\ttext\tgb\tGenBank flat file\n+popset\ttabular\ttext\tacc\tAccession number(s)\n+popset\ttabular\ttext\tseqid\tSeqID string\n+popset\ttabular\ttext\tuilist\tList of UIDs\n+popset\ttxt\ttext\tnone\ttext ASN.1\n+popset\txml\txml\tdocsum\tDocument summary\n+popset\txml\txml\tfasta\tTinySeq\n+popset\txml\txml\tgb\tGBSeq\n+popset\txml\txml\tgbc\tINSDSeq\n+popset\txml\txml\tnative\tFull record\n+popset\txml\txml\tuilist\tList of UIDs\n+protein\tbinary\tasn.1\tnone\tbinary ASN.1\n+protein\tfasta\ttext\tfasta\tFASTA\n+protein\ttabular\ttext\tacc\tAccession number(s)\n+protein\ttxt\ttext\tft\tFeature table\n+protein\ttabular\ttext\tseqid\tSeqID string\n+protein\ttabular\ttext\tuilist\tList of UIDs\n+protein\ttxt\ttext\tgp\tGenPept flat file\n+protein\ttxt\ttext\tnone\ttext ASN.1\n+protein\txml\txml\tdocsum\tDocument summary\n+protein\txml\txml\tfasta\tTinySeq\n+protein\txml\txml\tgp\tGBSeq\n+protein\txml\txml\tgpc\tINSDSeq\n+protein\txml\txml\tipg\tIdentical Protein\n+protein\txml\txml\tnative\tFull record\n+protein\txml\txml\tuilist\tList of UIDs\n+pubmed\ttabular\ttext\tuilist\tList of UIDs\n+pubmed\ttxt\tasn.1\tnone\ttext ASN.1\n+pubmed\ttxt\ttext\tabstract\tAbstract\n+pubmed\ttxt\ttext\tmedline\tMEDLINE\n+pubmed\txml\txml\tdocsum\tDocument summary\n+pubmed\txml\txml\tnone\tFull\n+pubmed\txml\txml\tuilist\tList of UIDs\n+sequences\tfasta\ttext\tfasta\tFASTA\n+sequences\ttabular\ttext\tacc\tAccession number(s)\n+sequences\ttabular\ttext\tseqid\tSeqID string\n+sequences\ttabular\ttext\tuilist\tList of UIDs\n+sequences\ttxt\ttext\tnone\ttext ASN.1\n+sequences\txml\txml\tdocsum\tDocument summary\n+sequences\txml\txml\tuilist\tList of UIDs\n+sequences\txml\tnone\tnone\tFull\n+snp\tfasta\ttext\tfasta\tFASTA\n+snp\tjson\tjson\tdocsum\tDocument summary\n+snp\tjson\tjson\tuilist\tList of UIDs\n+snp\ttabular\ttext\tssexemplar\tSS Exemplar list\n+snp\ttabular\ttext\tuilist\tList of UIDs\n+snp\ttxt\tasn.1\tnone\ttext ASN.1\n+snp\ttxt\ttext\tchr\tChromosome report\n+snp\ttxt\ttext\tdocset\tSummary\n+snp\ttxt\ttext\tflt\tFlat file\n+snp\ttxt\ttext\trsr\tRS Cluster report\n+snp\txml\txml\tdocsum\tDocument summary\n+snp\txml\txml\tnone\tXML\n+snp\txml\txml\tuilist\tList of UIDs\n+sra\ttabular\ttext\tuilist\tList of UIDs\n+sra\txml\txml\tdocsum\tDocument summary\n+sra\txml\txml\tfull\tFull\n+taxonomy\ttabular\ttext\tuilist\tList of UIDs\n+taxonomy\txml\txml\tnone\tFull\n+taxonomy\txml\txml\tdocsum\tDocument summary\n+taxonomy\txml\txml\tuilist\tList of UIDs\n'
b
diff -r c6096cd97120 -r e267701c187b macros.xml
--- a/macros.xml Wed Mar 11 04:03:14 2020 -0400
+++ b/macros.xml Wed Sep 23 09:48:26 2020 +0000
[
b'@@ -1,7 +1,7 @@\n <?xml version="1.0"?>\n <macros>\n   <token name="@PROFILE@">18.01</token>\n-  <token name="@WRAPPER_VERSION@">1.3</token>\n+  <token name="@WRAPPER_VERSION@">1.70</token>\n   <token name="@EMAIL_ARGUMENTS@">\n --user_email "$__user_email__"\n #set admin_emails = \';\'.join(str($__admin_users__).split(\',\'))\n@@ -86,743 +86,51 @@\n and licensing restrictions of linked pages and to secure all necessary\n permissions.\n         ]]></token>\n-    <xml name="dbselect"\n-        token_name="db_select"\n-        token_label="NCBI Database to Use"\n-        >\n-    <param name="@NAME@" type="select" label="@LABEL@">\n-      <option value="annotinfo">Annotation Information</option>\n-      <option value="assembly">Assembly</option>\n-      <option value="bioproject">BioProject</option>\n-      <option value="biosample">BioSample</option>\n-      <option value="biosystems">Biosystems</option>\n-      <option value="blastdbinfo">Blast Database Information</option>\n-      <option value="books">Books</option>\n-      <option value="cdd">Conserved Domains</option>\n-      <option value="clinvar">Clinical Variants</option>\n-      <option value="clone">CLone</option>\n-      <option value="dbvar">dbVar</option>\n-      <option value="gap">dbGaP</option>\n-      <option value="gapplus">gapplus</option>\n-      <option value="gds">GEO Datasets</option>\n-      <option value="gencoll">Gencoll</option>\n-      <option value="gene">Gene</option>\n-      <option value="genome">Genome</option>\n-      <option value="geoprofiles">GEO Profiles</option>\n-      <option value="grasp">grasp</option>\n-      <option value="gtr">Genetic Testing Registry</option>\n-      <option value="homologene">HomoloGene</option>\n-      <option value="medgen">MedGen</option>\n-      <option value="mesh">MeSH</option>\n-      <option value="ncbisearch">NCBI Web Site</option>\n-      <option value="nlmcatalog">NLM Catalog</option>\n-      <option value="nuccore">Nuccore</option>\n-      <option value="nucest">EST</option>\n-      <option value="nucgss">GSS</option>\n-      <option value="nucleotide">Nucleotide</option>\n-      <option value="omim">OMIM</option>\n-      <option value="orgtrack">Orgtrack</option>\n-      <option value="pcassay">PubChem BioAssay</option>\n-      <option value="pccompound">PubChem Compound</option>\n-      <option value="pcsubstance">PubChem Substance</option>\n-      <option value="pmc">PubMed Central</option>\n-      <option value="popset">PopSet</option>\n-      <option value="probe">Probe</option>\n-      <option value="protein">Protein</option>\n-      <option value="proteinclusters">Protein Clusters</option>\n-      <option value="pubmed">PubMed</option>\n-      <option value="pubmedhealth">PubMed Health</option>\n-      <option value="seqannot">seqannot</option>\n-      <option value="snp">SNP</option>\n-      <option value="sra">SRA</option>\n-      <option value="structure">Structure</option>\n-      <option value="taxonomy">Taxonomy</option>\n-      <option value="unigene">UniGene</option>\n-    </param>\n-  </xml>\n-  <xml name="db">\n-    <conditional name="db">\n-      <expand macro="dbselect" />\n-      <when value="annotinfo">\n-        <param name="output_format" type="select" label="Output Format">\n-          <option value="docsum-json">Document Summary (json)</option>\n-          <option value="docsum-xml">Document Summary (xml)</option>\n-          <option value="full-text">Full Document (text)</option>\n-          <option value="full-xml">Full Document (xml)</option>\n-          <option value="uilist-text">Unique Identifier List (text)</option>\n-          <option value="uilist-xml">Unique Identifier List (xml)</option>\n-        </param>\n-      </when>\n-      <when value="assembly">\n-        <param name="output_format" type="select" label="Output Format">\n-          <option value="docsum-json">Document Summary (json)</option>\n-          <option value="docsum-xml">Document Summary (xml)</option>\n-          <option value="full-text">Full Document (text)</option>\n-          <optio'..b'tion value="None">All Links</option>\n+                  <option value="dbvar_sparcle">Functional Class (dbvar_sparcle)</option>\n+                </param>\n+              </when>\n+              <when value="taxonomy">\n+                <param name="linkname" type="select" label="Link Name">\n+                  <option value="None">All Links</option>\n+                  <option value="dbvar_taxonomy">Taxonomy Links (dbvar_taxonomy)</option>\n+                </param>\n+              </when>\n+              <when value="gap">\n+                <param name="linkname" type="select" label="Link Name">\n+                  <option value="None">All Links</option>\n+                  <option value="dbvar_gap">dbGaP Links (dbvar_gap)</option>\n+                </param>\n+              </when>\n+            </conditional>\n+          </when>\n+          <when value="grasp">\n+            <conditional name="db_to">\n+              <param name="db_select_to" type="select" label="To NCBI Database">\n+                <option value="grasp">grasp (grasp)</option>\n+              </param>\n+              <when value="grasp">\n+                <param name="linkname" type="select" label="Link Name">\n+                  <option value="none">All Links</option>\n+                </param>\n+              </when>\n+            </conditional>\n+          </when>\n+          <when value="annotinfo">\n+            <conditional name="db_to">\n+              <param name="db_select_to" type="select" label="To NCBI Database">\n+                <option value="none">Not applicable</option>\n+              </param>\n+              <when value="none">\n+                <param name="linkname" type="select" label="Link Name">\n+                  <option value="none">Not applicable</option>\n+                </param>\n+              </when>\n+            </conditional>\n+          </when>\n+          <when value="gapplus">\n+            <conditional name="db_to">\n+              <param name="db_select_to" type="select" label="To NCBI Database">\n+                <option value="none">Not applicable</option>\n+              </param>\n+              <when value="none">\n+                <param name="linkname" type="select" label="Link Name">\n+                  <option value="none">Not applicable</option>\n+                </param>\n+              </when>\n+            </conditional>\n+          </when>\n+          <when value="ncbisearch">\n+            <conditional name="db_to">\n+              <param name="db_select_to" type="select" label="To NCBI Database">\n+                <option value="none">Not applicable</option>\n+              </param>\n+              <when value="none">\n+                <param name="linkname" type="select" label="Link Name">\n+                  <option value="none">Not applicable</option>\n+                </param>\n+              </when>\n+            </conditional>\n+          </when>\n+          <when value="grasp">\n+            <conditional name="db_to">\n+              <param name="db_select_to" type="select" label="To NCBI Database">\n+                <option value="none">Not applicable</option>\n+              </param>\n+              <when value="none">\n+                <param name="linkname" type="select" label="Link Name">\n+                  <option value="none">Not applicable</option>\n+                </param>\n+              </when>\n+            </conditional>\n+          </when>\n+    </conditional>\n+  </xml>\n+  <token name="@LINK_TOKEN@">\n+    <![CDATA[\n+#if $cmd.db_from_link.db_to.db_select_to == \'n/a\':\n+    none\n+#else:\n+    $cmd.db_from_link.db_to.db_select_to\n+#end if\n+\n+$cmd.db_from_link.db_select_from_link\n+\n+$cmd.cmd_select\n+\n+#if $cmd.output_format == \'json\':\n+    --retmode json\n+#elif $cmd.output_format == \'text\':\n+    --retmode uilist\n+#else:\n+    --retmode xml\n+#end if\n+\n+#if $cmd.db_from_link.db_to.linkname != \'None\' and $cmd.cmd_select in (\'neighbor\', \'neighbor_history\', \'neighbor_score\'):\n+    --linkname $cmd.db_from_link.db_to.linkname\n+#end if\n+    ]]>\n+  </token>\n </macros>\n'
b
diff -r c6096cd97120 -r e267701c187b test-data/efetchin.tabular
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test-data/efetchin.tabular Wed Sep 23 09:48:26 2020 +0000
b
@@ -0,0 +1,2 @@
+1899688395
+1896832511
b
diff -r c6096cd97120 -r e267701c187b test-data/einfo.dblist.xml
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test-data/einfo.dblist.xml Wed Sep 23 09:48:26 2020 +0000
b
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE eInfoResult PUBLIC "-//NLM//DTD einfo 20190110//EN" "https://eutils.ncbi.nlm.nih.gov/eutils/dtd/20190110/einfo.dtd">
+<eInfoResult>
+<DbList>
+
+ <DbName>pubmed</DbName>
+ <DbName>protein</DbName>
+ <DbName>nuccore</DbName>
+ <DbName>ipg</DbName>
+ <DbName>nucleotide</DbName>
+ <DbName>structure</DbName>
+ <DbName>sparcle</DbName>
+ <DbName>genome</DbName>
+ <DbName>annotinfo</DbName>
+ <DbName>assembly</DbName>
+ <DbName>bioproject</DbName>
+ <DbName>biosample</DbName>
+ <DbName>blastdbinfo</DbName>
+ <DbName>books</DbName>
+ <DbName>cdd</DbName>
+ <DbName>clinvar</DbName>
+ <DbName>gap</DbName>
+ <DbName>gapplus</DbName>
+ <DbName>grasp</DbName>
+ <DbName>dbvar</DbName>
+ <DbName>gene</DbName>
+ <DbName>gds</DbName>
+ <DbName>geoprofiles</DbName>
+ <DbName>homologene</DbName>
+ <DbName>medgen</DbName>
+ <DbName>mesh</DbName>
+ <DbName>ncbisearch</DbName>
+ <DbName>nlmcatalog</DbName>
+ <DbName>omim</DbName>
+ <DbName>orgtrack</DbName>
+ <DbName>pmc</DbName>
+ <DbName>popset</DbName>
+ <DbName>proteinclusters</DbName>
+ <DbName>pcassay</DbName>
+ <DbName>biosystems</DbName>
+ <DbName>pccompound</DbName>
+ <DbName>pcsubstance</DbName>
+ <DbName>seqannot</DbName>
+ <DbName>snp</DbName>
+ <DbName>sra</DbName>
+ <DbName>taxonomy</DbName>
+ <DbName>biocollections</DbName>
+ <DbName>gtr</DbName>
+</DbList>
+
+</eInfoResult>
+
b
diff -r c6096cd97120 -r e267701c187b test-data/elink.elinkin_1link.json
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test-data/elink.elinkin_1link.json Wed Sep 23 09:48:26 2020 +0000
[
@@ -0,0 +1,23 @@
+{
+    "header": {
+        "type": "elink",
+        "version": "0.3"
+    },
+    "linksets": [
+        {
+            "dbfrom": "gene",
+            "ids": [
+                "118502329"
+            ],
+            "linksetdbs": [
+                {
+                    "dbto": "nuccore",
+                    "linkname": "gene_nuccore_refseqrna",
+                    "links": [
+                        "1899688395"
+                    ]
+                }
+            ]
+        }
+    ]
+}
b
diff -r c6096cd97120 -r e267701c187b test-data/elink.elinkin_1link_hist.json
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test-data/elink.elinkin_1link_hist.json Wed Sep 23 09:48:26 2020 +0000
[
@@ -0,0 +1,22 @@
+{
+    "header": {
+        "type": "elink",
+        "version": "0.3"
+    },
+    "linksets": [
+        {
+            "dbfrom": "nuccore",
+            "ids": [
+                "1899688395"
+            ],
+            "linksetdbhistories": [
+                {
+                    "dbto": "gene",
+                    "linkname": "nuccore_gene",
+                    "querykey": "1"
+                }
+            ],
+            "webenv": "MCID_5f60d98126049170ce66fe2e"
+        }
+    ]
+}
b
diff -r c6096cd97120 -r e267701c187b test-data/elink.elinkin_1link_id.tabular
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test-data/elink.elinkin_1link_id.tabular Wed Sep 23 09:48:26 2020 +0000
b
@@ -0,0 +1,1 @@
+118502329
b
diff -r c6096cd97120 -r e267701c187b test-data/elink.elinkin_allalllinks_id.xml
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test-data/elink.elinkin_allalllinks_id.xml Wed Sep 23 09:48:26 2020 +0000
b
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE eLinkResult PUBLIC "-//NLM//DTD elink 20101123//EN" "https://eutils.ncbi.nlm.nih.gov/eutils/dtd/20101123/elink.dtd">
+<eLinkResult>
+<LinkSet>
+ <DbFrom>nuccore</DbFrom>
+ <IdList>
+ <Id>1899688395</Id>
+ <Id>1896832511</Id>
+ </IdList>
+ <LinkSetDbHistory>
+ <DbTo>gene</DbTo>
+ <LinkName>nuccore_gene</LinkName>
+ <QueryKey>1</QueryKey>
+ </LinkSetDbHistory>
+ <WebEnv>MCID_5f60e00e98743c5c3572195e</WebEnv>
+</LinkSet>
+</eLinkResult>
+
b
diff -r c6096cd97120 -r e267701c187b test-data/elink.elinkin_alllinks_id.xml
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test-data/elink.elinkin_alllinks_id.xml Wed Sep 23 09:48:26 2020 +0000
b
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE eLinkResult PUBLIC "-//NLM//DTD elink 20101123//EN" "https://eutils.ncbi.nlm.nih.gov/eutils/dtd/20101123/elink.dtd">
+<eLinkResult>
+
+  <LinkSet>
+    <DbFrom>gene</DbFrom>
+    <IdList>
+      <Id>118502329</Id>
+    </IdList>
+    <LinkSetDb>
+      <DbTo>nuccore</DbTo>
+      <LinkName>gene_nuccore</LinkName>
+      
+        <Link>
+ <Id>1899688395</Id>
+ </Link>
+        <Link>
+ <Id>1896832511</Id>
+ </Link>
+      
+    </LinkSetDb>
+    
+    <LinkSetDb>
+      <DbTo>nuccore</DbTo>
+      <LinkName>gene_nuccore_pos</LinkName>
+      
+        <Link>
+ <Id>1896832511</Id>
+ </Link>
+      
+    </LinkSetDb>
+    
+    <LinkSetDb>
+      <DbTo>nuccore</DbTo>
+      <LinkName>gene_nuccore_refseqrna</LinkName>
+      
+        <Link>
+ <Id>1899688395</Id>
+ </Link>
+      
+    </LinkSetDb>
+  </LinkSet>
+</eLinkResult>
+
b
diff -r c6096cd97120 -r e267701c187b test-data/elink.esearch_in_xmlid.xml
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test-data/elink.esearch_in_xmlid.xml Wed Sep 23 09:48:26 2020 +0000
[
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE eSearchResult PUBLIC "-//NLM//DTD esearch 20060628//EN" "https://eutils.ncbi.nlm.nih.gov/eutils/dtd/20060628/esearch.dtd">
+<eSearchResult><Count>1</Count><RetMax>1</RetMax><RetStart>0</RetStart><IdList>
+<Id>118502329</Id>
+</IdList><TranslationSet/><TranslationStack>   <TermSet>    <Term>118502329[UID]</Term>    <Field>UID</Field>    <Count>-1</Count>    <Explode>N</Explode>   </TermSet>   <OP>GROUP</OP>  </TranslationStack><QueryTranslation>118502329[UID]</QueryTranslation></eSearchResult>
+
b
diff -r c6096cd97120 -r e267701c187b test-data/elink.esearch_in_xmlid_1link.tabular
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test-data/elink.esearch_in_xmlid_1link.tabular Wed Sep 23 09:48:26 2020 +0000
b
@@ -0,0 +1,2 @@
+1899688395
+1896832511
b
diff -r c6096cd97120 -r e267701c187b test-data/elink.esearchin_id.json
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test-data/elink.esearchin_id.json Wed Sep 23 09:48:26 2020 +0000
[
@@ -0,0 +1,25 @@
+{
+    "header": {
+        "type": "esearch",
+        "version": "0.3"
+    },
+    "esearchresult": {
+        "count": "1",
+        "retmax": "1",
+        "retstart": "0",
+        "idlist": [
+            "118502329"
+        ],
+        "translationset": [],
+        "translationstack": [
+            {
+                "term": "118502329[UID]",
+                "field": "UID",
+                "count": "-1",
+                "explode": "N"
+            },
+            "GROUP"
+        ],
+        "querytranslation": "118502329[UID]"
+    }
+}
b
diff -r c6096cd97120 -r e267701c187b test-data/elink.esearchin_id.tabular
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test-data/elink.esearchin_id.tabular Wed Sep 23 09:48:26 2020 +0000
b
@@ -0,0 +1,1 @@
+118502329
b
diff -r c6096cd97120 -r e267701c187b test-data/elink.esearchin_id_1link.xml
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test-data/elink.esearchin_id_1link.xml Wed Sep 23 09:48:26 2020 +0000
b
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE eLinkResult PUBLIC "-//NLM//DTD elink 20101123//EN" "https://eutils.ncbi.nlm.nih.gov/eutils/dtd/20101123/elink.dtd">
+<eLinkResult>
+
+  <LinkSet>
+    <DbFrom>gene</DbFrom>
+    <IdList>
+      <Id>118502329</Id>
+    </IdList>
+    <LinkSetDb>
+      <DbTo>nuccore</DbTo>
+      <LinkName>gene_nuccore_refseqrna</LinkName>
+      
+        <Link>
+ <Id>1899688395</Id>
+ </Link>
+      
+    </LinkSetDb>
+  </LinkSet>
+</eLinkResult>
+
b
diff -r c6096cd97120 -r e267701c187b test-data/elink.esearchin_id_alllinks.json
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test-data/elink.esearchin_id_alllinks.json Wed Sep 23 09:48:26 2020 +0000
[
@@ -0,0 +1,38 @@
+{
+    "header": {
+        "type": "elink",
+        "version": "0.3"
+    },
+    "linksets": [
+        {
+            "dbfrom": "gene",
+            "ids": [
+                "118502329"
+            ],
+            "linksetdbs": [
+                {
+                    "dbto": "nuccore",
+                    "linkname": "gene_nuccore",
+                    "links": [
+                        "1899688395",
+                        "1896832511"
+                    ]
+                },
+                {
+                    "dbto": "nuccore",
+                    "linkname": "gene_nuccore_pos",
+                    "links": [
+                        "1896832511"
+                    ]
+                },
+                {
+                    "dbto": "nuccore",
+                    "linkname": "gene_nuccore_refseqrna",
+                    "links": [
+                        "1899688395"
+                    ]
+                }
+            ]
+        }
+    ]
+}
b
diff -r c6096cd97120 -r e267701c187b test-data/esearch.gene.hist.json
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test-data/esearch.gene.hist.json Wed Sep 23 09:48:26 2020 +0000
[
@@ -0,0 +1,27 @@
+{
+    "header": {
+        "type": "esearch",
+        "version": "0.3"
+    },
+    "esearchresult": {
+        "count": "1",
+        "retmax": "1",
+        "retstart": "0",
+        "querykey": "1",
+        "webenv": "MCID_5f5fd696d2dc7951442b7849",
+        "idlist": [
+            "118502329"
+        ],
+        "translationset": [],
+        "translationstack": [
+            {
+                "term": "118502329[UID]",
+                "field": "UID",
+                "count": "-1",
+                "explode": "N"
+            },
+            "GROUP"
+        ],
+        "querytranslation": "118502329[UID]"
+    }
+}
b
diff -r c6096cd97120 -r e267701c187b test-data/esearch.gene.hist.xml
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test-data/esearch.gene.hist.xml Wed Sep 23 09:48:26 2020 +0000
[
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE eSearchResult PUBLIC "-//NLM//DTD esearch 20060628//EN" "https://eutils.ncbi.nlm.nih.gov/eutils/dtd/20060628/esearch.dtd">
+<eSearchResult><Count>1</Count><RetMax>1</RetMax><RetStart>0</RetStart><QueryKey>1</QueryKey><WebEnv>MCID_5f5fd690407bc55fc76bad4e</WebEnv><IdList>
+<Id>118502329</Id>
+</IdList><TranslationSet/><TranslationStack>   <TermSet>    <Term>118502329[UID]</Term>    <Field>UID</Field>    <Count>-1</Count>    <Explode>N</Explode>   </TermSet>   <OP>GROUP</OP>  </TranslationStack><QueryTranslation>118502329[UID]</QueryTranslation></eSearchResult>
+
b
diff -r c6096cd97120 -r e267701c187b test-data/esearch.gene.json
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test-data/esearch.gene.json Wed Sep 23 09:48:26 2020 +0000
[
@@ -0,0 +1,25 @@
+{
+    "header": {
+        "type": "esearch",
+        "version": "0.3"
+    },
+    "esearchresult": {
+        "count": "1",
+        "retmax": "1",
+        "retstart": "0",
+        "idlist": [
+            "118502329"
+        ],
+        "translationset": [],
+        "translationstack": [
+            {
+                "term": "118502329[UID]",
+                "field": "UID",
+                "count": "-1",
+                "explode": "N"
+            },
+            "GROUP"
+        ],
+        "querytranslation": "118502329[UID]"
+    }
+}
b
diff -r c6096cd97120 -r e267701c187b test-data/esearch.gene.tabular
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test-data/esearch.gene.tabular Wed Sep 23 09:48:26 2020 +0000
b
@@ -0,0 +1,22 @@
+106632260
+100008587
+106632264
+106631781
+109910382
+109910381
+109910380
+109910379
+109864282
+109864281
+109864280
+109864279
+109864274
+109864273
+109864272
+109864271
+106631777
+100861532
+100169758
+100169768
+100169767
+100169766
b
diff -r c6096cd97120 -r e267701c187b test-data/esearch.gene.xml
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test-data/esearch.gene.xml Wed Sep 23 09:48:26 2020 +0000
[
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE eSearchResult PUBLIC "-//NLM//DTD esearch 20060628//EN" "https://eutils.ncbi.nlm.nih.gov/eutils/dtd/20060628/esearch.dtd">
+<eSearchResult><Count>1</Count><RetMax>1</RetMax><RetStart>0</RetStart><IdList>
+<Id>118502329</Id>
+</IdList><TranslationSet/><TranslationStack>   <TermSet>    <Term>118502329[UID]</Term>    <Field>UID</Field>    <Count>-1</Count>    <Explode>N</Explode>   </TermSet>   <OP>GROUP</OP>  </TranslationStack><QueryTranslation>118502329[UID]</QueryTranslation></eSearchResult>
+