Repository 'cpt_annotation_table'
hg clone https://toolshed.g2.bx.psu.edu/repos/cpt/cpt_annotation_table

Changeset 1:32e011fa615c (2023-04-26)
Previous changeset 0:6a4d1bd8ac1d (2022-06-17) Next changeset 2:6998c2570145 (2023-07-23)
Commit message:
planemo upload commit edc74553919d09dcbe27fcadf144612c1ad3a2a2
added:
cpt-macros.xml
gff3.py
macros.xml
phage_annotation_table.py
phage_annotation_table.xml
phageqc_report_annotation_table.html
phageqc_report_annotation_table.tsv
test-data/NC_001416_Table_In.fasta
test-data/NC_001416_Table_In.gff3
test-data/PhageTable_Out.html
test-data/PhageTable_Out.tabular
removed:
cpt_annotation_table/cpt-macros.xml
cpt_annotation_table/gff3.py
cpt_annotation_table/macros.xml
cpt_annotation_table/phage_annotation_table.py
cpt_annotation_table/phage_annotation_table.xml
cpt_annotation_table/phageqc_report_annotation_table.html
cpt_annotation_table/phageqc_report_annotation_table.tsv
cpt_annotation_table/test-data/NC_001416_Table_In.fasta
cpt_annotation_table/test-data/NC_001416_Table_In.gff3
cpt_annotation_table/test-data/PhageTable_Out.html
cpt_annotation_table/test-data/PhageTable_Out.tabular
b
diff -r 6a4d1bd8ac1d -r 32e011fa615c cpt-macros.xml
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/cpt-macros.xml Wed Apr 26 03:42:32 2023 +0000
[
@@ -0,0 +1,115 @@
+<macros>
+    <xml name="gff_requirements">
+        <requirements>
+            <requirement type="package" version="2.7">python</requirement>
+            <requirement type="package" version="1.65">biopython</requirement>
+            <requirement type="package" version="2.12.1">requests</requirement>
+ <requirement type="package" version="1.2.2">cpt_gffparser</requirement>
+            <yield/>
+        </requirements>
+        <version_command>
+ <![CDATA[
+ cd '$__tool_directory__' && git rev-parse HEAD
+ ]]>
+ </version_command>
+    </xml>
+    <xml name="citation/mijalisrasche">
+        <citation type="doi">10.1371/journal.pcbi.1008214</citation>
+        <citation type="bibtex">@unpublished{galaxyTools,
+ author = {E. Mijalis, H. Rasche},
+ title = {CPT Galaxy Tools},
+ year = {2013-2017},
+ note = {https://github.com/tamu-cpt/galaxy-tools/}
+ }
+ </citation>
+    </xml>
+    <xml name="citations">
+        <citations>
+            <citation type="doi">10.1371/journal.pcbi.1008214</citation>
+            <citation type="bibtex">
+ @unpublished{galaxyTools,
+ author = {E. Mijalis, H. Rasche},
+ title = {CPT Galaxy Tools},
+ year = {2013-2017},
+ note = {https://github.com/tamu-cpt/galaxy-tools/}
+ }
+ </citation>
+            <yield/>
+        </citations>
+    </xml>
+    <xml name="citations-crr">
+        <citations>
+            <citation type="doi">10.1371/journal.pcbi.1008214</citation>
+            <citation type="bibtex">
+ @unpublished{galaxyTools,
+ author = {C. Ross},
+ title = {CPT Galaxy Tools},
+ year = {2020-},
+ note = {https://github.com/tamu-cpt/galaxy-tools/}
+ }
+ </citation>
+            <yield/>
+        </citations>
+    </xml>
+    <xml name="citations-2020">
+        <citations>
+            <citation type="doi">10.1371/journal.pcbi.1008214</citation>
+            <citation type="bibtex">
+ @unpublished{galaxyTools,
+ author = {E. Mijalis, H. Rasche},
+ title = {CPT Galaxy Tools},
+ year = {2013-2017},
+ note = {https://github.com/tamu-cpt/galaxy-tools/}
+ }
+ </citation>
+            <citation type="bibtex">
+ @unpublished{galaxyTools,
+ author = {A. Criscione},
+ title = {CPT Galaxy Tools},
+ year = {2019-2021},
+ note = {https://github.com/tamu-cpt/galaxy-tools/}
+ }
+                        </citation>
+            <yield/>
+        </citations>
+    </xml>
+    <xml name="citations-2020-AJC-solo">
+        <citations>
+            <citation type="doi">10.1371/journal.pcbi.1008214</citation>
+            <citation type="bibtex">
+ @unpublished{galaxyTools,
+ author = {A. Criscione},
+ title = {CPT Galaxy Tools},
+ year = {2019-2021},
+ note = {https://github.com/tamu-cpt/galaxy-tools/}
+ }
+                        </citation>
+            <yield/>
+        </citations>
+    </xml>
+    <xml name="citations-clm">
+        <citations>
+            <citation type="doi">10.1371/journal.pcbi.1008214</citation>
+            <citation type="bibtex">
+ @unpublished{galaxyTools,
+ author = {C. Maughmer},
+ title = {CPT Galaxy Tools},
+ year = {2017-2020},
+ note = {https://github.com/tamu-cpt/galaxy-tools/}
+ }
+ </citation>
+            <yield/>
+        </citations>
+    </xml>
+    <xml name="sl-citations-clm">
+        <citation type="bibtex">
+ @unpublished{galaxyTools,
+ author = {C. Maughmer},
+ title = {CPT Galaxy Tools},
+ year = {2017-2020},
+ note = {https://github.com/tamu-cpt/galaxy-tools/}
+ }
+ </citation>
+        <yield/>
+    </xml>
+</macros>
b
diff -r 6a4d1bd8ac1d -r 32e011fa615c cpt_annotation_table/cpt-macros.xml
--- a/cpt_annotation_table/cpt-macros.xml Fri Jun 17 12:19:58 2022 +0000
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
[
@@ -1,115 +0,0 @@
-<?xml version="1.0"?>
-<macros>
- <xml name="gff_requirements">
- <requirements>
- <requirement type="package" version="2.7">python</requirement>
- <requirement type="package" version="1.65">biopython</requirement>
- <requirement type="package" version="2.12.1">requests</requirement>
- <yield/>
- </requirements>
- <version_command>
- <![CDATA[
- cd $__tool_directory__ && git rev-parse HEAD
- ]]>
- </version_command>
- </xml>
- <xml name="citation/mijalisrasche">
- <citation type="doi">10.1371/journal.pcbi.1008214</citation>
- <citation type="bibtex">@unpublished{galaxyTools,
- author = {E. Mijalis, H. Rasche},
- title = {CPT Galaxy Tools},
- year = {2013-2017},
- note = {https://github.com/tamu-cpt/galaxy-tools/}
- }
- </citation>
- </xml>
- <xml name="citations">
- <citations>
- <citation type="doi">10.1371/journal.pcbi.1008214</citation>
- <citation type="bibtex">
- @unpublished{galaxyTools,
- author = {E. Mijalis, H. Rasche},
- title = {CPT Galaxy Tools},
- year = {2013-2017},
- note = {https://github.com/tamu-cpt/galaxy-tools/}
- }
- </citation> 
- <yield/>
- </citations>
- </xml>
-     <xml name="citations-crr">
- <citations>
- <citation type="doi">10.1371/journal.pcbi.1008214</citation>
- <citation type="bibtex">
- @unpublished{galaxyTools,
- author = {C. Ross},
- title = {CPT Galaxy Tools},
- year = {2020-},
- note = {https://github.com/tamu-cpt/galaxy-tools/}
- }
- </citation>
- <yield/>
- </citations>
- </xml>
-        <xml name="citations-2020">
- <citations>
- <citation type="doi">10.1371/journal.pcbi.1008214</citation>
- <citation type="bibtex">
- @unpublished{galaxyTools,
- author = {E. Mijalis, H. Rasche},
- title = {CPT Galaxy Tools},
- year = {2013-2017},
- note = {https://github.com/tamu-cpt/galaxy-tools/}
- }
- </citation>
-                        <citation type="bibtex">
- @unpublished{galaxyTools,
- author = {A. Criscione},
- title = {CPT Galaxy Tools},
- year = {2019-2021},
- note = {https://github.com/tamu-cpt/galaxy-tools/}
- }
-                        </citation>
-                        <yield/>
- </citations>
- </xml>
-        <xml name="citations-2020-AJC-solo">
- <citations>
- <citation type="doi">10.1371/journal.pcbi.1008214</citation>
-                        <citation type="bibtex">
- @unpublished{galaxyTools,
- author = {A. Criscione},
- title = {CPT Galaxy Tools},
- year = {2019-2021},
- note = {https://github.com/tamu-cpt/galaxy-tools/}
- }
-                        </citation>
-                        <yield/>
- </citations>
- </xml>
-        <xml name="citations-clm">
- <citations>
- <citation type="doi">10.1371/journal.pcbi.1008214</citation>
- <citation type="bibtex">
- @unpublished{galaxyTools,
- author = {C. Maughmer},
- title = {CPT Galaxy Tools},
- year = {2017-2020},
- note = {https://github.com/tamu-cpt/galaxy-tools/}
- }
- </citation>
-                        <yield/>
- </citations>
- </xml>
-        <xml name="sl-citations-clm">
- <citation type="bibtex">
- @unpublished{galaxyTools,
- author = {C. Maughmer},
- title = {CPT Galaxy Tools},
- year = {2017-2020},
- note = {https://github.com/tamu-cpt/galaxy-tools/}
- }
- </citation>
-                        <yield/>
- </xml>
-</macros>
b
diff -r 6a4d1bd8ac1d -r 32e011fa615c cpt_annotation_table/gff3.py
--- a/cpt_annotation_table/gff3.py Fri Jun 17 12:19:58 2022 +0000
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
[
b'@@ -1,346 +0,0 @@\n-import copy\n-import logging\n-\n-log = logging.getLogger()\n-log.setLevel(logging.WARN)\n-\n-\n-def feature_lambda(\n-    feature_list,\n-    test,\n-    test_kwargs,\n-    subfeatures=True,\n-    parent=None,\n-    invert=False,\n-    recurse=True,\n-):\n-    """Recursively search through features, testing each with a test function, yielding matches.\n-\n-    GFF3 is a hierachical data structure, so we need to be able to recursively\n-    search through features. E.g. if you\'re looking for a feature with\n-    ID=\'bob.42\', you can\'t just do a simple list comprehension with a test\n-    case. You don\'t know how deeply burried bob.42 will be in the feature tree. This is where feature_lambda steps in.\n-\n-    :type feature_list: list\n-    :param feature_list: an iterable of features\n-\n-    :type test: function reference\n-    :param test: a closure with the method signature (feature, **kwargs) where\n-                 the kwargs are those passed in the next argument. This\n-                 function should return True or False, True if the feature is\n-                 to be yielded as part of the main feature_lambda function, or\n-                 False if it is to be ignored. This function CAN mutate the\n-                 features passed to it (think "apply").\n-\n-    :type test_kwargs: dictionary\n-    :param test_kwargs: kwargs to pass to your closure when it is called.\n-\n-    :type subfeatures: boolean\n-    :param subfeatures: when a feature is matched, should just that feature be\n-                        yielded to the caller, or should the entire sub_feature\n-                        tree for that feature be included? subfeatures=True is\n-                        useful in cases such as searching for a gene feature,\n-                        and wanting to know what RBS/Shine_Dalgarno_sequences\n-                        are in the sub_feature tree (which can be accomplished\n-                        with two feature_lambda calls). subfeatures=False is\n-                        useful in cases when you want to process (and possibly\n-                        return) the entire feature tree, such as applying a\n-                        qualifier to every single feature.\n-\n-    :type invert: boolean\n-    :param invert: Negate/invert the result of the filter.\n-\n-    :rtype: yielded list\n-    :return: Yields a list of matching features.\n-    """\n-    # Either the top level set of [features] or the subfeature attribute\n-    for feature in feature_list:\n-        feature._parent = parent\n-        if not parent:\n-            # Set to self so we cannot go above root.\n-            feature._parent = feature\n-        test_result = test(feature, **test_kwargs)\n-        # if (not invert and test_result) or (invert and not test_result):\n-        if invert ^ test_result:\n-            if not subfeatures:\n-                feature_copy = copy.deepcopy(feature)\n-                feature_copy.sub_features = list()\n-                yield feature_copy\n-            else:\n-                yield feature\n-\n-        if recurse and hasattr(feature, "sub_features"):\n-            for x in feature_lambda(\n-                feature.sub_features,\n-                test,\n-                test_kwargs,\n-                subfeatures=subfeatures,\n-                parent=feature,\n-                invert=invert,\n-                recurse=recurse,\n-            ):\n-                yield x\n-\n-\n-def fetchParent(feature):\n-    if not hasattr(feature, "_parent") or feature._parent is None:\n-        return feature\n-    else:\n-        return fetchParent(feature._parent)\n-\n-\n-def feature_test_true(feature, **kwargs):\n-    return True\n-\n-\n-def feature_test_type(feature, **kwargs):\n-    if "type" in kwargs:\n-        return str(feature.type).upper() == str(kwargs["type"]).upper()\n-    elif "types" in kwargs:\n-      for x in kwargs["types"]:\n-        if str(feature.type).upper() == str(x).upper():\n-          return True\n-      return False\n-    raise Exception("Incorrect feature_test_type call, ne'..b'feature.location.start,\n-        # feature.location.end,\n-        # feature.location.strand\n-        # )\n-    return result\n-\n-\n-def get_gff3_id(gene):\n-    return gene.qualifiers.get("Name", [gene.id])[0]\n-\n-\n-def ensure_location_in_bounds(start=0, end=0, parent_length=0):\n-    # This prevents frameshift errors\n-    while start < 0:\n-        start += 3\n-    while end < 0:\n-        end += 3\n-    while start > parent_length:\n-        start -= 3\n-    while end > parent_length:\n-        end -= 3\n-    return (start, end)\n-\n-\n-def coding_genes(feature_list):\n-    for x in genes(feature_list):\n-        if (\n-            len(\n-                list(\n-                    feature_lambda(\n-                        x.sub_features,\n-                        feature_test_type,\n-                        {"type": "CDS"},\n-                        subfeatures=False,\n-                    )\n-                )\n-            )\n-            > 0\n-        ):\n-            yield x\n-\n-\n-def genes(feature_list, feature_type="gene", sort=False):\n-    """\n-    Simple filter to extract gene features from the feature set.\n-    """\n-\n-    if not sort:\n-        for x in feature_lambda(\n-            feature_list, feature_test_type, {"type": feature_type}, subfeatures=True\n-        ):\n-            yield x\n-    else:\n-        data = list(genes(feature_list, feature_type=feature_type, sort=False))\n-        data = sorted(data, key=lambda feature: feature.location.start)\n-        for x in data:\n-            yield x\n-\n-\n-def wa_unified_product_name(feature):\n-    """\n-    Try and figure out a name. We gave conflicting instructions, so\n-    this isn\'t as trivial as it should be. Sometimes it will be in\n-    \'product\' or \'Product\', othertimes in \'Name\'\n-    """\n-    # Manually applied tags.\n-    protein_product = feature.qualifiers.get(\n-        "product", feature.qualifiers.get("Product", [None])\n-    )[0]\n-\n-    # If neither of those are available ...\n-    if protein_product is None:\n-        # And there\'s a name...\n-        if "Name" in feature.qualifiers:\n-            if not is_uuid(feature.qualifiers["Name"][0]):\n-                protein_product = feature.qualifiers["Name"][0]\n-\n-    return protein_product\n-\n-\n-def is_uuid(name):\n-    return name.count("-") == 4 and len(name) == 36\n-\n-\n-def get_rbs_from(gene):\n-    # Normal RBS annotation types\n-    rbs_rbs = list(\n-        feature_lambda(\n-            gene.sub_features, feature_test_type, {"type": "RBS"}, subfeatures=False\n-        )\n-    )\n-    rbs_sds = list(\n-        feature_lambda(\n-            gene.sub_features,\n-            feature_test_type,\n-            {"type": "Shine_Dalgarno_sequence"},\n-            subfeatures=False,\n-        )\n-    )\n-    # Fraking apollo\n-    apollo_exons = list(\n-        feature_lambda(\n-            gene.sub_features, feature_test_type, {"type": "exon"}, subfeatures=False\n-        )\n-    )\n-    apollo_exons = [x for x in apollo_exons if len(x) < 10]\n-    # These are more NCBI\'s style\n-    regulatory_elements = list(\n-        feature_lambda(\n-            gene.sub_features,\n-            feature_test_type,\n-            {"type": "regulatory"},\n-            subfeatures=False,\n-        )\n-    )\n-    rbs_regulatory = list(\n-        feature_lambda(\n-            regulatory_elements,\n-            feature_test_quals,\n-            {"regulatory_class": ["ribosome_binding_site"]},\n-            subfeatures=False,\n-        )\n-    )\n-    # Here\'s hoping you find just one ;)\n-    return rbs_rbs + rbs_sds + rbs_regulatory + apollo_exons\n-\n-\n-def nice_name(record):\n-    """\n-    get the real name rather than NCBI IDs and so on. If fails, will return record.id\n-    """\n-    name = record.id\n-    likely_parental_contig = list(genes(record.features, feature_type="contig"))\n-    if len(likely_parental_contig) == 1:\n-        name = likely_parental_contig[0].qualifiers.get("organism", [name])[0]\n-    return name\n-\n-\n-def fsort(it):\n-    for i in sorted(it, key=lambda x: int(x.location.start)):\n-        yield i\n'
b
diff -r 6a4d1bd8ac1d -r 32e011fa615c cpt_annotation_table/macros.xml
--- a/cpt_annotation_table/macros.xml Fri Jun 17 12:19:58 2022 +0000
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
b
@@ -1,23 +0,0 @@
-<?xml version="1.0"?>
-<macros>
-  <xml name="requirements">
-    <requirements>
- <requirement type="package" version="3.8.13">python</requirement>
- <requirement type="package" version="1.79">biopython</requirement>
- <requirement type="package" version="1.2.2">cpt_gffparser</requirement>  
- <yield/>
-    </requirements>
-  </xml>
-  <xml name="genome_selector">
-     <param name="genome_fasta" type="data" format="fasta" label="Source FASTA Sequence"/>
-  </xml>
-  <xml name="gff3_input">
-    <param label="GFF3 Annotations" name="gff3_data" type="data" format="gff3"/>
-  </xml>
-  <token name="@GENOME_SELECTOR_PRE@">
- ln -s $genome_fasta genomeref.fa;
- </token>
- <token name="@GENOME_SELECTOR@">
- genomeref.fa
- </token>
-</macros>
b
diff -r 6a4d1bd8ac1d -r 32e011fa615c cpt_annotation_table/phage_annotation_table.py
--- a/cpt_annotation_table/phage_annotation_table.py Fri Jun 17 12:19:58 2022 +0000
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
[
b'@@ -1,608 +0,0 @@\n-#!/usr/bin/env python\n-# vim: set fileencoding=utf-8\n-import os\n-import argparse\n-from gff3 import genes, get_gff3_id, get_rbs_from, feature_test_true, feature_lambda, feature_test_type\n-from CPT_GFFParser import gffParse, gffWrite\n-from Bio import SeqIO\n-from jinja2 import Environment, FileSystemLoader\n-import logging\n-from math import floor\n-\n-logging.basicConfig(level=logging.DEBUG)\n-log = logging.getLogger(name="pat")\n-\n-# Path to script, required because of Galaxy.\n-SCRIPT_PATH = os.path.dirname(os.path.realpath(__file__))\n-# Path to the HTML template for the report\n-\n-def genes_all(feature_list, feature_type=["gene"], sort=False):\n-    """\n-    Simple filter to extract gene features from the feature set.\n-    """\n-\n-    if not sort:\n-        for x in feature_lambda(\n-            feature_list, feature_test_type, {"types": feature_type}, subfeatures=True\n-        ):\n-            yield x\n-    else:\n-        data = list(genes_all(feature_list, feature_type, sort=False))\n-        data = sorted(data, key=lambda feature: feature.location.start)\n-        for x in data:\n-            yield x\n-\n-def checkSubs(feature, qualName):\n-    subFeats = []\n-    res = ""\n-    subFeats = feature.sub_features\n-    while (len(subFeats) > 0):\n-      for feat in subFeats:\n-        for i in feat.qualifiers.keys():\n-          for j in qualName:\n-            if i == j:\n-              if res == "":\n-                res = feat.qualifiers[i][0]\n-              else:\n-                res += "; " + feat.qualifiers[i][0]\n-      if res != "":\n-        return res \n-      tempFeats = []\n-      for feat in subFeats: # Should be breadth-first results\n-        for x in feat.sub_features:\n-          tempFeats.append(x)\n-      subFeats = tempFeats\n-    return res  \n-\n-def annotation_table_report(record, types, wanted_cols, gaf_data, searchSubs):\n-    getTypes = []\n-    for x in [y.strip() for y in types.split(",")]:\n-        getTypes.append(x)\n-    getTypes.append("gene")\n-    sorted_features = list(genes_all(record.features, getTypes, sort=True))\n-    if wanted_cols is None or len(wanted_cols.strip()) == 0:\n-        return [], []\n-    useSubs = searchSubs\n-\n-    def rid(record, feature):\n-        """Organism ID\n-        """\n-        return record.id\n-\n-    def id(record, feature):\n-        """ID\n-        """\n-        return feature.id\n-\n-    def featureType(record, feature):\n-        """Type\n-        """\n-        return feature.type\n-\n-    def name(record, feature):\n-        """Name\n-        """\n-        for x in ["Name", "name"]:\n-          for y in feature.qualifiers.keys():\n-            if x == y:\n-              return feature.qualifiers[x][0]\n-        if useSubs:\n-          res = checkSubs(feature, ["Name", "name"])\n-          if res != "":\n-            return res \n-        return "None"\n-    def start(record, feature):\n-        """Boundary\n-        """\n-        return str(feature.location.start + 1)\n-\n-    def end(record, feature):\n-        """Boundary\n-        """\n-        return str(feature.location.end)\n-\n-    def location(record, feature):\n-        """Location\n-        """\n-        return str(feature.location.start + 1) + "..{0.end}".format(feature.location)\n-\n-    def length(record, feature):\n-        """CDS Length (AA)\n-        """\n-        \n-        if feature.type == "CDS":\n-          cdss = [feature]\n-        else:\n-          cdss = list(genes(feature.sub_features, feature_type="CDS", sort=True))\n-        \n-        if cdss == []:\n-          return "None"\n-        res = (sum([len(cds) for cds in cdss]) / 3) - 1\n-        if floor(res) == res:\n-          res = int(res)\n-        return str(res)\n-\n-    def notes(record, feature):\n-        """User entered Notes"""\n-        for x in ["Note", "note", "Notes", "notes"]:\n-          for y in feature.qualifiers.keys():\n-            if x == y:\n-              return feature.qualifiers[x][0]\n-        if useSubs:\n-          res = checkSubs(feature, ["Note", "note", "Notes", "notes"])\n-          '..b'-    # \'10d04a01-5ed8-49c8-b724-d6aa4df5a98d\': {\n-    # \'annotation_extension\': \'\',\n-    # \'aspect\': \'\',\n-    # \'assigned_by\': \'CPT\',\n-    # \'date\': \'2017-05-04T16:25:22.161916Z\',\n-    # \'db\': \'UniProtKB\',\n-    # \'db_reference\': \'GO_REF:0000100\',\n-    # \'evidence_code\': \'ISA\',\n-    # \'gene\': \'0d307196-833d-46e8-90e9-d80f7a041d88\',\n-    # \'go_id\': \'GO:0039660\',\n-    # \'go_term\': \'structural constituent of virion\',\n-    # \'id\': \'10d04a01-5ed8-49c8-b724-d6aa4df5a98d\',\n-    # \'notes\': \'hit was putative minor structural protein\',\n-    # \'owner\': \'amarc1@tamu.edu\',\n-    # \'with_or_from\': \'UNIREF90:B2ZYZ7\'\n-    # },\n-    for row in file:\n-        if row.startswith("#"):\n-            # Header\n-            cols = (\n-                row.strip().replace("# ", "").replace("GO Term", "go_term").split("\\t")\n-            )\n-        else:\n-            line = row.strip().split("\\t")\n-            tmp = dict(zip(cols, line))\n-            if "gene" not in tmp.keys():\n-              continue\n-            if tmp["gene"] not in data:\n-                data[tmp["gene"]] = []\n-\n-            data[tmp["gene"]].append(tmp)\n-    return data\n-\n-\n-def evaluate_and_report(\n-    annotations,\n-    genome,\n-    types="gene",\n-    reportTemplateName="phage_annotation_validator.html",\n-    annotationTableCols="",\n-    gafData=None,\n-    searchSubs = False,\n-):\n-    """\n-    Generate our HTML evaluation of the genome\n-    """\n-    # Get features from GFF file\n-    seq_dict = SeqIO.to_dict(SeqIO.parse(genome, "fasta"))\n-    # Get the first GFF3 record\n-    # TODO: support multiple GFF3 files.\n-    at_table_data = []\n-    gaf = {}\n-    if gafData:\n-        gaf = parseGafData(gafData)\n-\n-    for record in gffParse(annotations, base_dict=seq_dict):\n-        if reportTemplateName.endswith(".html"):\n-            record.id = record.id.replace(".", "-")\n-        log.info("Producing an annotation table for %s" % record.id)\n-        annotation_table_data, annotation_table_col_names = annotation_table_report(\n-            record, types, annotationTableCols, gaf, searchSubs\n-        )\n-        at_table_data.append((record, annotation_table_data))\n-        # break\n-\n-    # This is data that will go into our HTML template\n-    kwargs = {\n-        "annotation_table_data": at_table_data,\n-        "annotation_table_col_names": annotation_table_col_names,\n-    }\n-\n-    env = Environment(\n-        loader=FileSystemLoader(SCRIPT_PATH), trim_blocks=True, lstrip_blocks=True\n-    )\n-    if reportTemplateName.endswith(".html"):\n-        env.filters["nice_id"] = str(get_gff3_id).replace(".", "-")\n-    else:\n-        env.filters["nice_id"] = get_gff3_id\n-\n-    def join(listy):\n-        return "\\n".join(listy)\n-\n-    env.filters.update({"join": join})\n-    tpl = env.get_template(reportTemplateName)\n-    return tpl.render(**kwargs).encode("utf-8")\n-\n-\n-if __name__ == "__main__":\n-    parser = argparse.ArgumentParser(\n-        description="rebase gff3 features against parent locations", epilog=""\n-    )\n-    parser.add_argument(\n-        "annotations", type=argparse.FileType("r"), help="Parent GFF3 annotations"\n-    )\n-    parser.add_argument("genome", type=argparse.FileType("r"), help="Genome Sequence")\n-\n-    parser.add_argument(\n-        "--types",\n-        help="Select extra types to display in output (Will always include gene)",\n-    )\n-\n-    parser.add_argument(\n-        "--reportTemplateName",\n-        help="Report template file name",\n-        default="phageqc_report_full.html",\n-    )\n-    parser.add_argument(\n-        "--annotationTableCols",\n-        help="Select columns to report in the annotation table output format",\n-    )\n-    parser.add_argument(\n-        "--gafData", help="CPT GAF-like table", type=argparse.FileType("r")\n-    )\n-    parser.add_argument(\n-        "--searchSubs", help="Attempt to populate fields from sub-features if qualifier is empty", action="store_true"\n-    )\n-\n-    args = parser.parse_args()\n-\n-    print(evaluate_and_report(**vars(args)).decode("utf-8"))\n'
b
diff -r 6a4d1bd8ac1d -r 32e011fa615c cpt_annotation_table/phage_annotation_table.xml
--- a/cpt_annotation_table/phage_annotation_table.xml Fri Jun 17 12:19:58 2022 +0000
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
[
@@ -1,112 +0,0 @@
-<?xml version="1.0"?>
-<tool id="edu.tamu.cpt2.phage.annotation_table" name="Annotation Table" version="20.0.0.1">
-    <description>from gff3 formatted datasets</description>
-    <macros>
-      <import>macros.xml</import>
-        <import>cpt-macros.xml</import>
-    </macros>
-    <expand macro="requirements">
- <requirement type="package" version="2.10.1">jinja2</requirement>  
-    </expand>
-    <command detect_errors="aggressive"><![CDATA[
-@GENOME_SELECTOR_PRE@
-
-python $__tool_directory__/phage_annotation_table.py
-$gff3_data
-@GENOME_SELECTOR@
---types "$extraTypes"
---reportTemplateName "$report_format"
---annotationTableCols "$cols,$gaf_cols"
-#if $gaf_data:
---gafData "$gaf_data"
-#end if
-$checkSubfeats
-> $output]]></command>
-    <inputs>
-        <expand macro="gff3_input" />
-        <expand macro="genome_selector" />
-        <param label="Extra sub-feature types to include (Comma-separated, Will always include gene)" optional="True" name="extraTypes" type="text" value =""/>
-        <param label="Columns" type="select" name="cols" multiple="True">
-            <option value="rid" selected="true">Record ID</option>
-            <option value="id" selected="true">ID</option>
-            <option value="type" selected="true">Type</option>
-            <option value="name" selected="true">Name</option>
-
-            <option value="location" selected="true">Location</option>
-            <option value="start" selected="true">Feature Start</option>
-            <option value="end" selected="true">Feature End</option>
-            <option value="strand" selected="true">Strand</option>
-            <option value="length" selected="true">Length</option>
-
-            <option value="sd_seq" selected="true">SD sequence</option>
-            <option value="sd_spacing" selected="true">SD spacing</option>
-            <option value="start_codon" selected="true">Start codon</option>
-            <option value="stop_codon" selected="true">Stop codon</option>
-
-            <option value="ig_dist" selected="true">Distance to next upstream feature</option>
-            <option value="upstream_feature__name" selected="true">Name of upstream feature</option>
-
-            <option value="owner" selected="true">Owner</option>
-            <option value="date_created" selected="true">Date created</option>
-            <option value="date_last_modified" selected="true">Date last modified</option>
-
-            <option value="notes" selected="true">Notes</option>
-            <option value="description" selected="true">Description</option>
-            <option value="product" selected="true">Product</option>
-            <option value="dbxrefs" selected="true">DBxrefs</option>
-            <option value="qualifiers" selected="true">Qualifiers</option>
-        </param>
-
-        <param label="GAF Data" name="gaf_data" type="data" format="tabular" optional="True"
-            help="Optional GAF Data Table. This usually comes from the CPT CACAO GAF Data Export tool. The [GAF] Columns below are ONLY available when this data is populated."/>
-        <param label="GAF Columns" type="select" name="gaf_cols" multiple="True">
-            <!-- GAF Specific Fields -->
-            <option value="gaf_annotation_extension" selected="false">GAF Annotation Extension</option>
-            <option value="gaf_aspect" selected="false">GAF Aspect</option>
-            <option value="gaf_assigned_by" selected="false">GAF Assigned By</option>
-            <option value="gaf_date" selected="false">GAF Date</option>
-            <option value="gaf_db" selected="false">GAF DB</option>
-            <option value="gaf_db_reference" selected="false">GAF DB Reference</option>
-            <option value="gaf_evidence_code" selected="false">GAF Evidence Code</option>
-            <option value="gaf_gene" selected="false">GAF Gene</option>
-            <option value="gaf_go_id" selected="false">GAF GO ID</option>
-            <option value="gaf_go_term" selected="false">GAF GO Term</option>
-            <option value="gaf_id" selected="false">GAF ID</option>
-            <option value="gaf_notes" selected="false">GAF Notes</option>
-            <option value="gaf_owner" selected="false">GAF Owner</option>
-            <option value="gaf_with_or_from" selected="false">GAF with_or_from</option>
-        </param>
-        <param name="checkSubfeats" label="Search sub-features for fields if gene does not define them" type="boolean" truevalue="--searchSubs" falsevalue=""/>
-        <param label="Report Format" type="select" name="report_format">
-            <option value="phageqc_report_annotation_table.html" selected="true">HTML Table</option>
-            <option value="phageqc_report_annotation_table.tsv">Tabular (Excel Compatible) Table</option>
-        </param>
-    </inputs>
-    <outputs>
-      <data format="html" name="output">
-          <change_format>
-            <when format="tabular" input="report_format" value="phageqc_report_annotation_table.tsv"/>
-          </change_format>
-      </data>
-    </outputs>
-    <tests> 
- <test>
- <param name="reference_genome_source" value="history" />
- <param name="genome_fasta" value="NC_001416_Table_In.fasta" />
- <param name="gff3_data" value="NC_001416_Table_In.gff3" />
- <param name="report_format" value="phageqc_report_annotation_table.html" />
- <output name="output" file="PhageTable_Out.html" />
- </test>
- <test>
- <param name="reference_genome_source" value="history" />
- <param name="genome_fasta" value="NC_001416_Table_In.fasta" />
- <param name="gff3_data" value="NC_001416_Table_In.gff3" />
- <param name="report_format" value="phageqc_report_annotation_table.tsv" />
- <output name="output" file="PhageTable_Out.tabular" />
- </test>
-  </tests>
-    <help><![CDATA[
-Generate an "annotation table" of a genome, with user-configurable data columns
-]]></help>
-    <expand macro="citations" />
-</tool>
b
diff -r 6a4d1bd8ac1d -r 32e011fa615c cpt_annotation_table/phageqc_report_annotation_table.html
--- a/cpt_annotation_table/phageqc_report_annotation_table.html Fri Jun 17 12:19:58 2022 +0000
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
[
@@ -1,244 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-  <head>
-    <meta charset="utf-8">
-    <meta http-equiv="X-UA-Compatible" content="IE=edge">
-    <meta name="viewport" content="width=device-width, initial-scale=1">
-    <!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags -->
-    <meta name="description" content="">
-    <meta name="author" content="">
-    <title>Annotation Table</title>
-    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.2/jquery.min.js"></script>
- <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" crossorigin="anonymous">
- <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap-theme.min.css" crossorigin="anonymous">
- <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js" crossorigin="anonymous"></script>
-
-    <link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.10.11/css/jquery.dataTables.css">
-    <script type="text/javascript" charset="utf8" src="https://cdn.datatables.net/1.10.11/js/jquery.dataTables.js"></script>
-    <style type="text/css">
-/*
- * Base structure
- */
-
-/* Move down content because we have a fixed navbar that is 50px tall */
-body {
-  padding-top: 50px;
-}
-
-h3:before {
-  display: block;
-  content: " ";
-  margin-top: -50px;
-  height: 50px;
-  visibility: hidden;
-}
-
-/*
- * Global add-ons
- */
-
-.sub-header {
-  padding-bottom: 10px;
-  border-bottom: 1px solid #eee;
-}
-
-/*
- * Top navigation
- * Hide default border to remove 1px line.
- */
-.navbar-fixed-top {
-  border: 0;
-}
-
-/*
- * Sidebar
- */
-
-/* Hide for mobile, show later */
-.sidebar {
-  display: none;
-}
-@media (min-width: 768px) {
-  .sidebar {
-    position: fixed;
-    top: 51px;
-    bottom: 0;
-    left: 0;
-    z-index: 1000;
-    display: block;
-    padding: 20px;
-    overflow-x: hidden;
-    overflow-y: auto; /* Scrollable contents if viewport is shorter than content. */
-    background-color: #f5f5f5;
-    border-right: 1px solid #eee;
-  }
-}
-
-/* Sidebar navigation */
-.nav-sidebar {
-  margin-right: -21px; /* 20px padding + 1px border */
-  margin-bottom: 20px;
-  margin-left: -20px;
-}
-.nav-sidebar > li > a {
-  padding-right: 20px;
-  padding-left: 20px;
-}
-.nav-sidebar > .active > a,
-.nav-sidebar > .active > a:hover,
-.nav-sidebar > .active > a:focus {
-  color: #fff;
-  background-color: #428bca;
-}
-
-
-/*
- * Main content
- */
-
-.main {
-  padding: 20px;
-}
-@media (min-width: 768px) {
-  .main {
-    padding-right: 40px;
-    padding-left: 40px;
-  }
-}
-.main .page-header {
-  margin-top: 0;
-}
-
-
-/*
- * Placeholder dashboard ideas
- */
-
-.placeholders {
-  margin-bottom: 30px;
-  text-align: center;
-}
-.placeholders h4 {
-  margin-bottom: 0;
-}
-.placeholder {
-  margin-bottom: 20px;
-}
-.placeholder img {
-  display: inline-block;
-  border-radius: 50%;
-}
-
-td.moron {
-    font-size: 150%;
-    padding: 0px;
-    color: gray;
-}
-.strand_emph {
-    text-decoration: underline;
-    color: black;
-}
-
-    </style>
-    <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
-    <!--[if lt IE 9]>
-      <script src="//oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
-      <script src="//oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
-    <![endif]-->
-  </head>
-  <body>
-
-    <nav class="navbar navbar-inverse navbar-fixed-top">
-      <div class="container-fluid">
-        <div class="navbar-header">
-            <a class="navbar-brand" href="#">Annotation Table</a>
-        </div>
-      </div>
-    </nav>
-
-    <div class="container-fluid">
-      <div class="row">
-        <div class="col-md-12 main" id="main">
-            <ul class="nav nav-tabs" role="tablist" id="myTabs">
-                <li role="presentation" class="active"><a href="#overview" role="tab" data-toggle="tab">Overview</a></li>
-                {% for (record, data) in annotation_table_data %}
-                <li role="presentation"><a href="#{{ record.id }}" role="tab" data-toggle="tab">{{ record.id }}</a></li>
-                {% endfor %}
-            </ul>
-
-            <!-- Tab panes -->
-            <div class="tab-content">
-                <div role="tabpanel" class="tab-pane active" id="overview">
-                    Data on each organism will be accessible from the tabs above.
-                </div>
-                {% for (record, data) in annotation_table_data %}
-                    <div role="tabpanel" class="tab-pane" id="{{ record.id }}">
-                        <table class="table table-striped" id="data">
-                            <thead>
-                                <tr>
-                                {% for col in annotation_table_col_names %}
-                                    <th>{{ col[0] }}</th>
-                                {% endfor %}
-                                </tr>
-                            </thead>
-                            <tbody>
-                                {% for row in data %}
-                                    <tr>
-                                    {% for col in row %}
-                                    <td>{% if col is not string %}<ul>{% for val in col %}<li>{{ val }}</li>{% endfor %}</ul>{% else %}{{ col }}{% endif %}</td>
-                                    {% endfor %}
-                                    </tr>
-                                {% endfor %}
-                            </tbody>
-                        </table>
-                    </div>
-                {% endfor %}
-            </div>
-        </div>
-      </div>
-    </div>
-
-    <script type="text/javascript">
-        $(document).ready( function () {
-
-
-
-$('#myTabs a').click(function (e) {
- e.preventDefault()
- $(this).tab('show')
-})
-
-
-
-jQuery.fn.dataTable.ext.type.detect.unshift( function ( data ) {
-    if ( typeof data !== 'string' ) {
-        return null;
-    }
-
-    var matches = data.match(/^(\d+)\.\.(\d+)/);
-    return matches ? 'genomic' : null;
-} );
-
-
-
-jQuery.extend( jQuery.fn.dataTableExt.oSort, {
-    "genomic-pre": function ( a ) {
-        var matches = a.match(/^(\d+)\.\.(\d+)/);
-        console.log(a + " " + matches[1]);
-        return parseInt(matches[1]);
-    },
-
-    "genomic-asc": function ( a, b ) {
-        return ((a < b) ? -1 : ((a > b) ? 1 : 0));
-    },
-
-    "genomic-desc": function ( a, b ) {
-        return ((a < b) ? 1 : ((a > b) ? -1 : 0));
-    }
-} );
-
-            $('table').DataTable();
-        });
-    </script>
-  </body>
-</html>
b
diff -r 6a4d1bd8ac1d -r 32e011fa615c cpt_annotation_table/phageqc_report_annotation_table.tsv
--- a/cpt_annotation_table/phageqc_report_annotation_table.tsv Fri Jun 17 12:19:58 2022 +0000
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
[
@@ -1,8 +0,0 @@
-{% for (record, dataset) in annotation_table_data %}
-# {% for col in annotation_table_col_names %}{{ col[0] }} {% endfor %}
-{% for row in dataset %}
-
-{% for col in row %}
-{% if col is not string %}"{{ col | join }}"{% else %}{{ col }}{% endif %} {% endfor %}
-{% endfor %}
-{% endfor %}
b
diff -r 6a4d1bd8ac1d -r 32e011fa615c cpt_annotation_table/test-data/NC_001416_Table_In.fasta
--- a/cpt_annotation_table/test-data/NC_001416_Table_In.fasta Fri Jun 17 12:19:58 2022 +0000
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
b
b'@@ -1,810 +0,0 @@\n->NC_001416.1_phage_lambda\n-GGGCGGCGACCTCGCGGGTTTTCGCTATTTATGAAAATTTTCCGGTTTAAGGCGTTTCCG\n-TTCTTCTTCGTCATAACTTAATGTTTTTATTTAAAATACCCTCTGAAAAGAAAGGAAACG\n-ACAGGTGCTGAAAGCGAGGCTTTTTGGCCTCTGTCGTTTCCTTTCTCTGTTTTTGTCCGT\n-GGAATGAACAATGGAAGTCAACAAAAAGCAGCTGGCTGACATTTTCGGTGCGAGTATCCG\n-TACCATTCAGAACTGGCAGGAACAGGGAATGCCCGTTCTGCGAGGCGGTGGCAAGGGTAA\n-TGAGGTGCTTTATGACTCTGCCGCCGTCATAAAATGGTATGCCGAAAGGGATGCTGAAAT\n-TGAGAACGAAAAGCTGCGCCGGGAGGTTGAAGAACTGCGGCAGGCCAGCGAGGCAGATCT\n-CCAGCCAGGAACTATTGAGTACGAACGCCATCGACTTACGCGTGCGCAGGCCGACGCACA\n-GGAACTGAAGAATGCCAGAGACTCCGCTGAAGTGGTGGAAACCGCATTCTGTACTTTCGT\n-GCTGTCGCGGATCGCAGGTGAAATTGCCAGTATTCTCGACGGGCTCCCCCTGTCGGTGCA\n-GCGGCGTTTTCCGGAACTGGAAAACCGACATGTTGATTTCCTGAAACGGGATATCATCAA\n-AGCCATGAACAAAGCAGCCGCGCTGGATGAACTGATACCGGGGTTGCTGAGTGAATATAT\n-CGAACAGTCAGGTTAACAGGCTGCGGCATTTTGTCCGCGCCGGGCTTCGCTCACTGTTCA\n-GGCCGGAGCCACAGACCGCCGTTGAATGGGCGGATGCTAATTACTATCTCCCGAAAGAAT\n-CCGCATACCAGGAAGGGCGCTGGGAAACACTGCCCTTTCAGCGGGCCATCATGAATGCGA\n-TGGGCAGCGACTACATCCGTGAGGTGAATGTGGTGAAGTCTGCCCGTGTCGGTTATTCCA\n-AAATGCTGCTGGGTGTTTATGCCTACTTTATAGAGCATAAGCAGCGCAACACCCTTATCT\n-GGTTGCCGACGGATGGTGATGCCGAGAACTTTATGAAAACCCACGTTGAGCCGACTATTC\n-GTGATATTCCGTCGCTGCTGGCGCTGGCCCCGTGGTATGGCAAAAAGCACCGGGATAACA\n-CGCTCACCATGAAGCGTTTCACTAATGGGCGTGGCTTCTGGTGCCTGGGCGGTAAAGCGG\n-CAAAAAACTACCGTGAAAAGTCGGTGGATGTGGCGGGTTATGATGAACTTGCTGCTTTTG\n-ATGATGATATTGAACAGGAAGGCTCTCCGACGTTCCTGGGTGACAAGCGTATTGAAGGCT\n-CGGTCTGGCCAAAGTCCATCCGTGGCTCCACGCCAAAAGTGAGAGGCACCTGTCAGATTG\n-AGCGTGCAGCCAGTGAATCCCCGCATTTTATGCGTTTTCATGTTGCCTGCCCGCATTGCG\n-GGGAGGAGCAGTATCTTAAATTTGGCGACAAAGAGACGCCGTTTGGCCTCAAATGGACGC\n-CGGATGACCCCTCCAGCGTGTTTTATCTCTGCGAGCATAATGCCTGCGTCATCCGCCAGC\n-AGGAGCTGGACTTTACTGATGCCCGTTATATCTGCGAAAAGACCGGGATCTGGACCCGTG\n-ATGGCATTCTCTGGTTTTCGTCATCCGGTGAAGAGATTGAGCCACCTGACAGTGTGACCT\n-TTCACATCTGGACAGCGTACAGCCCGTTCACCACCTGGGTGCAGATTGTCAAAGACTGGA\n-TGAAAACGAAAGGGGATACGGGAAAACGTAAAACCTTCGTAAACACCACGCTCGGTGAGA\n-CGTGGGAGGCGAAAATTGGCGAACGTCCGGATGCTGAAGTGATGGCAGAGCGGAAAGAGC\n-ATTATTCAGCGCCCGTTCCTGACCGTGTGGCTTACCTGACCGCCGGTATCGACTCCCAGC\n-TGGACCGCTACGAAATGCGCGTATGGGGATGGGGGCCGGGTGAGGAAAGCTGGCTGATTG\n-ACCGGCAGATTATTATGGGCCGCCACGACGATGAACAGACGCTGCTGCGTGTGGATGAGG\n-CCATCAATAAAACCTATACCCGCCGGAATGGTGCAGAAATGTCGATATCCCGTATCTGCT\n-GGGATACTGGCGGGATTGACCCGACCATTGTGTATGAACGCTCGAAAAAACATGGGCTGT\n-TCCGGGTGATCCCCATTAAAGGGGCATCCGTCTACGGAAAGCCGGTGGCCAGCATGCCAC\n-GTAAGCGAAACAAAAACGGGGTTTACCTTACCGAAATCGGTACGGATACCGCGAAAGAGC\n-AGATTTATAACCGCTTCACACTGACGCCGGAAGGGGATGAACCGCTTCCCGGTGCCGTTC\n-ACTTCCCGAATAACCCGGATATTTTTGATCTGACCGAAGCGCAGCAGCTGACTGCTGAAG\n-AGCAGGTCGAAAAATGGGTGGATGGCAGGAAAAAAATACTGTGGGACAGCAAAAAGCGAC\n-GCAATGAGGCACTCGACTGCTTCGTTTATGCGCTGGCGGCGCTGCGCATCAGTATTTCCC\n-GCTGGCAGCTGGATCTCAGTGCGCTGCTGGCGAGCCTGCAGGAAGAGGATGGTGCAGCAA\n-CCAACAAGAAAACACTGGCAGATTACGCCCGTGCCTTATCCGGAGAGGATGAATGACGCG\n-ACAGGAAGAACTTGCCGCTGCCCGTGCGGCACTGCATGACCTGATGACAGGTAAACGGGT\n-GGCAACAGTACAGAAAGACGGACGAAGGGTGGAGTTTACGGCCACTTCCGTGTCTGACCT\n-GAAAAAATATATTGCAGAGCTGGAAGTGCAGACCGGCATGACACAGCGACGCAGGGGACC\n-TGCAGGATTTTATGTATGAAAACGCCCACCATTCCCACCCTTCTGGGGCCGGACGGCATG\n-ACATCGCTGCGCGAATATGCCGGTTATCACGGCGGTGGCAGCGGATTTGGAGGGCAGTTG\n-CGGTCGTGGAACCCACCGAGTGAAAGTGTGGATGCAGCCCTGTTGCCCAACTTTACCCGT\n-GGCAATGCCCGCGCAGACGATCTGGTACGCAATAACGGCTATGCCGCCAACGCCATCCAG\n-CTGCATCAGGATCATATCGTCGGGTCTTTTTTCCGGCTCAGTCATCGCCCAAGCTGGCGC\n-TATCTGGGCATCGGGGAGGAAGAAGCCCGTGCCTTTTCCCGCGAGGTTGAAGCGGCATGG\n-AAAGAGTTTGCCGAGGATGACTGCTGCTGCATTGACGTTGAGCGAAAACGCACGTTTACC\n-ATGATGATTCGGGAAGGTGTGGCCATGCACGCCTTTAACGGTGAACTGTTCGTTCAGGCC\n-ACCTGGGATACCAGTTCGTCGCGGCTTTTCCGGACACAGTTCCGGATGGTCAGCCCGAAG\n-CGCATCAGCAACCCGAACAATACCGGCGACAGCCGGAACTGCCGTGCCGGTGTGCAGATT\n-AATGACAGCGGTGCGGCGCTGGGATATTACGTCAGCGAGGACGGGTATCCTGGCTGGATG\n-CCGCAGAAATGGACATGGATACCCCGTGAGTTACCCGGCGGGCGCGCCTCGTTCATTCAC\n-GTTTTTGAACCCGTGGAGGACGGGCAGACTCGCGGTGCAAATGTGTTTTACAGCGTGATG\n-GAGCAGATGAAGATGCTCGACACGCTGCAGAACACGCAGCTGCAGAGCGCCATTGTGAAG\n-GCGATGTATGCCGCCACCATTGAGAGTGAGCTGGATACGCAGTCAGCGATGGATTTTATT\n-CTGGGCGCGAACAGTCAGGAGCAGCGGGAAAGGCTGACCGGCTGGATTGGTGAAATTGCC\n-GCGTATTACGCCGCAGCGCCGGTCCGGCTGGGAGGCGCAAAAGTACC'..b'CGCTTA\n-CTACCGATTCCGCCTAGTTGGTCACTTCGACGTATCGTCTGGAACTCCAACCATCGCAGG\n-CAGAGAGGTCTGCAAAATGCAATCCCGAAACAGTTCGCAGGTAATAGTTAGAGCCTGCAT\n-AACGGTTTCGGGATTTTTTATATCTGCACAACAGGTAAGAGCATTGAGTCGATAATCGTG\n-AAGAGTCGGCGAGCCTGGTTAGCCAGTGCTCTTTCCGTTGTGCTGAATTAAGCGAATACC\n-GGAAGCAGAACCGGATCACCAAATGCGTACAGGCGTCATCGCCGCCCAGCAACAGCACAA\n-CCCAAACTGAGCCGTAGCCACTGTCTGTCCTGAATTCATTAGTAATAGTTACGCTGCGGC\n-CTTTTACACATGACCTTCGTGAAAGCGGGTGGCAGGAGGTCGCGCTAACAACCTCCTGCC\n-GTTTTGCCCGTGCATATCGGTCACGAACAAATCTGATTACTAAACACAGTAGCCTGGATT\n-TGTTCTATCAGTAATCGACCTTATTCCTAATTAAATAGAGCAAATCCCCTTATTGGGGGT\n-AAGACATGAAGATGCCAGAAAAACATGACCTGTTGGCCGCCATTCTCGCGGCAAAGGAAC\n-AAGGCATCGGGGCAATCCTTGCGTTTGCAATGGCGTACCTTCGCGGCAGATATAATGGCG\n-GTGCGTTTACAAAAACAGTAATCGACGCAACGATGTGCGCCATTATCGCCTGGTTCATTC\n-GTGACCTTCTCGACTTCGCCGGACTAAGTAGCAATCTCGCTTATATAACGAGCGTGTTTA\n-TCGGCTACATCGGTACTGACTCGATTGGTTCGCTTATCAAACGCTTCGCTGCTAAAAAAG\n-CCGGAGTAGAAGATGGTAGAAATCAATAATCAACGTAAGGCGTTCCTCGATATGCTGGCG\n-TGGTCGGAGGGAACTGATAACGGACGTCAGAAAACCAGAAATCATGGTTATGACGTCATT\n-GTAGGCGGAGAGCTATTTACTGATTACTCCGATCACCCTCGCAAACTTGTCACGCTAAAC\n-CCAAAACTCAAATCAACAGGCGCCGGACGCTACCAGCTTCTTTCCCGTTGGTGGGATGCC\n-TACCGCAAGCAGCTTGGCCTGAAAGACTTCTCTCCGAAAAGTCAGGACGCTGTGGCATTG\n-CAGCAGATTAAGGAGCGTGGCGCTTTACCTATGATTGATCGTGGTGATATCCGTCAGGCA\n-ATCGACCGTTGCAGCAATATCTGGGCTTCACTGCCGGGCGCTGGTTATGGTCAGTTCGAG\n-CATAAGGCTGACAGCCTGATTGCAAAATTCAAAGAAGCGGGCGGAACGGTCAGAGAGATT\n-GATGTATGAGCAGAGTCACCGCGATTATCTCCGCTCTGGTTATCTGCATCATCGTCTGCC\n-TGTCATGGGCTGTTAATCATTACCGTGATAACGCCATTACCTACAAAGCCCAGCGCGACA\n-AAAATGCCAGAGAACTGAAGCTGGCGAACGCGGCAATTACTGACATGCAGATGCGTCAGC\n-GTGATGTTGCTGCGCTCGATGCAAAATACACGAAGGAGTTAGCTGATGCTAAAGCTGAAA\n-ATGATGCTCTGCGTGATGATGTTGCCGCTGGTCGTCGTCGGTTGCACATCAAAGCAGTCT\n-GTCAGTCAGTGCGTGAAGCCACCACCGCCTCCGGCGTGGATAATGCAGCCTCCCCCCGAC\n-TGGCAGACACCGCTGAACGGGATTATTTCACCCTCAGAGAGAGGCTGATCACTATGCAAA\n-AACAACTGGAAGGAACCCAGAAGTATATTAATGAGCAGTGCAGATAGAGTTGCCCATATC\n-GATGGGCAACTCATGCAATTATTGTGAGCAATACACACGCGCTTCCAGCGGAGTATAAAT\n-GCCTAAAGTAATAAAACCGAGCAATCCATTTACGAATGTTTGCTGGGTTTCTGTTTTAAC\n-AACATTTTCTGCGCCGCCACAAATTTTGGCTGCATCGACAGTTTTCTTCTGCCCAATTCC\n-AGAAACGAAGAAATGATGGGTGATGGTTTCCTTTGGTGCTACTGCTGCCGGTTTGTTTTG\n-AACAGTAAACGTCTGTTGAGCACATCCTGTAATAAGCAGGGCCAGCGCAGTAGCGAGTAG\n-CATTTTTTTCATGGTGTTATTCCCGATGCTTTTTGAAGTTCGCAGAATCGTATGTGTAGA\n-AAATTAAACAAACCCTAAACAATGAGTTGAAATTTCATATTGTTAATATTTATTAATGTA\n-TGTCAGGTGCGATGAATCGTCATTGTATTCCCGGATTAACTATGTCCACAGCCCTGACGG\n-GGAACTTCTCTGCGGGAGTGTCCGGGAATAATTAAAACGATGCACACAGGGTTTAGCGCG\n-TACACGTATTGCATTATGCCAACGCCCCGGTGCTGACACGGAAGAAACCGGACGTTATGA\n-TTTAGCGTGGAAAGATTTGTGTAGTGTTCTGAATGCTCTCAGTAAATAGTAATGAATTAT\n-CAAAGGTATAGTAATATCTTTTATGTTCATGGATATTTGTAACCCATCGGAAAACTCCTG\n-CTTTAGCAAGATTTTCCCTGTATTGCTGAAATGTGATTTCTCTTGATTTCAACCTATCAT\n-AGGACGTTTCTATAAGATGCGTGTTTCTTGAGAATTTAACATTTACAACCTTTTTAAGTC\n-CTTTTATTAACACGGTGTTATCGTTTTCTAACACGATGTGAATATTATCTGTGGCTAGAT\n-AGTAAATATAATGTGAGACGTTGTGACGTTTTAGTTCAGAATAAAACAATTCACAGTCTA\n-AATCTTTTCGCACTTGATCGAATATTTCTTTAAAAATGGCAACCTGAGCCATTGGTAAAA\n-CCTTCCATGTGATACGAGGGCGCGTAGTTTGCATTATCGTTTTTATCGTTTCAATCTGGT\n-CTGACCTCCTTGTGTTTTGTTGATGATTTATGTCAAATATTAGGAATGTTTTCACTTAAT\n-AGTATTGGTTGCGTAACAAAGTGCGGTCCTGCTGGCATTCTGGAGGGAAATACAACCGAC\n-AGATGTATGTAAGGCCAACGTGCTCAAATCTTCATACAGAAAGATTTGAAGTAATATTTT\n-AACCGCTAGATGAAGAGCAAGCGCATGGAGCGACAAAATGAATAAAGAACAATCTGCTGA\n-TGATCCCTCCGTGGATCTGATTCGTGTAAAAAATATGCTTAATAGCACCATTTCTATGAG\n-TTACCCTGATGTTGTAATTGCATGTATAGAACATAAGGTGTCTCTGGAAGCATTCAGAGC\n-AATTGAGGCAGCGTTGGTGAAGCACGATAATAATATGAAGGATTATTCCCTGGTGGTTGA\n-CTGATCACCATAACTGCTAATCATTCAAACTATTTAGTCTGTGACAGAGCCAACACGCAG\n-TCTGTCACTGTCAGGAAAGTGGTAAAACTGCAACTCAATTACTGCAATGCCCTCGTAATT\n-AAGTGAATTTACAATATCGTCCTGTTCGGAGGGAAGAACGCGGGATGTTCATTCTTCATC\n-ACTTTTAATTGATGTATATGCTCTCTTTTCTGACGTTAGTCTCCGACGGCAGGCTTCAAT\n-GACCCAGGCTGAGAAATTCCCGGACCCTTTTTGCTCAAGAGCGATGTTAATTTGTTCAAT\n-CATTTGGTTAGGAAAGCGGATGTTGCGGGTTGTTGTTCTGCGGGTTCTGTTCTTCGTTGA\n-CATGAGGTTGCCCCGTATTCAGTGTCGCTGATTTGTATTGTCTGAAGTTGTTTTTACGTT\n-AAGTTGATGCAGATCAATTAATACGATACCTGCGTCATAATTGATTATTTGACGTGGTTT\n-GATGGCCTCCACGCACGTTGTGATATGTAGATGATAATCATTATCACTTTACGGGTCCTT\n-TCCGGTGATCCGACAGGTTACG\n'
b
diff -r 6a4d1bd8ac1d -r 32e011fa615c cpt_annotation_table/test-data/NC_001416_Table_In.gff3
--- a/cpt_annotation_table/test-data/NC_001416_Table_In.gff3 Fri Jun 17 12:19:58 2022 +0000
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
b
b'@@ -1,476 +0,0 @@\n-##gff-version 3\n-##sequence-region NC_001416.1_phage_lambda 1 48502\n-NC_001416.1_phage_lambda\t.\tgene\t14261\t14875\t.\t+\t.\towner=jolenerr@tamu.edu;ID=cdb58372-2440-4e07-bcc4-739e0aba461f;date_last_modified=2020-05-27;Name=tail tip assembly protein K;date_creation=2020-05-13\n-NC_001416.1_phage_lambda\t.\tmRNA\t14261\t14875\t.\t+\t.\towner=jolenerr@tamu.edu;Parent=cdb58372-2440-4e07-bcc4-739e0aba461f;ID=c53587ff-12d9-4a3a-a7ac-e87e54c5232b;date_last_modified=2020-05-27;Name=tail tip assembly protein K;date_creation=2020-05-13\n-NC_001416.1_phage_lambda\t.\tCDS\t14276\t14875\t.\t+\t0\tParent=c53587ff-12d9-4a3a-a7ac-e87e54c5232b;ID=2d4171c3-93b5-4c30-afba-9a0969dd4a6f;Name=2d4171c3-93b5-4c30-afba-9a0969dd4a6f\n-NC_001416.1_phage_lambda\t.\texon\t14276\t14875\t.\t+\t.\tParent=c53587ff-12d9-4a3a-a7ac-e87e54c5232b;ID=782a319b-3988-4cb2-aedf-47242382005c;Name=782a319b-3988-4cb2-aedf-47242382005c\n-NC_001416.1_phage_lambda\t.\texon\t14261\t14265\t.\t+\t.\tParent=c53587ff-12d9-4a3a-a7ac-e87e54c5232b;ID=709a9728-db34-4010-bfc3-41b0687edb80;Name=709a9728-db34-4010-bfc3-41b0687edb80\n-###\n-NC_001416.1_phage_lambda\t.\tgene\t47727\t47944\t.\t+\t.\towner=jolenerr@tamu.edu;ID=e3d29177-18a8-4bc0-8d91-15e5760f3bd3;date_last_modified=2020-05-13;Name=NC_001416.1_phage_lambda.gene_60;date_creation=2020-05-13\n-NC_001416.1_phage_lambda\t.\tmRNA\t47727\t47944\t.\t+\t.\towner=jolenerr@tamu.edu;Parent=e3d29177-18a8-4bc0-8d91-15e5760f3bd3;ID=90e550aa-f463-46d2-bec4-87aa38e63ee2;date_last_modified=2020-05-13;Name=NC_001416.1_phage_lambda.gene_60-00001;date_creation=2020-05-13\n-NC_001416.1_phage_lambda\t.\tCDS\t47738\t47944\t.\t+\t0\tParent=90e550aa-f463-46d2-bec4-87aa38e63ee2;ID=586909d5-f3a6-44bb-8e9e-5a5230b2127a;Name=586909d5-f3a6-44bb-8e9e-5a5230b2127a\n-NC_001416.1_phage_lambda\t.\texon\t47727\t47730\t.\t+\t.\tParent=90e550aa-f463-46d2-bec4-87aa38e63ee2;ID=bf5e1837-0469-4372-9652-7677fee29843;Name=bf5e1837-0469-4372-9652-7677fee29843\n-NC_001416.1_phage_lambda\t.\texon\t47738\t47944\t.\t+\t.\tParent=90e550aa-f463-46d2-bec4-87aa38e63ee2;ID=14232a86-ebfe-4f10-aa28-2910207542f7;Name=14232a86-ebfe-4f10-aa28-2910207542f7\n-###\n-NC_001416.1_phage_lambda\t.\tgene\t13416\t14127\t.\t+\t.\towner=jolenerr@tamu.edu;ID=11a10b01-003c-4900-aae9-46139ae23652;date_last_modified=2020-05-27;Name=tail tip protein L;date_creation=2020-05-13\n-NC_001416.1_phage_lambda\t.\tmRNA\t13416\t14127\t.\t+\t.\towner=jolenerr@tamu.edu;Parent=11a10b01-003c-4900-aae9-46139ae23652;ID=07cc76d7-d0f4-447f-a851-3aeaee1b71ac;date_last_modified=2020-05-27;Name=tail tip protein L;date_creation=2020-05-13\n-NC_001416.1_phage_lambda\t.\tCDS\t13429\t14127\t.\t+\t0\tParent=07cc76d7-d0f4-447f-a851-3aeaee1b71ac;ID=37a2e1ad-d49c-43d3-b33b-727c848e2a38;Name=37a2e1ad-d49c-43d3-b33b-727c848e2a38\n-NC_001416.1_phage_lambda\t.\texon\t13429\t14127\t.\t+\t.\tParent=07cc76d7-d0f4-447f-a851-3aeaee1b71ac;ID=008fa66b-e5b3-489b-8d99-208103afde8f;Name=008fa66b-e5b3-489b-8d99-208103afde8f\n-NC_001416.1_phage_lambda\t.\texon\t13416\t13419\t.\t+\t.\tParent=07cc76d7-d0f4-447f-a851-3aeaee1b71ac;ID=0a4ad3d9-21dc-4fab-badb-a94e4493c397;Name=0a4ad3d9-21dc-4fab-badb-a94e4493c397\n-###\n-NC_001416.1_phage_lambda\t.\tgene\t19641\t20855\t.\t+\t.\towner=jolenerr@tamu.edu;ID=e7267f7d-4717-4e28-a1a0-564b10661248;date_last_modified=2020-05-27;Name=tail fiber protein stf;date_creation=2020-05-13\n-NC_001416.1_phage_lambda\t.\tmRNA\t19641\t20855\t.\t+\t.\towner=jolenerr@tamu.edu;Parent=e7267f7d-4717-4e28-a1a0-564b10661248;ID=b7a5dc48-88c5-4edc-9081-091ef39ff3ea;date_last_modified=2020-05-27;Name=tail fiber protein stf;date_creation=2020-05-13\n-NC_001416.1_phage_lambda\t.\texon\t19641\t19644\t.\t+\t.\tParent=b7a5dc48-88c5-4edc-9081-091ef39ff3ea;ID=e794f054-733e-4b10-99df-128b37cd80e8;Name=e794f054-733e-4b10-99df-128b37cd80e8\n-NC_001416.1_phage_lambda\t.\tCDS\t19650\t20855\t.\t+\t0\tParent=b7a5dc48-88c5-4edc-9081-091ef39ff3ea;ID=f1584714-9cc6-4209-8730-0d7c199cff35;Name=f1584714-9cc6-4209-8730-0d7c199cff35\n-NC_001416.1_phage_lambda\t.\texon\t19650\t20855\t.\t+\t.\tParent=b7a5dc48-88c5-4edc-9081-091ef39ff3ea;ID=b44a883e-e4e2-4432-b665-355a803943dc;Name=b44a883e-e4e2-4432-b665-35'..b'.\tgene\t38031\t38241\t.\t+\t.\towner=jason.gill@tamu.edu;Note=added by JJG;ID=d731b2bf-e9f9-484f-8851-814510c6a5ec;date_last_modified=2020-05-27;Name=regulatory protein cro;date_creation=2019-02-07\n-NC_001416.1_phage_lambda\t.\tmRNA\t38031\t38241\t.\t+\t.\towner=jason.gill@tamu.edu;Parent=d731b2bf-e9f9-484f-8851-814510c6a5ec;ID=5cfd706b-b814-43f9-b604-fc8c179f853a;date_last_modified=2020-05-27;Name=regulatory protein cro;date_creation=2019-02-07\n-NC_001416.1_phage_lambda\t.\texon\t38031\t38035\t.\t+\t.\tParent=5cfd706b-b814-43f9-b604-fc8c179f853a;ID=041be549-96a8-42a3-b5eb-258ebb4ffd5f;Name=041be549-96a8-42a3-b5eb-258ebb4ffd5f\n-NC_001416.1_phage_lambda\t.\tCDS\t38041\t38241\t.\t+\t0\tParent=5cfd706b-b814-43f9-b604-fc8c179f853a;ID=97688d3e-13d8-48fd-9e05-2d919293f914;Name=97688d3e-13d8-48fd-9e05-2d919293f914\n-NC_001416.1_phage_lambda\t.\texon\t38041\t38241\t.\t+\t.\tParent=5cfd706b-b814-43f9-b604-fc8c179f853a;ID=e6686b84-108a-4c8f-a5b9-64b92b6dc5b5;Name=e6686b84-108a-4c8f-a5b9-64b92b6dc5b5\n-###\n-NC_001416.1_phage_lambda\t.\tgene\t38345\t38653\t.\t+\t.\towner=jason.gill@tamu.edu;Note=added by JJG;ID=1b331cf3-0897-4489-aa55-b14669ea5998;date_last_modified=2020-05-27;Name=regulatory protein cII;date_creation=2019-02-07\n-NC_001416.1_phage_lambda\t.\tmRNA\t38345\t38653\t.\t+\t.\towner=jason.gill@tamu.edu;Parent=1b331cf3-0897-4489-aa55-b14669ea5998;ID=b970dd3f-9787-459c-84ed-5b839b85b9e7;date_last_modified=2020-05-27;Name=regulatory protein cII;date_creation=2019-02-07\n-NC_001416.1_phage_lambda\t.\tCDS\t38360\t38653\t.\t+\t0\tParent=b970dd3f-9787-459c-84ed-5b839b85b9e7;ID=c4ae3e6a-4e56-412d-8ece-0036053910ef;Name=c4ae3e6a-4e56-412d-8ece-0036053910ef\n-NC_001416.1_phage_lambda\t.\texon\t38360\t38653\t.\t+\t.\tParent=b970dd3f-9787-459c-84ed-5b839b85b9e7;ID=c29fe89e-348d-4da7-b313-85c362f92307;Name=c29fe89e-348d-4da7-b313-85c362f92307\n-NC_001416.1_phage_lambda\t.\texon\t38345\t38348\t.\t+\t.\tParent=b970dd3f-9787-459c-84ed-5b839b85b9e7;ID=b2199555-1cbb-443c-92d3-fa5a0a17670e;Name=b2199555-1cbb-443c-92d3-fa5a0a17670e\n-###\n-NC_001416.1_phage_lambda\t.\tgene\t30392\t30626\t.\t-\t.\towner=jolenerr@tamu.edu;Note=Contains InterPro domain IPR000962;ID=4fba3f9c-cbc0-4454-b278-efe088695fef;date_last_modified=2020-05-27;Name=DksA/TraR C4-type zinc finger domain-containing protein;date_creation=2020-05-13\n-NC_001416.1_phage_lambda\t.\tmRNA\t30392\t30626\t.\t-\t.\towner=jolenerr@tamu.edu;Parent=4fba3f9c-cbc0-4454-b278-efe088695fef;ID=7dfc2c2d-a50d-4e77-b673-2dcd16c2ef3b;date_last_modified=2020-05-27;Name=DksA/TraR C4-type zinc finger domain-containing protein;date_creation=2020-05-13\n-NC_001416.1_phage_lambda\t.\texon\t30624\t30626\t.\t-\t.\tParent=7dfc2c2d-a50d-4e77-b673-2dcd16c2ef3b;ID=cbf0bbe9-fdae-426f-a6ce-40926606c1ca;Name=cbf0bbe9-fdae-426f-a6ce-40926606c1ca\n-NC_001416.1_phage_lambda\t.\tCDS\t30392\t30613\t.\t-\t0\tParent=7dfc2c2d-a50d-4e77-b673-2dcd16c2ef3b;ID=1fa7e4a7-9200-45d9-b9fd-21e36b254897;Name=1fa7e4a7-9200-45d9-b9fd-21e36b254897\n-NC_001416.1_phage_lambda\t.\texon\t30392\t30613\t.\t-\t.\tParent=7dfc2c2d-a50d-4e77-b673-2dcd16c2ef3b;ID=529064a4-a072-48ec-bd67-0eb3011ae53c;Name=529064a4-a072-48ec-bd67-0eb3011ae53c\n-###\n-NC_001416.1_phage_lambda\t.\tgene\t46174\t46368\t.\t+\t.\towner=jolenerr@tamu.edu;ID=18e578e1-38cb-4846-96e8-0190a5a26369;date_last_modified=2020-05-13;Name=o-spanin Rz1;date_creation=2020-05-13\n-NC_001416.1_phage_lambda\t.\tmRNA\t46174\t46368\t.\t+\t.\towner=jolenerr@tamu.edu;Parent=18e578e1-38cb-4846-96e8-0190a5a26369;ID=b4c199b2-1838-43c0-a09d-77718a49dbe2;date_last_modified=2020-05-13;Name=o-spanin Rz1;date_creation=2020-05-13\n-NC_001416.1_phage_lambda\t.\tCDS\t46186\t46368\t.\t+\t0\tParent=b4c199b2-1838-43c0-a09d-77718a49dbe2;ID=7ac7bb1c-0d6d-4476-8a02-8ad963da6156;Name=7ac7bb1c-0d6d-4476-8a02-8ad963da6156\n-NC_001416.1_phage_lambda\t.\texon\t46174\t46178\t.\t+\t.\tParent=b4c199b2-1838-43c0-a09d-77718a49dbe2;ID=668e24d1-fcea-459e-bbf3-6132bbb9854e;Name=668e24d1-fcea-459e-bbf3-6132bbb9854e\n-NC_001416.1_phage_lambda\t.\texon\t46186\t46368\t.\t+\t.\tParent=b4c199b2-1838-43c0-a09d-77718a49dbe2;ID=f8d07a76-6f8d-4100-9ba6-cec0e3a033f9;Name=f8d07a76-6f8d-4100-9ba6-cec0e3a033f9\n'
b
diff -r 6a4d1bd8ac1d -r 32e011fa615c cpt_annotation_table/test-data/PhageTable_Out.html
--- a/cpt_annotation_table/test-data/PhageTable_Out.html Fri Jun 17 12:19:58 2022 +0000
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
[
b'@@ -1,2124 +0,0 @@\n-<!DOCTYPE html>\n-<html lang="en">\n-  <head>\n-    <meta charset="utf-8">\n-    <meta http-equiv="X-UA-Compatible" content="IE=edge">\n-    <meta name="viewport" content="width=device-width, initial-scale=1">\n-    <!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags -->\n-    <meta name="description" content="">\n-    <meta name="author" content="">\n-    <title>Annotation Table</title>\n-    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.2/jquery.min.js"></script>\n-\t<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" crossorigin="anonymous">\n-\t<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap-theme.min.css" crossorigin="anonymous">\n-\t<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js" crossorigin="anonymous"></script>\n-\n-    <link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.10.11/css/jquery.dataTables.css">\n-    <script type="text/javascript" charset="utf8" src="https://cdn.datatables.net/1.10.11/js/jquery.dataTables.js"></script>\n-    <style type="text/css">\n-/*\n- * Base structure\n- */\n-\n-/* Move down content because we have a fixed navbar that is 50px tall */\n-body {\n-  padding-top: 50px;\n-}\n-\n-h3:before {\n-  display: block;\n-  content: " ";\n-  margin-top: -50px;\n-  height: 50px;\n-  visibility: hidden;\n-}\n-\n-/*\n- * Global add-ons\n- */\n-\n-.sub-header {\n-  padding-bottom: 10px;\n-  border-bottom: 1px solid #eee;\n-}\n-\n-/*\n- * Top navigation\n- * Hide default border to remove 1px line.\n- */\n-.navbar-fixed-top {\n-  border: 0;\n-}\n-\n-/*\n- * Sidebar\n- */\n-\n-/* Hide for mobile, show later */\n-.sidebar {\n-  display: none;\n-}\n-@media (min-width: 768px) {\n-  .sidebar {\n-    position: fixed;\n-    top: 51px;\n-    bottom: 0;\n-    left: 0;\n-    z-index: 1000;\n-    display: block;\n-    padding: 20px;\n-    overflow-x: hidden;\n-    overflow-y: auto; /* Scrollable contents if viewport is shorter than content. */\n-    background-color: #f5f5f5;\n-    border-right: 1px solid #eee;\n-  }\n-}\n-\n-/* Sidebar navigation */\n-.nav-sidebar {\n-  margin-right: -21px; /* 20px padding + 1px border */\n-  margin-bottom: 20px;\n-  margin-left: -20px;\n-}\n-.nav-sidebar > li > a {\n-  padding-right: 20px;\n-  padding-left: 20px;\n-}\n-.nav-sidebar > .active > a,\n-.nav-sidebar > .active > a:hover,\n-.nav-sidebar > .active > a:focus {\n-  color: #fff;\n-  background-color: #428bca;\n-}\n-\n-\n-/*\n- * Main content\n- */\n-\n-.main {\n-  padding: 20px;\n-}\n-@media (min-width: 768px) {\n-  .main {\n-    padding-right: 40px;\n-    padding-left: 40px;\n-  }\n-}\n-.main .page-header {\n-  margin-top: 0;\n-}\n-\n-\n-/*\n- * Placeholder dashboard ideas\n- */\n-\n-.placeholders {\n-  margin-bottom: 30px;\n-  text-align: center;\n-}\n-.placeholders h4 {\n-  margin-bottom: 0;\n-}\n-.placeholder {\n-  margin-bottom: 20px;\n-}\n-.placeholder img {\n-  display: inline-block;\n-  border-radius: 50%;\n-}\n-\n-td.moron {\n-    font-size: 150%;\n-    padding: 0px;\n-    color: gray;\n-}\n-.strand_emph {\n-    text-decoration: underline;\n-    color: black;\n-}\n-\n-    </style>\n-    <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->\n-    <!--[if lt IE 9]>\n-      <script src="//oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>\n-      <script src="//oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>\n-    <![endif]-->\n-  </head>\n-  <body>\n-\n-    <nav class="navbar navbar-inverse navbar-fixed-top">\n-      <div class="container-fluid">\n-        <div class="navbar-header">\n-            <a class="navbar-brand" href="#">Annotation Table</a>\n-        </div>\n-      </div>\n-    </nav>\n-\n-    <div class="container-fluid">\n-      <div class="row">\n-        <div class="col-md-12 main" id="main">\n-            <ul class="nav nav-tabs" role="tablist" id="myTabs">\n-                <li role="presentation" class="active"><a href="#overview" role="tab" data-toggle="tab">Overview</a></li>\n-  '..b'                                  <td>None</td>\n-                                    <td>None</td>\n-                                    <td>None</td>\n-                                    <td>None</td>\n-                                    </tr>\n-                                    <tr>\n-                                    <td>NC_001416-1_phage_lambda</td>\n-                                    <td>e3d29177-18a8-4bc0-8d91-15e5760f3bd3</td>\n-                                    <td>gene</td>\n-                                    <td>NC_001416.1_phage_lambda.gene_60</td>\n-                                    <td>47727..47944</td>\n-                                    <td>47727</td>\n-                                    <td>47944</td>\n-                                    <td>+</td>\n-                                    <td>68</td>\n-                                    <td>GGAG</td>\n-                                    <td>7</td>\n-                                    <td>ATG</td>\n-                                    <td>TGA</td>\n-                                    <td>74</td>\n-                                    <td>752f31a0-018a-4223-a7f9-72685604b7a3</td>\n-                                    <td>jolenerr@tamu.edu</td>\n-                                    <td>2020-05-13</td>\n-                                    <td>2020-05-13</td>\n-                                    <td>None</td>\n-                                    <td>None</td>\n-                                    <td>None</td>\n-                                    <td>None</td>\n-                                    </tr>\n-                                    <tr>\n-                                    <td>NC_001416-1_phage_lambda</td>\n-                                    <td>e0b009e4-729e-4d97-b818-ec0cbebcb1f4</td>\n-                                    <td>gene</td>\n-                                    <td>NC_001416.1_phage_lambda.orf00102</td>\n-                                    <td>48109..48314</td>\n-                                    <td>48109</td>\n-                                    <td>48314</td>\n-                                    <td>-</td>\n-                                    <td>64</td>\n-                                    <td>GGGG</td>\n-                                    <td>7</td>\n-                                    <td>ATG</td>\n-                                    <td>TGA</td>\n-                                    <td>None</td>\n-                                    <td>None</td>\n-                                    <td>jolenerr@tamu.edu</td>\n-                                    <td>2020-05-13</td>\n-                                    <td>2020-05-13</td>\n-                                    <td>None</td>\n-                                    <td>None</td>\n-                                    <td>None</td>\n-                                    <td>None</td>\n-                                    </tr>\n-                            </tbody>\n-                        </table>\n-                    </div>\n-            </div>\n-        </div>\n-      </div>\n-    </div>\n-\n-    <script type="text/javascript">\n-        $(document).ready( function () {\n-\n-\n-\n-$(\'#myTabs a\').click(function (e) {\n-\te.preventDefault()\n-\t$(this).tab(\'show\')\n-})\n-\n-\n-\n-jQuery.fn.dataTable.ext.type.detect.unshift( function ( data ) {\n-    if ( typeof data !== \'string\' ) {\n-        return null;\n-    }\n-\n-    var matches = data.match(/^(\\d+)\\.\\.(\\d+)/);\n-    return matches ? \'genomic\' : null;\n-} );\n-\n-\n-\n-jQuery.extend( jQuery.fn.dataTableExt.oSort, {\n-    "genomic-pre": function ( a ) {\n-        var matches = a.match(/^(\\d+)\\.\\.(\\d+)/);\n-        console.log(a + " " + matches[1]);\n-        return parseInt(matches[1]);\n-    },\n-\n-    "genomic-asc": function ( a, b ) {\n-        return ((a < b) ? -1 : ((a > b) ? 1 : 0));\n-    },\n-\n-    "genomic-desc": function ( a, b ) {\n-        return ((a < b) ? 1 : ((a > b) ? -1 : 0));\n-    }\n-} );\n-\n-            $(\'table\').DataTable();\n-        });\n-    </script>\n-  </body>\n-</html>\n'
b
diff -r 6a4d1bd8ac1d -r 32e011fa615c cpt_annotation_table/test-data/PhageTable_Out.tabular
--- a/cpt_annotation_table/test-data/PhageTable_Out.tabular Fri Jun 17 12:19:58 2022 +0000
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
b
b'@@ -1,79 +0,0 @@\n-# Organism ID\tID\tType\tName\tLocation\tBoundary\tBoundary\tStrand\tCDS Length (AA)\tShine-Dalgarno sequence\tShine-Dalgarno spacing\tStart Codon\tStop Codon\tDistance to next upstream gene on same strand\tNext gene upstream\tOwner\tCreated\tLast Modified\tUser entered Notes\tDescription\tProduct\tDBxrefs\t\n-NC_001416.1_phage_lambda\t1185c0aa-14c4-45c3-ab0d-889bbfdbf137\tgene\tterminase small subunit\t181..736\t181\t736\t+\t181\tGGA\t7\tATG\tTAA\tNone\tNone\tjolenerr@tamu.edu\t2020-05-13\t2020-05-27\tNone\tNone\tNone\tNone\t\n-NC_001416.1_phage_lambda\t6d452f5a-9508-40c1-9d43-d98831ca8919\tgene\tterminase large subunit\t701..2636\t701\t2636\t+\t641\tGGGT\t6\tGTG\tTGA\t-36\t1185c0aa-14c4-45c3-ab0d-889bbfdbf137\tjolenerr@tamu.edu\t2020-05-13\t2020-05-27\tNone\tNone\tNone\tNone\t\n-NC_001416.1_phage_lambda\t38175e36-b9e9-48ea-9d1c-7f115fae7194\tgene\thead completion protein\t2622..2839\t2622\t2839\t+\t68\tGGAG\t7\tATG\tTGA\t-15\t6d452f5a-9508-40c1-9d43-d98831ca8919\tjolenerr@tamu.edu\t2020-05-13\t2020-05-28\tInterPro domain IPR004174\tNone\tNone\tNone\t\n-NC_001416.1_phage_lambda\t0695da41-cce8-4f07-a27a-eb42ac82ae56\tgene\tportal protein B\t2824..4437\t2824\t4437\t+\t533\tAGGA\t8\tATG\tTAA\t-16\t38175e36-b9e9-48ea-9d1c-7f115fae7194\tjolenerr@tamu.edu\t2020-05-13\t2020-05-27\tNone\tNone\tNone\tNone\t\n-NC_001416.1_phage_lambda\t422aab41-ac49-4818-b87e-9331023dcacf\tgene\tcapsid assembly protein\t4406..5737\t4406\t5737\t+\t673\t"GGAG, AGGAGG"\t"-609, 6"\t"GTG (4417..5737:1), GTG (5035..5737:1)"\tTAA\t-32\t0695da41-cce8-4f07-a27a-eb42ac82ae56\tjolenerr@tamu.edu\t2020-05-13\t2020-05-27\tNone\tNone\tNone\tNone\t\n-NC_001416.1_phage_lambda\t274304d3-079e-4e08-aa11-219235e1aacb\tgene\tcapsid decoration protein\t5739..6079\t5739\t6079\t+\t110\tGGA\t5\tATG\tTAA\t1\t422aab41-ac49-4818-b87e-9331023dcacf\tjolenerr@tamu.edu\t2020-05-13\t2020-05-28\tNone\tNone\tNone\tNone\t\n-NC_001416.1_phage_lambda\t4f815f9e-5092-4db3-b722-0d912c116293\tgene\tmajor capsid protein\t6099..7160\t6099\t7160\t+\t350\tAGG\t6\tGTG\tTAA\t19\t274304d3-079e-4e08-aa11-219235e1aacb\tjolenerr@tamu.edu\t2020-05-13\t2020-05-27\tNone\tNone\tNone\tNone\t\n-NC_001416.1_phage_lambda\t4da662d1-028a-43e2-9b50-f48ad03bdd64\tgene\tDNA packaging protein FI\t7173..7600\t7173\t7600\t+\t136\tGGGG\t13\tGTG\tTAA\t12\t4f815f9e-5092-4db3-b722-0d912c116293\tjolenerr@tamu.edu\t2020-05-13\t2020-05-27\tNone\tNone\tNone\tNone\t\n-NC_001416.1_phage_lambda\t638d1ac5-aea4-4988-b46d-01cf79793dfb\tgene\thead-tail connector protein FII\t7603..7965\t7603\t7965\t+\t117\tGGAG\t5\tGTG\tTGA\t2\t4da662d1-028a-43e2-9b50-f48ad03bdd64\tjolenerr@tamu.edu\t2020-05-13\t2020-05-27\tNone\tNone\tNone\tNone\t\n-NC_001416.1_phage_lambda\tb541056e-bf91-4113-bce7-86bab4d27f76\tgene\ttail completion protein Z\t7968..8555\t7968\t8555\t+\t192\tGGGG\t5\tATG\tTGA\t2\t638d1ac5-aea4-4988-b46d-01cf79793dfb\tjolenerr@tamu.edu\t2020-05-13\t2020-05-27\tNone\tNone\tNone\tNone\t\n-NC_001416.1_phage_lambda\tfca8745c-c433-4845-9430-c8a9dd4dc37b\tgene\ttail tube terminator protein U\t8535..8947\t8535\t8947\t+\t131\tAGGA\t13\tATG\tTGA\t-21\tb541056e-bf91-4113-bce7-86bab4d27f76\tjolenerr@tamu.edu\t2020-05-13\t2020-05-27\tNone\tNone\tNone\tNone\t\n-NC_001416.1_phage_lambda\td9ee7b0e-b4a7-48cf-8c4b-aa95ca8863e0\tgene\ttail tube protein V\t8946..9695\t8946\t9695\t+\t246\tGAGG\t5\tATG\tTAA\t-2\tfca8745c-c433-4845-9430-c8a9dd4dc37b\tjolenerr@tamu.edu\t2020-05-13\t2020-05-27\tNone\tNone\tNone\tNone\t\n-NC_001416.1_phage_lambda\t0b5a859a-39f6-4c49-91b7-0b2def466374\tgene\ttape measure chaperone GT\t9699..10133\t9699\t10133\t+\t275\t"GGAG, GGAG"\t"8, 8"\t"ATG (9710..10133:1), ATG (9710..10115:1)"\tTGA\t3\td9ee7b0e-b4a7-48cf-8c4b-aa95ca8863e0\tjolenerr@tamu.edu\t2020-05-13\t2020-05-27\tNone\tNone\tNone\tNone\t\n-NC_001416.1_phage_lambda\tc6a11d3b-7b9d-446f-ba16-3a642f3f6fbd\tgene\ttape measure chaperone GT\t10115..10549\t10115\t10549\t+\t144\tNone\tNone\tGTG\tTGA\t-19\t0b5a859a-39f6-4c49-91b7-0b2def466374\tjolenerr@tamu.edu\t2020-05-13\t2020-05-27\tNone\tNone\tNone\tNone\t\n-NC_001416.1_phage_lambda\t7ef9ffad-d420-4a1c-9a8d-2312dc04b558\tgene\ttape measure protein H\t10528..13103\t10528\t13103\t+\t853\tAGGAGG\t8\tATG\tTGA\t-22\tc6a11d3b-7b9d-446f-ba16-3a642f3f6fbd\tjolenerr@tamu.edu\t2020-05-13\t2020-05-27\tNone\tNone\tNone\tNone\t\n-NC_001416.1_phage_lambda\t55fe46da-9e0e-4454-aff9-f'..b'002363577\t42080..42272\t42080\t42272\t+\t60\tAGGAG\t5\tGTG\tTGA\t-44\tc402b694-7971-451b-aa24-4e70550eef2d\tjolenerr@tamu.edu\t2020-05-13\t2020-05-13\tNone\tNone\tNone\tNone\t\n-NC_001416.1_phage_lambda\t49135134-fa86-4df5-a0d7-b629ff152305\tgene\tCDS.0.1596_0.0419290277112\t42230..42439\t42230\t42439\t+\t66\tGGA\t6\tGTG\tTAA\t-43\t61a0d8bd-5b58-4077-bc04-2bcf4dee8939\tjolenerr@tamu.edu\t2020-05-13\t2020-05-13\tNone\tNone\tNone\tNone\t\n-NC_001416.1_phage_lambda\ta8c3d9d2-cc66-4394-a821-ec0db2e411de\tgene\tNC_001416.1_phage_lambda.gene_51\t42419..43043\t42419\t43043\t+\t204\tGAGG\t6\tATG\tTGA\t-21\t49135134-fa86-4df5-a0d7-b629ff152305\tjolenerr@tamu.edu\t2020-05-13\t2020-05-13\tNone\tNone\tNone\tNone\t\n-NC_001416.1_phage_lambda\t0cea2875-2757-4c9b-97f5-7b0ec545394a\tgene\tNC_001416.1_phage_lambda.gene_52\t43032..43246\t43032\t43246\t+\t68\tGAG\t5\tATG\tTGA\t-12\ta8c3d9d2-cc66-4394-a821-ec0db2e411de\tjolenerr@tamu.edu\t2020-05-13\t2020-05-13\tNone\tNone\tNone\tNone\t\n-NC_001416.1_phage_lambda\t8ce6427a-594a-4320-955b-be6dd71ab223\tgene\tNC_001416.1_phage_lambda.gene_53\t43135..43889\t43135\t43889\t+\t221\tGGAG\t8\tATG\tTGA\t-112\t0cea2875-2757-4c9b-97f5-7b0ec545394a\tjolenerr@tamu.edu\t2020-05-13\t2020-05-13\tNone\tNone\tNone\tNone\t\n-NC_001416.1_phage_lambda\t1653554c-4be1-453b-a377-86e20759321a\tgene\tlate gene regulator\t43875..44509\t43875\t44509\t+\t207\tGGAG\t7\tATG\tTAG\t-15\t8ce6427a-594a-4320-955b-be6dd71ab223\tjolenerr@tamu.edu\t2020-05-13\t2020-05-27\tNone\tNone\tNone\tNone\t\n-NC_001416.1_phage_lambda\t6c3ab9c5-a407-47b8-89df-fa997262a409\tgene\tCDS.0.1630_0.132755174165\t44612..44815\t44612\t44815\t+\t64\tGGT\t6\tATG\tTAA\t102\t1653554c-4be1-453b-a377-86e20759321a\tjolenerr@tamu.edu\t2020-05-13\t2020-05-13\tNone\tNone\tNone\tNone\t\n-NC_001416.1_phage_lambda\t94064736-88a6-4e82-8c89-8226f57526e8\tgene\tantiholin S107\t45175..45509\t45175\t45509\t+\t213\t"GGGGG, GGGGG"\t"6, 6"\t"ATG (45185..45509:1), ATG (45191..45509:1)"\tTAA\t359\t6c3ab9c5-a407-47b8-89df-fa997262a409\tjolenerr@tamu.edu\t2020-05-13\t2020-05-13\tNone\tNone\tNone\tNone\t\n-NC_001416.1_phage_lambda\tf8ba23df-ca21-4307-8f5a-ef8243f01acc\tgene\tendolysin R\t45483..45969\t45483\t45969\t+\t158\tGGAG\t6\tATG\tTGA\t-27\t94064736-88a6-4e82-8c89-8226f57526e8\tjolenerr@tamu.edu\t2020-05-13\t2020-05-13\tNone\tNone\tNone\tNone\t\n-NC_001416.1_phage_lambda\t4795342c-7959-44d9-925a-781f17371e7a\tgene\ti-spanin Rz\t45953..46427\t45953\t46427\t+\t153\tGAG\t10\tATG\tTAG\t-17\tf8ba23df-ca21-4307-8f5a-ef8243f01acc\tjolenerr@tamu.edu\t2020-05-13\t2020-05-13\tNone\tNone\tNone\tNone\t\n-NC_001416.1_phage_lambda\t18e578e1-38cb-4846-96e8-0190a5a26369\tgene\to-spanin Rz1\t46174..46368\t46174\t46368\t+\t60\tAGGAG\t7\tATG\tTGA\t-254\t4795342c-7959-44d9-925a-781f17371e7a\tjolenerr@tamu.edu\t2020-05-13\t2020-05-13\tNone\tNone\tNone\tNone\t\n-NC_001416.1_phage_lambda\t6eedd5c5-3c44-404a-af31-acee26522035\tgene\tbor\t46459..46763\t46459\t46763\t-\t97\tGGA\t8\tATG\tTAA\t132\t2da1b434-7877-4aa2-b88b-24922f70988b\tjolenerr@tamu.edu\t2020-05-13\t2020-05-13\tNone\tNone\tNone\tNone\t\n-NC_001416.1_phage_lambda\t2da1b434-7877-4aa2-b88b-24922f70988b\tgene\tCDS.0.3497_0.36888415839\t46896..47029\t46896\t47029\t-\t32\tGGGG\t10\tATG\tTAA\t12\t5d4e2403-4389-497c-a220-9740cd9935cc\tjolenerr@tamu.edu\t2020-05-13\t2020-05-13\tNone\tNone\tNone\tNone\t\n-NC_001416.1_phage_lambda\t5d4e2403-4389-497c-a220-9740cd9935cc\tgene\tCDS.0.2625_0.768191840841\t47042..47463\t47042\t47463\t-\t136\tAGGT\t7\tATG\tTAA\t645\te0b009e4-729e-4d97-b818-ec0cbebcb1f4\tjolenerr@tamu.edu\t2020-05-13\t2020-05-13\tNone\tNone\tNone\tNone\t\n-NC_001416.1_phage_lambda\t752f31a0-018a-4223-a7f9-72685604b7a3\tgene\tCDS.0.864_0.667986446525\t47518..47652\t47518\t47652\t+\t40\tGGT\t9\tTTG\tTAA\t1149\t18e578e1-38cb-4846-96e8-0190a5a26369\tjolenerr@tamu.edu\t2020-05-13\t2020-05-13\tNone\tNone\tNone\tNone\t\n-NC_001416.1_phage_lambda\te3d29177-18a8-4bc0-8d91-15e5760f3bd3\tgene\tNC_001416.1_phage_lambda.gene_60\t47727..47944\t47727\t47944\t+\t68\tGGAG\t7\tATG\tTGA\t74\t752f31a0-018a-4223-a7f9-72685604b7a3\tjolenerr@tamu.edu\t2020-05-13\t2020-05-13\tNone\tNone\tNone\tNone\t\n-NC_001416.1_phage_lambda\te0b009e4-729e-4d97-b818-ec0cbebcb1f4\tgene\tNC_001416.1_phage_lambda.orf00102\t48109..48314\t48109\t48314\t-\t64\tGGGG\t7\tATG\tTGA\tNone\tNone\tjolenerr@tamu.edu\t2020-05-13\t2020-05-13\tNone\tNone\tNone\tNone\t\n'
b
diff -r 6a4d1bd8ac1d -r 32e011fa615c gff3.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/gff3.py Wed Apr 26 03:42:32 2023 +0000
[
b'@@ -0,0 +1,346 @@\n+import copy\n+import logging\n+\n+log = logging.getLogger()\n+log.setLevel(logging.WARN)\n+\n+\n+def feature_lambda(\n+    feature_list,\n+    test,\n+    test_kwargs,\n+    subfeatures=True,\n+    parent=None,\n+    invert=False,\n+    recurse=True,\n+):\n+    """Recursively search through features, testing each with a test function, yielding matches.\n+\n+    GFF3 is a hierachical data structure, so we need to be able to recursively\n+    search through features. E.g. if you\'re looking for a feature with\n+    ID=\'bob.42\', you can\'t just do a simple list comprehension with a test\n+    case. You don\'t know how deeply burried bob.42 will be in the feature tree. This is where feature_lambda steps in.\n+\n+    :type feature_list: list\n+    :param feature_list: an iterable of features\n+\n+    :type test: function reference\n+    :param test: a closure with the method signature (feature, **kwargs) where\n+                 the kwargs are those passed in the next argument. This\n+                 function should return True or False, True if the feature is\n+                 to be yielded as part of the main feature_lambda function, or\n+                 False if it is to be ignored. This function CAN mutate the\n+                 features passed to it (think "apply").\n+\n+    :type test_kwargs: dictionary\n+    :param test_kwargs: kwargs to pass to your closure when it is called.\n+\n+    :type subfeatures: boolean\n+    :param subfeatures: when a feature is matched, should just that feature be\n+                        yielded to the caller, or should the entire sub_feature\n+                        tree for that feature be included? subfeatures=True is\n+                        useful in cases such as searching for a gene feature,\n+                        and wanting to know what RBS/Shine_Dalgarno_sequences\n+                        are in the sub_feature tree (which can be accomplished\n+                        with two feature_lambda calls). subfeatures=False is\n+                        useful in cases when you want to process (and possibly\n+                        return) the entire feature tree, such as applying a\n+                        qualifier to every single feature.\n+\n+    :type invert: boolean\n+    :param invert: Negate/invert the result of the filter.\n+\n+    :rtype: yielded list\n+    :return: Yields a list of matching features.\n+    """\n+    # Either the top level set of [features] or the subfeature attribute\n+    for feature in feature_list:\n+        feature._parent = parent\n+        if not parent:\n+            # Set to self so we cannot go above root.\n+            feature._parent = feature\n+        test_result = test(feature, **test_kwargs)\n+        # if (not invert and test_result) or (invert and not test_result):\n+        if invert ^ test_result:\n+            if not subfeatures:\n+                feature_copy = copy.deepcopy(feature)\n+                feature_copy.sub_features = list()\n+                yield feature_copy\n+            else:\n+                yield feature\n+\n+        if recurse and hasattr(feature, "sub_features"):\n+            for x in feature_lambda(\n+                feature.sub_features,\n+                test,\n+                test_kwargs,\n+                subfeatures=subfeatures,\n+                parent=feature,\n+                invert=invert,\n+                recurse=recurse,\n+            ):\n+                yield x\n+\n+\n+def fetchParent(feature):\n+    if not hasattr(feature, "_parent") or feature._parent is None:\n+        return feature\n+    else:\n+        return fetchParent(feature._parent)\n+\n+\n+def feature_test_true(feature, **kwargs):\n+    return True\n+\n+\n+def feature_test_type(feature, **kwargs):\n+    if "type" in kwargs:\n+        return str(feature.type).upper() == str(kwargs["type"]).upper()\n+    elif "types" in kwargs:\n+        for x in kwargs["types"]:\n+            if str(feature.type).upper() == str(x).upper():\n+                return True\n+        return False\n+    raise Exception("Incorrect feature_test'..b'feature.location.start,\n+        # feature.location.end,\n+        # feature.location.strand\n+        # )\n+    return result\n+\n+\n+def get_gff3_id(gene):\n+    return gene.qualifiers.get("Name", [gene.id])[0]\n+\n+\n+def ensure_location_in_bounds(start=0, end=0, parent_length=0):\n+    # This prevents frameshift errors\n+    while start < 0:\n+        start += 3\n+    while end < 0:\n+        end += 3\n+    while start > parent_length:\n+        start -= 3\n+    while end > parent_length:\n+        end -= 3\n+    return (start, end)\n+\n+\n+def coding_genes(feature_list):\n+    for x in genes(feature_list):\n+        if (\n+            len(\n+                list(\n+                    feature_lambda(\n+                        x.sub_features,\n+                        feature_test_type,\n+                        {"type": "CDS"},\n+                        subfeatures=False,\n+                    )\n+                )\n+            )\n+            > 0\n+        ):\n+            yield x\n+\n+\n+def genes(feature_list, feature_type="gene", sort=False):\n+    """\n+    Simple filter to extract gene features from the feature set.\n+    """\n+\n+    if not sort:\n+        for x in feature_lambda(\n+            feature_list, feature_test_type, {"type": feature_type}, subfeatures=True\n+        ):\n+            yield x\n+    else:\n+        data = list(genes(feature_list, feature_type=feature_type, sort=False))\n+        data = sorted(data, key=lambda feature: feature.location.start)\n+        for x in data:\n+            yield x\n+\n+\n+def wa_unified_product_name(feature):\n+    """\n+    Try and figure out a name. We gave conflicting instructions, so\n+    this isn\'t as trivial as it should be. Sometimes it will be in\n+    \'product\' or \'Product\', othertimes in \'Name\'\n+    """\n+    # Manually applied tags.\n+    protein_product = feature.qualifiers.get(\n+        "product", feature.qualifiers.get("Product", [None])\n+    )[0]\n+\n+    # If neither of those are available ...\n+    if protein_product is None:\n+        # And there\'s a name...\n+        if "Name" in feature.qualifiers:\n+            if not is_uuid(feature.qualifiers["Name"][0]):\n+                protein_product = feature.qualifiers["Name"][0]\n+\n+    return protein_product\n+\n+\n+def is_uuid(name):\n+    return name.count("-") == 4 and len(name) == 36\n+\n+\n+def get_rbs_from(gene):\n+    # Normal RBS annotation types\n+    rbs_rbs = list(\n+        feature_lambda(\n+            gene.sub_features, feature_test_type, {"type": "RBS"}, subfeatures=False\n+        )\n+    )\n+    rbs_sds = list(\n+        feature_lambda(\n+            gene.sub_features,\n+            feature_test_type,\n+            {"type": "Shine_Dalgarno_sequence"},\n+            subfeatures=False,\n+        )\n+    )\n+    # Fraking apollo\n+    apollo_exons = list(\n+        feature_lambda(\n+            gene.sub_features, feature_test_type, {"type": "exon"}, subfeatures=False\n+        )\n+    )\n+    apollo_exons = [x for x in apollo_exons if len(x) < 10]\n+    # These are more NCBI\'s style\n+    regulatory_elements = list(\n+        feature_lambda(\n+            gene.sub_features,\n+            feature_test_type,\n+            {"type": "regulatory"},\n+            subfeatures=False,\n+        )\n+    )\n+    rbs_regulatory = list(\n+        feature_lambda(\n+            regulatory_elements,\n+            feature_test_quals,\n+            {"regulatory_class": ["ribosome_binding_site"]},\n+            subfeatures=False,\n+        )\n+    )\n+    # Here\'s hoping you find just one ;)\n+    return rbs_rbs + rbs_sds + rbs_regulatory + apollo_exons\n+\n+\n+def nice_name(record):\n+    """\n+    get the real name rather than NCBI IDs and so on. If fails, will return record.id\n+    """\n+    name = record.id\n+    likely_parental_contig = list(genes(record.features, feature_type="contig"))\n+    if len(likely_parental_contig) == 1:\n+        name = likely_parental_contig[0].qualifiers.get("organism", [name])[0]\n+    return name\n+\n+\n+def fsort(it):\n+    for i in sorted(it, key=lambda x: int(x.location.start)):\n+        yield i\n'
b
diff -r 6a4d1bd8ac1d -r 32e011fa615c macros.xml
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/macros.xml Wed Apr 26 03:42:32 2023 +0000
b
@@ -0,0 +1,74 @@
+<macros>
+    <xml name="requirements">
+        <requirements>
+            <requirement type="package">progressivemauve</requirement>
+            <!--<requirement type="package" version="2.7">python</requirement>-->
+            <requirement type="package" version="0.6.4">bcbiogff</requirement>
+            <yield/>
+        </requirements>
+    </xml>
+    <token name="@WRAPPER_VERSION@">2.4.0</token>
+    <xml name="citation/progressive_mauve">
+        <citation type="doi">10.1371/journal.pone.0011147</citation>
+    </xml>
+    <xml name="citation/gepard">
+        <citation type="doi">10.1093/bioinformatics/btm039</citation>
+    </xml>
+    <token name="@XMFA_INPUT@">
+ '$xmfa'
+ </token>
+    <xml name="xmfa_input" token_formats="xmfa">
+        <param type="data" format="@FORMATS@" name="xmfa" label="XMFA MSA"/>
+    </xml>
+    <token name="@XMFA_FA_INPUT@">
+ '$sequences'
+ </token>
+    <xml name="xmfa_fa_input">
+        <param type="data" format="fasta" name="sequences" label="Sequences in alignment" help="These sequences should be the SAME DATASET that was used in the progressiveMauve run. Failing that, they should be provided in the same order as in original progressiveMauve run"/>
+    </xml>
+    <xml name="genome_selector">
+        <conditional name="reference_genome">
+            <param name="reference_genome_source" type="select" label="Reference Genome">
+                <option value="history" selected="True">From History</option>
+                <option value="cached">Locally Cached</option>
+            </param>
+            <when value="cached">
+                <param name="fasta_indexes" type="select" label="Source FASTA Sequence">
+                    <options from_data_table="all_fasta"/>
+                </param>
+            </when>
+            <when value="history">
+                <param name="genome_fasta" type="data" format="fasta" label="Source FASTA Sequence"/>
+            </when>
+        </conditional>
+    </xml>
+    <xml name="gff3_input">
+        <param label="GFF3 Annotations" name="gff3_data" type="data" format="gff3"/>
+    </xml>
+    <xml name="input/gff3+fasta">
+        <expand macro="gff3_input"/>
+        <expand macro="genome_selector"/>
+    </xml>
+    <token name="@INPUT_GFF@">
+     '$gff3_data'
+ </token>
+    <token name="@INPUT_FASTA@">
+    #if str($reference_genome.reference_genome_source) == 'cached':
+            '${reference_genome.fasta_indexes.fields.path}'
+    #else if str($reference_genome.reference_genome_source) == 'history':
+            genomeref.fa
+    #end if
+ </token>
+    <token name="@GENOME_SELECTOR_PRE@">
+    #if $reference_genome.reference_genome_source == 'history':
+            ln -s '$reference_genome.genome_fasta' genomeref.fa;
+    #end if
+ </token>
+    <token name="@GENOME_SELECTOR@">
+    #if str($reference_genome.reference_genome_source) == 'cached':
+            '${reference_genome.fasta_indexes.fields.path}'
+    #else if str($reference_genome.reference_genome_source) == 'history':
+            genomeref.fa
+    #end if
+ </token>
+</macros>
b
diff -r 6a4d1bd8ac1d -r 32e011fa615c phage_annotation_table.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/phage_annotation_table.py Wed Apr 26 03:42:32 2023 +0000
[
b'@@ -0,0 +1,608 @@\n+#!/usr/bin/env python\n+# vim: set fileencoding=utf-8\n+import os\n+import argparse\n+from gff3 import (\n+    genes,\n+    get_gff3_id,\n+    get_rbs_from,\n+    feature_test_true,\n+    feature_lambda,\n+    feature_test_type,\n+)\n+from CPT_GFFParser import gffParse, gffWrite\n+from Bio import SeqIO\n+from jinja2 import Environment, FileSystemLoader\n+import logging\n+from math import floor\n+\n+logging.basicConfig(level=logging.DEBUG)\n+log = logging.getLogger(name="pat")\n+\n+# Path to script, required because of Galaxy.\n+SCRIPT_PATH = os.path.dirname(os.path.realpath(__file__))\n+# Path to the HTML template for the report\n+\n+\n+def genes_all(feature_list, feature_type=["gene"], sort=False):\n+    """\n+    Simple filter to extract gene features from the feature set.\n+    """\n+\n+    if not sort:\n+        for x in feature_lambda(\n+            feature_list, feature_test_type, {"types": feature_type}, subfeatures=True\n+        ):\n+            yield x\n+    else:\n+        data = list(genes_all(feature_list, feature_type, sort=False))\n+        data = sorted(data, key=lambda feature: feature.location.start)\n+        for x in data:\n+            yield x\n+\n+\n+def checkSubs(feature, qualName):\n+    subFeats = []\n+    res = ""\n+    subFeats = feature.sub_features\n+    while len(subFeats) > 0:\n+        for feat in subFeats:\n+            for i in feat.qualifiers.keys():\n+                for j in qualName:\n+                    if i == j:\n+                        if res == "":\n+                            res = feat.qualifiers[i][0]\n+                        else:\n+                            res += "; " + feat.qualifiers[i][0]\n+        if res != "":\n+            return res\n+        tempFeats = []\n+        for feat in subFeats:  # Should be breadth-first results\n+            for x in feat.sub_features:\n+                tempFeats.append(x)\n+        subFeats = tempFeats\n+    return res\n+\n+\n+def annotation_table_report(record, types, wanted_cols, gaf_data, searchSubs):\n+    getTypes = []\n+    for x in [y.strip() for y in types.split(",")]:\n+        getTypes.append(x)\n+    getTypes.append("gene")\n+    sorted_features = list(genes_all(record.features, getTypes, sort=True))\n+    if wanted_cols is None or len(wanted_cols.strip()) == 0:\n+        return [], []\n+    useSubs = searchSubs\n+\n+    def rid(record, feature):\n+        """Organism ID"""\n+        return record.id\n+\n+    def id(record, feature):\n+        """ID"""\n+        return feature.id\n+\n+    def featureType(record, feature):\n+        """Type"""\n+        return feature.type\n+\n+    def name(record, feature):\n+        """Name"""\n+        for x in ["Name", "name"]:\n+            for y in feature.qualifiers.keys():\n+                if x == y:\n+                    return feature.qualifiers[x][0]\n+        if useSubs:\n+            res = checkSubs(feature, ["Name", "name"])\n+            if res != "":\n+                return res\n+        return "None"\n+\n+    def start(record, feature):\n+        """Boundary"""\n+        return str(feature.location.start + 1)\n+\n+    def end(record, feature):\n+        """Boundary"""\n+        return str(feature.location.end)\n+\n+    def location(record, feature):\n+        """Location"""\n+        return str(feature.location.start + 1) + "..{0.end}".format(feature.location)\n+\n+    def length(record, feature):\n+        """CDS Length (AA)"""\n+\n+        if feature.type == "CDS":\n+            cdss = [feature]\n+        else:\n+            cdss = list(genes(feature.sub_features, feature_type="CDS", sort=True))\n+\n+        if cdss == []:\n+            return "None"\n+        res = (sum([len(cds) for cds in cdss]) / 3) - 1\n+        if floor(res) == res:\n+            res = int(res)\n+        return str(res)\n+\n+    def notes(record, feature):\n+        """User entered Notes"""\n+        for x in ["Note", "note", "Notes", "notes"]:\n+            for y in feature.qualifiers.keys():\n+                if x == y:\n+                    return feature.qualifiers[x][0]\n+        if useSubs:\n+            r'..b'd8-49c8-b724-d6aa4df5a98d\': {\n+    # \'annotation_extension\': \'\',\n+    # \'aspect\': \'\',\n+    # \'assigned_by\': \'CPT\',\n+    # \'date\': \'2017-05-04T16:25:22.161916Z\',\n+    # \'db\': \'UniProtKB\',\n+    # \'db_reference\': \'GO_REF:0000100\',\n+    # \'evidence_code\': \'ISA\',\n+    # \'gene\': \'0d307196-833d-46e8-90e9-d80f7a041d88\',\n+    # \'go_id\': \'GO:0039660\',\n+    # \'go_term\': \'structural constituent of virion\',\n+    # \'id\': \'10d04a01-5ed8-49c8-b724-d6aa4df5a98d\',\n+    # \'notes\': \'hit was putative minor structural protein\',\n+    # \'owner\': \'amarc1@tamu.edu\',\n+    # \'with_or_from\': \'UNIREF90:B2ZYZ7\'\n+    # },\n+    for row in file:\n+        if row.startswith("#"):\n+            # Header\n+            cols = (\n+                row.strip().replace("# ", "").replace("GO Term", "go_term").split("\\t")\n+            )\n+        else:\n+            line = row.strip().split("\\t")\n+            tmp = dict(zip(cols, line))\n+            if "gene" not in tmp.keys():\n+                continue\n+            if tmp["gene"] not in data:\n+                data[tmp["gene"]] = []\n+\n+            data[tmp["gene"]].append(tmp)\n+    return data\n+\n+\n+def evaluate_and_report(\n+    annotations,\n+    genome,\n+    types="gene",\n+    reportTemplateName="phage_annotation_validator.html",\n+    annotationTableCols="",\n+    gafData=None,\n+    searchSubs=False,\n+):\n+    """\n+    Generate our HTML evaluation of the genome\n+    """\n+    # Get features from GFF file\n+    seq_dict = SeqIO.to_dict(SeqIO.parse(genome, "fasta"))\n+    # Get the first GFF3 record\n+    # TODO: support multiple GFF3 files.\n+    at_table_data = []\n+    gaf = {}\n+    if gafData:\n+        gaf = parseGafData(gafData)\n+\n+    for record in gffParse(annotations, base_dict=seq_dict):\n+        if reportTemplateName.endswith(".html"):\n+            record.id = record.id.replace(".", "-")\n+        log.info("Producing an annotation table for %s" % record.id)\n+        annotation_table_data, annotation_table_col_names = annotation_table_report(\n+            record, types, annotationTableCols, gaf, searchSubs\n+        )\n+        at_table_data.append((record, annotation_table_data))\n+        # break\n+\n+    # This is data that will go into our HTML template\n+    kwargs = {\n+        "annotation_table_data": at_table_data,\n+        "annotation_table_col_names": annotation_table_col_names,\n+    }\n+\n+    env = Environment(\n+        loader=FileSystemLoader(SCRIPT_PATH), trim_blocks=True, lstrip_blocks=True\n+    )\n+    if reportTemplateName.endswith(".html"):\n+        env.filters["nice_id"] = str(get_gff3_id).replace(".", "-")\n+    else:\n+        env.filters["nice_id"] = get_gff3_id\n+\n+    def join(listy):\n+        return "\\n".join(listy)\n+\n+    env.filters.update({"join": join})\n+    tpl = env.get_template(reportTemplateName)\n+    return tpl.render(**kwargs).encode("utf-8")\n+\n+\n+if __name__ == "__main__":\n+    parser = argparse.ArgumentParser(\n+        description="rebase gff3 features against parent locations", epilog=""\n+    )\n+    parser.add_argument(\n+        "annotations", type=argparse.FileType("r"), help="Parent GFF3 annotations"\n+    )\n+    parser.add_argument("genome", type=argparse.FileType("r"), help="Genome Sequence")\n+\n+    parser.add_argument(\n+        "--types",\n+        help="Select extra types to display in output (Will always include gene)",\n+    )\n+\n+    parser.add_argument(\n+        "--reportTemplateName",\n+        help="Report template file name",\n+        default="phageqc_report_full.html",\n+    )\n+    parser.add_argument(\n+        "--annotationTableCols",\n+        help="Select columns to report in the annotation table output format",\n+    )\n+    parser.add_argument(\n+        "--gafData", help="CPT GAF-like table", type=argparse.FileType("r")\n+    )\n+    parser.add_argument(\n+        "--searchSubs",\n+        help="Attempt to populate fields from sub-features if qualifier is empty",\n+        action="store_true",\n+    )\n+\n+    args = parser.parse_args()\n+\n+    print(evaluate_and_report(**vars(args)).decode("utf-8"))\n'
b
diff -r 6a4d1bd8ac1d -r 32e011fa615c phage_annotation_table.xml
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/phage_annotation_table.xml Wed Apr 26 03:42:32 2023 +0000
[
@@ -0,0 +1,104 @@
+<tool id="edu.tamu.cpt2.phage.annotation_table" name="Annotation Table" version="20.0.0.1">
+    <description>from gff3 formatted datasets</description>
+    <macros>
+        <import>macros.xml</import>
+        <import>cpt-macros.xml</import>
+    </macros>
+    <expand macro="requirements">
+        <requirement type="package" version="2.10.1">jinja2</requirement>
+    </expand>
+    <command detect_errors="aggressive"><![CDATA[
+@GENOME_SELECTOR_PRE@
+
+python '$__tool_directory__/phage_annotation_table.py'
+'$gff3_data'
+@GENOME_SELECTOR@
+--types "$extraTypes"
+--reportTemplateName "$report_format"
+--annotationTableCols "$cols,$gaf_cols"
+#if $gaf_data:
+--gafData "$gaf_data"
+#end if
+'$checkSubfeats'
+> '$output']]></command>
+    <inputs>
+        <expand macro="gff3_input"/>
+        <expand macro="genome_selector"/>
+        <param label="Extra sub-feature types to include (Comma-separated, Will always include gene)" optional="True" name="extraTypes" type="text" value=""/>
+        <param label="Columns" type="select" name="cols" multiple="True">
+            <option value="rid" selected="true">Record ID</option>
+            <option value="id" selected="true">ID</option>
+            <option value="type" selected="true">Type</option>
+            <option value="name" selected="true">Name</option>
+            <option value="location" selected="true">Location</option>
+            <option value="start" selected="true">Feature Start</option>
+            <option value="end" selected="true">Feature End</option>
+            <option value="strand" selected="true">Strand</option>
+            <option value="length" selected="true">Length</option>
+            <option value="sd_seq" selected="true">SD sequence</option>
+            <option value="sd_spacing" selected="true">SD spacing</option>
+            <option value="start_codon" selected="true">Start codon</option>
+            <option value="stop_codon" selected="true">Stop codon</option>
+            <option value="ig_dist" selected="true">Distance to next upstream feature</option>
+            <option value="upstream_feature__name" selected="true">Name of upstream feature</option>
+            <option value="owner" selected="true">Owner</option>
+            <option value="date_created" selected="true">Date created</option>
+            <option value="date_last_modified" selected="true">Date last modified</option>
+            <option value="notes" selected="true">Notes</option>
+            <option value="description" selected="true">Description</option>
+            <option value="product" selected="true">Product</option>
+            <option value="dbxrefs" selected="true">DBxrefs</option>
+            <option value="qualifiers" selected="true">Qualifiers</option>
+        </param>
+        <param label="GAF Data" name="gaf_data" type="data" format="tabular" optional="True" help="Optional GAF Data Table. This usually comes from the CPT CACAO GAF Data Export tool. The [GAF] Columns below are ONLY available when this data is populated."/>
+        <param label="GAF Columns" type="select" name="gaf_cols" multiple="True">
+            <!-- GAF Specific Fields -->
+            <option value="gaf_annotation_extension" selected="false">GAF Annotation Extension</option>
+            <option value="gaf_aspect" selected="false">GAF Aspect</option>
+            <option value="gaf_assigned_by" selected="false">GAF Assigned By</option>
+            <option value="gaf_date" selected="false">GAF Date</option>
+            <option value="gaf_db" selected="false">GAF DB</option>
+            <option value="gaf_db_reference" selected="false">GAF DB Reference</option>
+            <option value="gaf_evidence_code" selected="false">GAF Evidence Code</option>
+            <option value="gaf_gene" selected="false">GAF Gene</option>
+            <option value="gaf_go_id" selected="false">GAF GO ID</option>
+            <option value="gaf_go_term" selected="false">GAF GO Term</option>
+            <option value="gaf_id" selected="false">GAF ID</option>
+            <option value="gaf_notes" selected="false">GAF Notes</option>
+            <option value="gaf_owner" selected="false">GAF Owner</option>
+            <option value="gaf_with_or_from" selected="false">GAF with_or_from</option>
+        </param>
+        <param name="checkSubfeats" label="Search sub-features for fields if gene does not define them" type="boolean" truevalue="--searchSubs" falsevalue=""/>
+        <param label="Report Format" type="select" name="report_format">
+            <option value="phageqc_report_annotation_table.html" selected="true">HTML Table</option>
+            <option value="phageqc_report_annotation_table.tsv">Tabular (Excel Compatible) Table</option>
+        </param>
+    </inputs>
+    <outputs>
+        <data format="html" name="output">
+            <change_format>
+                <when format="tabular" input="report_format" value="phageqc_report_annotation_table.tsv"/>
+            </change_format>
+        </data>
+    </outputs>
+    <tests>
+        <test>
+            <param name="reference_genome_source" value="history"/>
+            <param name="genome_fasta" value="NC_001416_Table_In.fasta"/>
+            <param name="gff3_data" value="NC_001416_Table_In.gff3"/>
+            <param name="report_format" value="phageqc_report_annotation_table.html"/>
+            <output name="output" file="PhageTable_Out.html"/>
+        </test>
+        <test>
+            <param name="reference_genome_source" value="history"/>
+            <param name="genome_fasta" value="NC_001416_Table_In.fasta"/>
+            <param name="gff3_data" value="NC_001416_Table_In.gff3"/>
+            <param name="report_format" value="phageqc_report_annotation_table.tsv"/>
+            <output name="output" file="PhageTable_Out.tabular"/>
+        </test>
+    </tests>
+    <help><![CDATA[
+Generate an "annotation table" of a genome, with user-configurable data columns
+]]></help>
+    <expand macro="citations"/>
+</tool>
b
diff -r 6a4d1bd8ac1d -r 32e011fa615c phageqc_report_annotation_table.html
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/phageqc_report_annotation_table.html Wed Apr 26 03:42:32 2023 +0000
[
@@ -0,0 +1,244 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="utf-8">
+    <meta http-equiv="X-UA-Compatible" content="IE=edge">
+    <meta name="viewport" content="width=device-width, initial-scale=1">
+    <!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags -->
+    <meta name="description" content="">
+    <meta name="author" content="">
+    <title>Annotation Table</title>
+    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.2/jquery.min.js"></script>
+ <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" crossorigin="anonymous">
+ <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap-theme.min.css" crossorigin="anonymous">
+ <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js" crossorigin="anonymous"></script>
+
+    <link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.10.11/css/jquery.dataTables.css">
+    <script type="text/javascript" charset="utf8" src="https://cdn.datatables.net/1.10.11/js/jquery.dataTables.js"></script>
+    <style type="text/css">
+/*
+ * Base structure
+ */
+
+/* Move down content because we have a fixed navbar that is 50px tall */
+body {
+  padding-top: 50px;
+}
+
+h3:before {
+  display: block;
+  content: " ";
+  margin-top: -50px;
+  height: 50px;
+  visibility: hidden;
+}
+
+/*
+ * Global add-ons
+ */
+
+.sub-header {
+  padding-bottom: 10px;
+  border-bottom: 1px solid #eee;
+}
+
+/*
+ * Top navigation
+ * Hide default border to remove 1px line.
+ */
+.navbar-fixed-top {
+  border: 0;
+}
+
+/*
+ * Sidebar
+ */
+
+/* Hide for mobile, show later */
+.sidebar {
+  display: none;
+}
+@media (min-width: 768px) {
+  .sidebar {
+    position: fixed;
+    top: 51px;
+    bottom: 0;
+    left: 0;
+    z-index: 1000;
+    display: block;
+    padding: 20px;
+    overflow-x: hidden;
+    overflow-y: auto; /* Scrollable contents if viewport is shorter than content. */
+    background-color: #f5f5f5;
+    border-right: 1px solid #eee;
+  }
+}
+
+/* Sidebar navigation */
+.nav-sidebar {
+  margin-right: -21px; /* 20px padding + 1px border */
+  margin-bottom: 20px;
+  margin-left: -20px;
+}
+.nav-sidebar > li > a {
+  padding-right: 20px;
+  padding-left: 20px;
+}
+.nav-sidebar > .active > a,
+.nav-sidebar > .active > a:hover,
+.nav-sidebar > .active > a:focus {
+  color: #fff;
+  background-color: #428bca;
+}
+
+
+/*
+ * Main content
+ */
+
+.main {
+  padding: 20px;
+}
+@media (min-width: 768px) {
+  .main {
+    padding-right: 40px;
+    padding-left: 40px;
+  }
+}
+.main .page-header {
+  margin-top: 0;
+}
+
+
+/*
+ * Placeholder dashboard ideas
+ */
+
+.placeholders {
+  margin-bottom: 30px;
+  text-align: center;
+}
+.placeholders h4 {
+  margin-bottom: 0;
+}
+.placeholder {
+  margin-bottom: 20px;
+}
+.placeholder img {
+  display: inline-block;
+  border-radius: 50%;
+}
+
+td.moron {
+    font-size: 150%;
+    padding: 0px;
+    color: gray;
+}
+.strand_emph {
+    text-decoration: underline;
+    color: black;
+}
+
+    </style>
+    <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
+    <!--[if lt IE 9]>
+      <script src="//oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
+      <script src="//oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
+    <![endif]-->
+  </head>
+  <body>
+
+    <nav class="navbar navbar-inverse navbar-fixed-top">
+      <div class="container-fluid">
+        <div class="navbar-header">
+            <a class="navbar-brand" href="#">Annotation Table</a>
+        </div>
+      </div>
+    </nav>
+
+    <div class="container-fluid">
+      <div class="row">
+        <div class="col-md-12 main" id="main">
+            <ul class="nav nav-tabs" role="tablist" id="myTabs">
+                <li role="presentation" class="active"><a href="#overview" role="tab" data-toggle="tab">Overview</a></li>
+                {% for (record, data) in annotation_table_data %}
+                <li role="presentation"><a href="#{{ record.id }}" role="tab" data-toggle="tab">{{ record.id }}</a></li>
+                {% endfor %}
+            </ul>
+
+            <!-- Tab panes -->
+            <div class="tab-content">
+                <div role="tabpanel" class="tab-pane active" id="overview">
+                    Data on each organism will be accessible from the tabs above.
+                </div>
+                {% for (record, data) in annotation_table_data %}
+                    <div role="tabpanel" class="tab-pane" id="{{ record.id }}">
+                        <table class="table table-striped" id="data">
+                            <thead>
+                                <tr>
+                                {% for col in annotation_table_col_names %}
+                                    <th>{{ col[0] }}</th>
+                                {% endfor %}
+                                </tr>
+                            </thead>
+                            <tbody>
+                                {% for row in data %}
+                                    <tr>
+                                    {% for col in row %}
+                                    <td>{% if col is not string %}<ul>{% for val in col %}<li>{{ val }}</li>{% endfor %}</ul>{% else %}{{ col }}{% endif %}</td>
+                                    {% endfor %}
+                                    </tr>
+                                {% endfor %}
+                            </tbody>
+                        </table>
+                    </div>
+                {% endfor %}
+            </div>
+        </div>
+      </div>
+    </div>
+
+    <script type="text/javascript">
+        $(document).ready( function () {
+
+
+
+$('#myTabs a').click(function (e) {
+ e.preventDefault()
+ $(this).tab('show')
+})
+
+
+
+jQuery.fn.dataTable.ext.type.detect.unshift( function ( data ) {
+    if ( typeof data !== 'string' ) {
+        return null;
+    }
+
+    var matches = data.match(/^(\d+)\.\.(\d+)/);
+    return matches ? 'genomic' : null;
+} );
+
+
+
+jQuery.extend( jQuery.fn.dataTableExt.oSort, {
+    "genomic-pre": function ( a ) {
+        var matches = a.match(/^(\d+)\.\.(\d+)/);
+        console.log(a + " " + matches[1]);
+        return parseInt(matches[1]);
+    },
+
+    "genomic-asc": function ( a, b ) {
+        return ((a < b) ? -1 : ((a > b) ? 1 : 0));
+    },
+
+    "genomic-desc": function ( a, b ) {
+        return ((a < b) ? 1 : ((a > b) ? -1 : 0));
+    }
+} );
+
+            $('table').DataTable();
+        });
+    </script>
+  </body>
+</html>
b
diff -r 6a4d1bd8ac1d -r 32e011fa615c phageqc_report_annotation_table.tsv
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/phageqc_report_annotation_table.tsv Wed Apr 26 03:42:32 2023 +0000
[
@@ -0,0 +1,8 @@
+{% for (record, dataset) in annotation_table_data %}
+# {% for col in annotation_table_col_names %}{{ col[0] }} {% endfor %}
+{% for row in dataset %}
+
+{% for col in row %}
+{% if col is not string %}"{{ col | join }}"{% else %}{{ col }}{% endif %} {% endfor %}
+{% endfor %}
+{% endfor %}
b
diff -r 6a4d1bd8ac1d -r 32e011fa615c test-data/NC_001416_Table_In.fasta
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test-data/NC_001416_Table_In.fasta Wed Apr 26 03:42:32 2023 +0000
b
b'@@ -0,0 +1,810 @@\n+>NC_001416.1_phage_lambda\n+GGGCGGCGACCTCGCGGGTTTTCGCTATTTATGAAAATTTTCCGGTTTAAGGCGTTTCCG\n+TTCTTCTTCGTCATAACTTAATGTTTTTATTTAAAATACCCTCTGAAAAGAAAGGAAACG\n+ACAGGTGCTGAAAGCGAGGCTTTTTGGCCTCTGTCGTTTCCTTTCTCTGTTTTTGTCCGT\n+GGAATGAACAATGGAAGTCAACAAAAAGCAGCTGGCTGACATTTTCGGTGCGAGTATCCG\n+TACCATTCAGAACTGGCAGGAACAGGGAATGCCCGTTCTGCGAGGCGGTGGCAAGGGTAA\n+TGAGGTGCTTTATGACTCTGCCGCCGTCATAAAATGGTATGCCGAAAGGGATGCTGAAAT\n+TGAGAACGAAAAGCTGCGCCGGGAGGTTGAAGAACTGCGGCAGGCCAGCGAGGCAGATCT\n+CCAGCCAGGAACTATTGAGTACGAACGCCATCGACTTACGCGTGCGCAGGCCGACGCACA\n+GGAACTGAAGAATGCCAGAGACTCCGCTGAAGTGGTGGAAACCGCATTCTGTACTTTCGT\n+GCTGTCGCGGATCGCAGGTGAAATTGCCAGTATTCTCGACGGGCTCCCCCTGTCGGTGCA\n+GCGGCGTTTTCCGGAACTGGAAAACCGACATGTTGATTTCCTGAAACGGGATATCATCAA\n+AGCCATGAACAAAGCAGCCGCGCTGGATGAACTGATACCGGGGTTGCTGAGTGAATATAT\n+CGAACAGTCAGGTTAACAGGCTGCGGCATTTTGTCCGCGCCGGGCTTCGCTCACTGTTCA\n+GGCCGGAGCCACAGACCGCCGTTGAATGGGCGGATGCTAATTACTATCTCCCGAAAGAAT\n+CCGCATACCAGGAAGGGCGCTGGGAAACACTGCCCTTTCAGCGGGCCATCATGAATGCGA\n+TGGGCAGCGACTACATCCGTGAGGTGAATGTGGTGAAGTCTGCCCGTGTCGGTTATTCCA\n+AAATGCTGCTGGGTGTTTATGCCTACTTTATAGAGCATAAGCAGCGCAACACCCTTATCT\n+GGTTGCCGACGGATGGTGATGCCGAGAACTTTATGAAAACCCACGTTGAGCCGACTATTC\n+GTGATATTCCGTCGCTGCTGGCGCTGGCCCCGTGGTATGGCAAAAAGCACCGGGATAACA\n+CGCTCACCATGAAGCGTTTCACTAATGGGCGTGGCTTCTGGTGCCTGGGCGGTAAAGCGG\n+CAAAAAACTACCGTGAAAAGTCGGTGGATGTGGCGGGTTATGATGAACTTGCTGCTTTTG\n+ATGATGATATTGAACAGGAAGGCTCTCCGACGTTCCTGGGTGACAAGCGTATTGAAGGCT\n+CGGTCTGGCCAAAGTCCATCCGTGGCTCCACGCCAAAAGTGAGAGGCACCTGTCAGATTG\n+AGCGTGCAGCCAGTGAATCCCCGCATTTTATGCGTTTTCATGTTGCCTGCCCGCATTGCG\n+GGGAGGAGCAGTATCTTAAATTTGGCGACAAAGAGACGCCGTTTGGCCTCAAATGGACGC\n+CGGATGACCCCTCCAGCGTGTTTTATCTCTGCGAGCATAATGCCTGCGTCATCCGCCAGC\n+AGGAGCTGGACTTTACTGATGCCCGTTATATCTGCGAAAAGACCGGGATCTGGACCCGTG\n+ATGGCATTCTCTGGTTTTCGTCATCCGGTGAAGAGATTGAGCCACCTGACAGTGTGACCT\n+TTCACATCTGGACAGCGTACAGCCCGTTCACCACCTGGGTGCAGATTGTCAAAGACTGGA\n+TGAAAACGAAAGGGGATACGGGAAAACGTAAAACCTTCGTAAACACCACGCTCGGTGAGA\n+CGTGGGAGGCGAAAATTGGCGAACGTCCGGATGCTGAAGTGATGGCAGAGCGGAAAGAGC\n+ATTATTCAGCGCCCGTTCCTGACCGTGTGGCTTACCTGACCGCCGGTATCGACTCCCAGC\n+TGGACCGCTACGAAATGCGCGTATGGGGATGGGGGCCGGGTGAGGAAAGCTGGCTGATTG\n+ACCGGCAGATTATTATGGGCCGCCACGACGATGAACAGACGCTGCTGCGTGTGGATGAGG\n+CCATCAATAAAACCTATACCCGCCGGAATGGTGCAGAAATGTCGATATCCCGTATCTGCT\n+GGGATACTGGCGGGATTGACCCGACCATTGTGTATGAACGCTCGAAAAAACATGGGCTGT\n+TCCGGGTGATCCCCATTAAAGGGGCATCCGTCTACGGAAAGCCGGTGGCCAGCATGCCAC\n+GTAAGCGAAACAAAAACGGGGTTTACCTTACCGAAATCGGTACGGATACCGCGAAAGAGC\n+AGATTTATAACCGCTTCACACTGACGCCGGAAGGGGATGAACCGCTTCCCGGTGCCGTTC\n+ACTTCCCGAATAACCCGGATATTTTTGATCTGACCGAAGCGCAGCAGCTGACTGCTGAAG\n+AGCAGGTCGAAAAATGGGTGGATGGCAGGAAAAAAATACTGTGGGACAGCAAAAAGCGAC\n+GCAATGAGGCACTCGACTGCTTCGTTTATGCGCTGGCGGCGCTGCGCATCAGTATTTCCC\n+GCTGGCAGCTGGATCTCAGTGCGCTGCTGGCGAGCCTGCAGGAAGAGGATGGTGCAGCAA\n+CCAACAAGAAAACACTGGCAGATTACGCCCGTGCCTTATCCGGAGAGGATGAATGACGCG\n+ACAGGAAGAACTTGCCGCTGCCCGTGCGGCACTGCATGACCTGATGACAGGTAAACGGGT\n+GGCAACAGTACAGAAAGACGGACGAAGGGTGGAGTTTACGGCCACTTCCGTGTCTGACCT\n+GAAAAAATATATTGCAGAGCTGGAAGTGCAGACCGGCATGACACAGCGACGCAGGGGACC\n+TGCAGGATTTTATGTATGAAAACGCCCACCATTCCCACCCTTCTGGGGCCGGACGGCATG\n+ACATCGCTGCGCGAATATGCCGGTTATCACGGCGGTGGCAGCGGATTTGGAGGGCAGTTG\n+CGGTCGTGGAACCCACCGAGTGAAAGTGTGGATGCAGCCCTGTTGCCCAACTTTACCCGT\n+GGCAATGCCCGCGCAGACGATCTGGTACGCAATAACGGCTATGCCGCCAACGCCATCCAG\n+CTGCATCAGGATCATATCGTCGGGTCTTTTTTCCGGCTCAGTCATCGCCCAAGCTGGCGC\n+TATCTGGGCATCGGGGAGGAAGAAGCCCGTGCCTTTTCCCGCGAGGTTGAAGCGGCATGG\n+AAAGAGTTTGCCGAGGATGACTGCTGCTGCATTGACGTTGAGCGAAAACGCACGTTTACC\n+ATGATGATTCGGGAAGGTGTGGCCATGCACGCCTTTAACGGTGAACTGTTCGTTCAGGCC\n+ACCTGGGATACCAGTTCGTCGCGGCTTTTCCGGACACAGTTCCGGATGGTCAGCCCGAAG\n+CGCATCAGCAACCCGAACAATACCGGCGACAGCCGGAACTGCCGTGCCGGTGTGCAGATT\n+AATGACAGCGGTGCGGCGCTGGGATATTACGTCAGCGAGGACGGGTATCCTGGCTGGATG\n+CCGCAGAAATGGACATGGATACCCCGTGAGTTACCCGGCGGGCGCGCCTCGTTCATTCAC\n+GTTTTTGAACCCGTGGAGGACGGGCAGACTCGCGGTGCAAATGTGTTTTACAGCGTGATG\n+GAGCAGATGAAGATGCTCGACACGCTGCAGAACACGCAGCTGCAGAGCGCCATTGTGAAG\n+GCGATGTATGCCGCCACCATTGAGAGTGAGCTGGATACGCAGTCAGCGATGGATTTTATT\n+CTGGGCGCGAACAGTCAGGAGCAGCGGGAAAGGCTGACCGGCTGGATTGGTGAAATTGCC\n+GCGTATTACGCCGCAGCGCCGGTCCGGCTGGGAGGCGCAAAAGTACC'..b'CGCTTA\n+CTACCGATTCCGCCTAGTTGGTCACTTCGACGTATCGTCTGGAACTCCAACCATCGCAGG\n+CAGAGAGGTCTGCAAAATGCAATCCCGAAACAGTTCGCAGGTAATAGTTAGAGCCTGCAT\n+AACGGTTTCGGGATTTTTTATATCTGCACAACAGGTAAGAGCATTGAGTCGATAATCGTG\n+AAGAGTCGGCGAGCCTGGTTAGCCAGTGCTCTTTCCGTTGTGCTGAATTAAGCGAATACC\n+GGAAGCAGAACCGGATCACCAAATGCGTACAGGCGTCATCGCCGCCCAGCAACAGCACAA\n+CCCAAACTGAGCCGTAGCCACTGTCTGTCCTGAATTCATTAGTAATAGTTACGCTGCGGC\n+CTTTTACACATGACCTTCGTGAAAGCGGGTGGCAGGAGGTCGCGCTAACAACCTCCTGCC\n+GTTTTGCCCGTGCATATCGGTCACGAACAAATCTGATTACTAAACACAGTAGCCTGGATT\n+TGTTCTATCAGTAATCGACCTTATTCCTAATTAAATAGAGCAAATCCCCTTATTGGGGGT\n+AAGACATGAAGATGCCAGAAAAACATGACCTGTTGGCCGCCATTCTCGCGGCAAAGGAAC\n+AAGGCATCGGGGCAATCCTTGCGTTTGCAATGGCGTACCTTCGCGGCAGATATAATGGCG\n+GTGCGTTTACAAAAACAGTAATCGACGCAACGATGTGCGCCATTATCGCCTGGTTCATTC\n+GTGACCTTCTCGACTTCGCCGGACTAAGTAGCAATCTCGCTTATATAACGAGCGTGTTTA\n+TCGGCTACATCGGTACTGACTCGATTGGTTCGCTTATCAAACGCTTCGCTGCTAAAAAAG\n+CCGGAGTAGAAGATGGTAGAAATCAATAATCAACGTAAGGCGTTCCTCGATATGCTGGCG\n+TGGTCGGAGGGAACTGATAACGGACGTCAGAAAACCAGAAATCATGGTTATGACGTCATT\n+GTAGGCGGAGAGCTATTTACTGATTACTCCGATCACCCTCGCAAACTTGTCACGCTAAAC\n+CCAAAACTCAAATCAACAGGCGCCGGACGCTACCAGCTTCTTTCCCGTTGGTGGGATGCC\n+TACCGCAAGCAGCTTGGCCTGAAAGACTTCTCTCCGAAAAGTCAGGACGCTGTGGCATTG\n+CAGCAGATTAAGGAGCGTGGCGCTTTACCTATGATTGATCGTGGTGATATCCGTCAGGCA\n+ATCGACCGTTGCAGCAATATCTGGGCTTCACTGCCGGGCGCTGGTTATGGTCAGTTCGAG\n+CATAAGGCTGACAGCCTGATTGCAAAATTCAAAGAAGCGGGCGGAACGGTCAGAGAGATT\n+GATGTATGAGCAGAGTCACCGCGATTATCTCCGCTCTGGTTATCTGCATCATCGTCTGCC\n+TGTCATGGGCTGTTAATCATTACCGTGATAACGCCATTACCTACAAAGCCCAGCGCGACA\n+AAAATGCCAGAGAACTGAAGCTGGCGAACGCGGCAATTACTGACATGCAGATGCGTCAGC\n+GTGATGTTGCTGCGCTCGATGCAAAATACACGAAGGAGTTAGCTGATGCTAAAGCTGAAA\n+ATGATGCTCTGCGTGATGATGTTGCCGCTGGTCGTCGTCGGTTGCACATCAAAGCAGTCT\n+GTCAGTCAGTGCGTGAAGCCACCACCGCCTCCGGCGTGGATAATGCAGCCTCCCCCCGAC\n+TGGCAGACACCGCTGAACGGGATTATTTCACCCTCAGAGAGAGGCTGATCACTATGCAAA\n+AACAACTGGAAGGAACCCAGAAGTATATTAATGAGCAGTGCAGATAGAGTTGCCCATATC\n+GATGGGCAACTCATGCAATTATTGTGAGCAATACACACGCGCTTCCAGCGGAGTATAAAT\n+GCCTAAAGTAATAAAACCGAGCAATCCATTTACGAATGTTTGCTGGGTTTCTGTTTTAAC\n+AACATTTTCTGCGCCGCCACAAATTTTGGCTGCATCGACAGTTTTCTTCTGCCCAATTCC\n+AGAAACGAAGAAATGATGGGTGATGGTTTCCTTTGGTGCTACTGCTGCCGGTTTGTTTTG\n+AACAGTAAACGTCTGTTGAGCACATCCTGTAATAAGCAGGGCCAGCGCAGTAGCGAGTAG\n+CATTTTTTTCATGGTGTTATTCCCGATGCTTTTTGAAGTTCGCAGAATCGTATGTGTAGA\n+AAATTAAACAAACCCTAAACAATGAGTTGAAATTTCATATTGTTAATATTTATTAATGTA\n+TGTCAGGTGCGATGAATCGTCATTGTATTCCCGGATTAACTATGTCCACAGCCCTGACGG\n+GGAACTTCTCTGCGGGAGTGTCCGGGAATAATTAAAACGATGCACACAGGGTTTAGCGCG\n+TACACGTATTGCATTATGCCAACGCCCCGGTGCTGACACGGAAGAAACCGGACGTTATGA\n+TTTAGCGTGGAAAGATTTGTGTAGTGTTCTGAATGCTCTCAGTAAATAGTAATGAATTAT\n+CAAAGGTATAGTAATATCTTTTATGTTCATGGATATTTGTAACCCATCGGAAAACTCCTG\n+CTTTAGCAAGATTTTCCCTGTATTGCTGAAATGTGATTTCTCTTGATTTCAACCTATCAT\n+AGGACGTTTCTATAAGATGCGTGTTTCTTGAGAATTTAACATTTACAACCTTTTTAAGTC\n+CTTTTATTAACACGGTGTTATCGTTTTCTAACACGATGTGAATATTATCTGTGGCTAGAT\n+AGTAAATATAATGTGAGACGTTGTGACGTTTTAGTTCAGAATAAAACAATTCACAGTCTA\n+AATCTTTTCGCACTTGATCGAATATTTCTTTAAAAATGGCAACCTGAGCCATTGGTAAAA\n+CCTTCCATGTGATACGAGGGCGCGTAGTTTGCATTATCGTTTTTATCGTTTCAATCTGGT\n+CTGACCTCCTTGTGTTTTGTTGATGATTTATGTCAAATATTAGGAATGTTTTCACTTAAT\n+AGTATTGGTTGCGTAACAAAGTGCGGTCCTGCTGGCATTCTGGAGGGAAATACAACCGAC\n+AGATGTATGTAAGGCCAACGTGCTCAAATCTTCATACAGAAAGATTTGAAGTAATATTTT\n+AACCGCTAGATGAAGAGCAAGCGCATGGAGCGACAAAATGAATAAAGAACAATCTGCTGA\n+TGATCCCTCCGTGGATCTGATTCGTGTAAAAAATATGCTTAATAGCACCATTTCTATGAG\n+TTACCCTGATGTTGTAATTGCATGTATAGAACATAAGGTGTCTCTGGAAGCATTCAGAGC\n+AATTGAGGCAGCGTTGGTGAAGCACGATAATAATATGAAGGATTATTCCCTGGTGGTTGA\n+CTGATCACCATAACTGCTAATCATTCAAACTATTTAGTCTGTGACAGAGCCAACACGCAG\n+TCTGTCACTGTCAGGAAAGTGGTAAAACTGCAACTCAATTACTGCAATGCCCTCGTAATT\n+AAGTGAATTTACAATATCGTCCTGTTCGGAGGGAAGAACGCGGGATGTTCATTCTTCATC\n+ACTTTTAATTGATGTATATGCTCTCTTTTCTGACGTTAGTCTCCGACGGCAGGCTTCAAT\n+GACCCAGGCTGAGAAATTCCCGGACCCTTTTTGCTCAAGAGCGATGTTAATTTGTTCAAT\n+CATTTGGTTAGGAAAGCGGATGTTGCGGGTTGTTGTTCTGCGGGTTCTGTTCTTCGTTGA\n+CATGAGGTTGCCCCGTATTCAGTGTCGCTGATTTGTATTGTCTGAAGTTGTTTTTACGTT\n+AAGTTGATGCAGATCAATTAATACGATACCTGCGTCATAATTGATTATTTGACGTGGTTT\n+GATGGCCTCCACGCACGTTGTGATATGTAGATGATAATCATTATCACTTTACGGGTCCTT\n+TCCGGTGATCCGACAGGTTACG\n'
b
diff -r 6a4d1bd8ac1d -r 32e011fa615c test-data/NC_001416_Table_In.gff3
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test-data/NC_001416_Table_In.gff3 Wed Apr 26 03:42:32 2023 +0000
b
b'@@ -0,0 +1,476 @@\n+##gff-version 3\n+##sequence-region NC_001416.1_phage_lambda 1 48502\n+NC_001416.1_phage_lambda\t.\tgene\t14261\t14875\t.\t+\t.\towner=jolenerr@tamu.edu;ID=cdb58372-2440-4e07-bcc4-739e0aba461f;date_last_modified=2020-05-27;Name=tail tip assembly protein K;date_creation=2020-05-13\n+NC_001416.1_phage_lambda\t.\tmRNA\t14261\t14875\t.\t+\t.\towner=jolenerr@tamu.edu;Parent=cdb58372-2440-4e07-bcc4-739e0aba461f;ID=c53587ff-12d9-4a3a-a7ac-e87e54c5232b;date_last_modified=2020-05-27;Name=tail tip assembly protein K;date_creation=2020-05-13\n+NC_001416.1_phage_lambda\t.\tCDS\t14276\t14875\t.\t+\t0\tParent=c53587ff-12d9-4a3a-a7ac-e87e54c5232b;ID=2d4171c3-93b5-4c30-afba-9a0969dd4a6f;Name=2d4171c3-93b5-4c30-afba-9a0969dd4a6f\n+NC_001416.1_phage_lambda\t.\texon\t14276\t14875\t.\t+\t.\tParent=c53587ff-12d9-4a3a-a7ac-e87e54c5232b;ID=782a319b-3988-4cb2-aedf-47242382005c;Name=782a319b-3988-4cb2-aedf-47242382005c\n+NC_001416.1_phage_lambda\t.\texon\t14261\t14265\t.\t+\t.\tParent=c53587ff-12d9-4a3a-a7ac-e87e54c5232b;ID=709a9728-db34-4010-bfc3-41b0687edb80;Name=709a9728-db34-4010-bfc3-41b0687edb80\n+###\n+NC_001416.1_phage_lambda\t.\tgene\t47727\t47944\t.\t+\t.\towner=jolenerr@tamu.edu;ID=e3d29177-18a8-4bc0-8d91-15e5760f3bd3;date_last_modified=2020-05-13;Name=NC_001416.1_phage_lambda.gene_60;date_creation=2020-05-13\n+NC_001416.1_phage_lambda\t.\tmRNA\t47727\t47944\t.\t+\t.\towner=jolenerr@tamu.edu;Parent=e3d29177-18a8-4bc0-8d91-15e5760f3bd3;ID=90e550aa-f463-46d2-bec4-87aa38e63ee2;date_last_modified=2020-05-13;Name=NC_001416.1_phage_lambda.gene_60-00001;date_creation=2020-05-13\n+NC_001416.1_phage_lambda\t.\tCDS\t47738\t47944\t.\t+\t0\tParent=90e550aa-f463-46d2-bec4-87aa38e63ee2;ID=586909d5-f3a6-44bb-8e9e-5a5230b2127a;Name=586909d5-f3a6-44bb-8e9e-5a5230b2127a\n+NC_001416.1_phage_lambda\t.\texon\t47727\t47730\t.\t+\t.\tParent=90e550aa-f463-46d2-bec4-87aa38e63ee2;ID=bf5e1837-0469-4372-9652-7677fee29843;Name=bf5e1837-0469-4372-9652-7677fee29843\n+NC_001416.1_phage_lambda\t.\texon\t47738\t47944\t.\t+\t.\tParent=90e550aa-f463-46d2-bec4-87aa38e63ee2;ID=14232a86-ebfe-4f10-aa28-2910207542f7;Name=14232a86-ebfe-4f10-aa28-2910207542f7\n+###\n+NC_001416.1_phage_lambda\t.\tgene\t13416\t14127\t.\t+\t.\towner=jolenerr@tamu.edu;ID=11a10b01-003c-4900-aae9-46139ae23652;date_last_modified=2020-05-27;Name=tail tip protein L;date_creation=2020-05-13\n+NC_001416.1_phage_lambda\t.\tmRNA\t13416\t14127\t.\t+\t.\towner=jolenerr@tamu.edu;Parent=11a10b01-003c-4900-aae9-46139ae23652;ID=07cc76d7-d0f4-447f-a851-3aeaee1b71ac;date_last_modified=2020-05-27;Name=tail tip protein L;date_creation=2020-05-13\n+NC_001416.1_phage_lambda\t.\tCDS\t13429\t14127\t.\t+\t0\tParent=07cc76d7-d0f4-447f-a851-3aeaee1b71ac;ID=37a2e1ad-d49c-43d3-b33b-727c848e2a38;Name=37a2e1ad-d49c-43d3-b33b-727c848e2a38\n+NC_001416.1_phage_lambda\t.\texon\t13429\t14127\t.\t+\t.\tParent=07cc76d7-d0f4-447f-a851-3aeaee1b71ac;ID=008fa66b-e5b3-489b-8d99-208103afde8f;Name=008fa66b-e5b3-489b-8d99-208103afde8f\n+NC_001416.1_phage_lambda\t.\texon\t13416\t13419\t.\t+\t.\tParent=07cc76d7-d0f4-447f-a851-3aeaee1b71ac;ID=0a4ad3d9-21dc-4fab-badb-a94e4493c397;Name=0a4ad3d9-21dc-4fab-badb-a94e4493c397\n+###\n+NC_001416.1_phage_lambda\t.\tgene\t19641\t20855\t.\t+\t.\towner=jolenerr@tamu.edu;ID=e7267f7d-4717-4e28-a1a0-564b10661248;date_last_modified=2020-05-27;Name=tail fiber protein stf;date_creation=2020-05-13\n+NC_001416.1_phage_lambda\t.\tmRNA\t19641\t20855\t.\t+\t.\towner=jolenerr@tamu.edu;Parent=e7267f7d-4717-4e28-a1a0-564b10661248;ID=b7a5dc48-88c5-4edc-9081-091ef39ff3ea;date_last_modified=2020-05-27;Name=tail fiber protein stf;date_creation=2020-05-13\n+NC_001416.1_phage_lambda\t.\texon\t19641\t19644\t.\t+\t.\tParent=b7a5dc48-88c5-4edc-9081-091ef39ff3ea;ID=e794f054-733e-4b10-99df-128b37cd80e8;Name=e794f054-733e-4b10-99df-128b37cd80e8\n+NC_001416.1_phage_lambda\t.\tCDS\t19650\t20855\t.\t+\t0\tParent=b7a5dc48-88c5-4edc-9081-091ef39ff3ea;ID=f1584714-9cc6-4209-8730-0d7c199cff35;Name=f1584714-9cc6-4209-8730-0d7c199cff35\n+NC_001416.1_phage_lambda\t.\texon\t19650\t20855\t.\t+\t.\tParent=b7a5dc48-88c5-4edc-9081-091ef39ff3ea;ID=b44a883e-e4e2-4432-b665-355a803943dc;Name=b44a883e-e4e2-4432-b665-35'..b'.\tgene\t38031\t38241\t.\t+\t.\towner=jason.gill@tamu.edu;Note=added by JJG;ID=d731b2bf-e9f9-484f-8851-814510c6a5ec;date_last_modified=2020-05-27;Name=regulatory protein cro;date_creation=2019-02-07\n+NC_001416.1_phage_lambda\t.\tmRNA\t38031\t38241\t.\t+\t.\towner=jason.gill@tamu.edu;Parent=d731b2bf-e9f9-484f-8851-814510c6a5ec;ID=5cfd706b-b814-43f9-b604-fc8c179f853a;date_last_modified=2020-05-27;Name=regulatory protein cro;date_creation=2019-02-07\n+NC_001416.1_phage_lambda\t.\texon\t38031\t38035\t.\t+\t.\tParent=5cfd706b-b814-43f9-b604-fc8c179f853a;ID=041be549-96a8-42a3-b5eb-258ebb4ffd5f;Name=041be549-96a8-42a3-b5eb-258ebb4ffd5f\n+NC_001416.1_phage_lambda\t.\tCDS\t38041\t38241\t.\t+\t0\tParent=5cfd706b-b814-43f9-b604-fc8c179f853a;ID=97688d3e-13d8-48fd-9e05-2d919293f914;Name=97688d3e-13d8-48fd-9e05-2d919293f914\n+NC_001416.1_phage_lambda\t.\texon\t38041\t38241\t.\t+\t.\tParent=5cfd706b-b814-43f9-b604-fc8c179f853a;ID=e6686b84-108a-4c8f-a5b9-64b92b6dc5b5;Name=e6686b84-108a-4c8f-a5b9-64b92b6dc5b5\n+###\n+NC_001416.1_phage_lambda\t.\tgene\t38345\t38653\t.\t+\t.\towner=jason.gill@tamu.edu;Note=added by JJG;ID=1b331cf3-0897-4489-aa55-b14669ea5998;date_last_modified=2020-05-27;Name=regulatory protein cII;date_creation=2019-02-07\n+NC_001416.1_phage_lambda\t.\tmRNA\t38345\t38653\t.\t+\t.\towner=jason.gill@tamu.edu;Parent=1b331cf3-0897-4489-aa55-b14669ea5998;ID=b970dd3f-9787-459c-84ed-5b839b85b9e7;date_last_modified=2020-05-27;Name=regulatory protein cII;date_creation=2019-02-07\n+NC_001416.1_phage_lambda\t.\tCDS\t38360\t38653\t.\t+\t0\tParent=b970dd3f-9787-459c-84ed-5b839b85b9e7;ID=c4ae3e6a-4e56-412d-8ece-0036053910ef;Name=c4ae3e6a-4e56-412d-8ece-0036053910ef\n+NC_001416.1_phage_lambda\t.\texon\t38360\t38653\t.\t+\t.\tParent=b970dd3f-9787-459c-84ed-5b839b85b9e7;ID=c29fe89e-348d-4da7-b313-85c362f92307;Name=c29fe89e-348d-4da7-b313-85c362f92307\n+NC_001416.1_phage_lambda\t.\texon\t38345\t38348\t.\t+\t.\tParent=b970dd3f-9787-459c-84ed-5b839b85b9e7;ID=b2199555-1cbb-443c-92d3-fa5a0a17670e;Name=b2199555-1cbb-443c-92d3-fa5a0a17670e\n+###\n+NC_001416.1_phage_lambda\t.\tgene\t30392\t30626\t.\t-\t.\towner=jolenerr@tamu.edu;Note=Contains InterPro domain IPR000962;ID=4fba3f9c-cbc0-4454-b278-efe088695fef;date_last_modified=2020-05-27;Name=DksA/TraR C4-type zinc finger domain-containing protein;date_creation=2020-05-13\n+NC_001416.1_phage_lambda\t.\tmRNA\t30392\t30626\t.\t-\t.\towner=jolenerr@tamu.edu;Parent=4fba3f9c-cbc0-4454-b278-efe088695fef;ID=7dfc2c2d-a50d-4e77-b673-2dcd16c2ef3b;date_last_modified=2020-05-27;Name=DksA/TraR C4-type zinc finger domain-containing protein;date_creation=2020-05-13\n+NC_001416.1_phage_lambda\t.\texon\t30624\t30626\t.\t-\t.\tParent=7dfc2c2d-a50d-4e77-b673-2dcd16c2ef3b;ID=cbf0bbe9-fdae-426f-a6ce-40926606c1ca;Name=cbf0bbe9-fdae-426f-a6ce-40926606c1ca\n+NC_001416.1_phage_lambda\t.\tCDS\t30392\t30613\t.\t-\t0\tParent=7dfc2c2d-a50d-4e77-b673-2dcd16c2ef3b;ID=1fa7e4a7-9200-45d9-b9fd-21e36b254897;Name=1fa7e4a7-9200-45d9-b9fd-21e36b254897\n+NC_001416.1_phage_lambda\t.\texon\t30392\t30613\t.\t-\t.\tParent=7dfc2c2d-a50d-4e77-b673-2dcd16c2ef3b;ID=529064a4-a072-48ec-bd67-0eb3011ae53c;Name=529064a4-a072-48ec-bd67-0eb3011ae53c\n+###\n+NC_001416.1_phage_lambda\t.\tgene\t46174\t46368\t.\t+\t.\towner=jolenerr@tamu.edu;ID=18e578e1-38cb-4846-96e8-0190a5a26369;date_last_modified=2020-05-13;Name=o-spanin Rz1;date_creation=2020-05-13\n+NC_001416.1_phage_lambda\t.\tmRNA\t46174\t46368\t.\t+\t.\towner=jolenerr@tamu.edu;Parent=18e578e1-38cb-4846-96e8-0190a5a26369;ID=b4c199b2-1838-43c0-a09d-77718a49dbe2;date_last_modified=2020-05-13;Name=o-spanin Rz1;date_creation=2020-05-13\n+NC_001416.1_phage_lambda\t.\tCDS\t46186\t46368\t.\t+\t0\tParent=b4c199b2-1838-43c0-a09d-77718a49dbe2;ID=7ac7bb1c-0d6d-4476-8a02-8ad963da6156;Name=7ac7bb1c-0d6d-4476-8a02-8ad963da6156\n+NC_001416.1_phage_lambda\t.\texon\t46174\t46178\t.\t+\t.\tParent=b4c199b2-1838-43c0-a09d-77718a49dbe2;ID=668e24d1-fcea-459e-bbf3-6132bbb9854e;Name=668e24d1-fcea-459e-bbf3-6132bbb9854e\n+NC_001416.1_phage_lambda\t.\texon\t46186\t46368\t.\t+\t.\tParent=b4c199b2-1838-43c0-a09d-77718a49dbe2;ID=f8d07a76-6f8d-4100-9ba6-cec0e3a033f9;Name=f8d07a76-6f8d-4100-9ba6-cec0e3a033f9\n'
b
diff -r 6a4d1bd8ac1d -r 32e011fa615c test-data/PhageTable_Out.html
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test-data/PhageTable_Out.html Wed Apr 26 03:42:32 2023 +0000
[
b'@@ -0,0 +1,2124 @@\n+<!DOCTYPE html>\n+<html lang="en">\n+  <head>\n+    <meta charset="utf-8">\n+    <meta http-equiv="X-UA-Compatible" content="IE=edge">\n+    <meta name="viewport" content="width=device-width, initial-scale=1">\n+    <!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags -->\n+    <meta name="description" content="">\n+    <meta name="author" content="">\n+    <title>Annotation Table</title>\n+    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.2/jquery.min.js"></script>\n+\t<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" crossorigin="anonymous">\n+\t<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap-theme.min.css" crossorigin="anonymous">\n+\t<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js" crossorigin="anonymous"></script>\n+\n+    <link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.10.11/css/jquery.dataTables.css">\n+    <script type="text/javascript" charset="utf8" src="https://cdn.datatables.net/1.10.11/js/jquery.dataTables.js"></script>\n+    <style type="text/css">\n+/*\n+ * Base structure\n+ */\n+\n+/* Move down content because we have a fixed navbar that is 50px tall */\n+body {\n+  padding-top: 50px;\n+}\n+\n+h3:before {\n+  display: block;\n+  content: " ";\n+  margin-top: -50px;\n+  height: 50px;\n+  visibility: hidden;\n+}\n+\n+/*\n+ * Global add-ons\n+ */\n+\n+.sub-header {\n+  padding-bottom: 10px;\n+  border-bottom: 1px solid #eee;\n+}\n+\n+/*\n+ * Top navigation\n+ * Hide default border to remove 1px line.\n+ */\n+.navbar-fixed-top {\n+  border: 0;\n+}\n+\n+/*\n+ * Sidebar\n+ */\n+\n+/* Hide for mobile, show later */\n+.sidebar {\n+  display: none;\n+}\n+@media (min-width: 768px) {\n+  .sidebar {\n+    position: fixed;\n+    top: 51px;\n+    bottom: 0;\n+    left: 0;\n+    z-index: 1000;\n+    display: block;\n+    padding: 20px;\n+    overflow-x: hidden;\n+    overflow-y: auto; /* Scrollable contents if viewport is shorter than content. */\n+    background-color: #f5f5f5;\n+    border-right: 1px solid #eee;\n+  }\n+}\n+\n+/* Sidebar navigation */\n+.nav-sidebar {\n+  margin-right: -21px; /* 20px padding + 1px border */\n+  margin-bottom: 20px;\n+  margin-left: -20px;\n+}\n+.nav-sidebar > li > a {\n+  padding-right: 20px;\n+  padding-left: 20px;\n+}\n+.nav-sidebar > .active > a,\n+.nav-sidebar > .active > a:hover,\n+.nav-sidebar > .active > a:focus {\n+  color: #fff;\n+  background-color: #428bca;\n+}\n+\n+\n+/*\n+ * Main content\n+ */\n+\n+.main {\n+  padding: 20px;\n+}\n+@media (min-width: 768px) {\n+  .main {\n+    padding-right: 40px;\n+    padding-left: 40px;\n+  }\n+}\n+.main .page-header {\n+  margin-top: 0;\n+}\n+\n+\n+/*\n+ * Placeholder dashboard ideas\n+ */\n+\n+.placeholders {\n+  margin-bottom: 30px;\n+  text-align: center;\n+}\n+.placeholders h4 {\n+  margin-bottom: 0;\n+}\n+.placeholder {\n+  margin-bottom: 20px;\n+}\n+.placeholder img {\n+  display: inline-block;\n+  border-radius: 50%;\n+}\n+\n+td.moron {\n+    font-size: 150%;\n+    padding: 0px;\n+    color: gray;\n+}\n+.strand_emph {\n+    text-decoration: underline;\n+    color: black;\n+}\n+\n+    </style>\n+    <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->\n+    <!--[if lt IE 9]>\n+      <script src="//oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>\n+      <script src="//oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>\n+    <![endif]-->\n+  </head>\n+  <body>\n+\n+    <nav class="navbar navbar-inverse navbar-fixed-top">\n+      <div class="container-fluid">\n+        <div class="navbar-header">\n+            <a class="navbar-brand" href="#">Annotation Table</a>\n+        </div>\n+      </div>\n+    </nav>\n+\n+    <div class="container-fluid">\n+      <div class="row">\n+        <div class="col-md-12 main" id="main">\n+            <ul class="nav nav-tabs" role="tablist" id="myTabs">\n+                <li role="presentation" class="active"><a href="#overview" role="tab" data-toggle="tab">Overview</a></li>\n+  '..b'                                  <td>None</td>\n+                                    <td>None</td>\n+                                    <td>None</td>\n+                                    <td>None</td>\n+                                    </tr>\n+                                    <tr>\n+                                    <td>NC_001416-1_phage_lambda</td>\n+                                    <td>e3d29177-18a8-4bc0-8d91-15e5760f3bd3</td>\n+                                    <td>gene</td>\n+                                    <td>NC_001416.1_phage_lambda.gene_60</td>\n+                                    <td>47727..47944</td>\n+                                    <td>47727</td>\n+                                    <td>47944</td>\n+                                    <td>+</td>\n+                                    <td>68</td>\n+                                    <td>GGAG</td>\n+                                    <td>7</td>\n+                                    <td>ATG</td>\n+                                    <td>TGA</td>\n+                                    <td>74</td>\n+                                    <td>752f31a0-018a-4223-a7f9-72685604b7a3</td>\n+                                    <td>jolenerr@tamu.edu</td>\n+                                    <td>2020-05-13</td>\n+                                    <td>2020-05-13</td>\n+                                    <td>None</td>\n+                                    <td>None</td>\n+                                    <td>None</td>\n+                                    <td>None</td>\n+                                    </tr>\n+                                    <tr>\n+                                    <td>NC_001416-1_phage_lambda</td>\n+                                    <td>e0b009e4-729e-4d97-b818-ec0cbebcb1f4</td>\n+                                    <td>gene</td>\n+                                    <td>NC_001416.1_phage_lambda.orf00102</td>\n+                                    <td>48109..48314</td>\n+                                    <td>48109</td>\n+                                    <td>48314</td>\n+                                    <td>-</td>\n+                                    <td>64</td>\n+                                    <td>GGGG</td>\n+                                    <td>7</td>\n+                                    <td>ATG</td>\n+                                    <td>TGA</td>\n+                                    <td>None</td>\n+                                    <td>None</td>\n+                                    <td>jolenerr@tamu.edu</td>\n+                                    <td>2020-05-13</td>\n+                                    <td>2020-05-13</td>\n+                                    <td>None</td>\n+                                    <td>None</td>\n+                                    <td>None</td>\n+                                    <td>None</td>\n+                                    </tr>\n+                            </tbody>\n+                        </table>\n+                    </div>\n+            </div>\n+        </div>\n+      </div>\n+    </div>\n+\n+    <script type="text/javascript">\n+        $(document).ready( function () {\n+\n+\n+\n+$(\'#myTabs a\').click(function (e) {\n+\te.preventDefault()\n+\t$(this).tab(\'show\')\n+})\n+\n+\n+\n+jQuery.fn.dataTable.ext.type.detect.unshift( function ( data ) {\n+    if ( typeof data !== \'string\' ) {\n+        return null;\n+    }\n+\n+    var matches = data.match(/^(\\d+)\\.\\.(\\d+)/);\n+    return matches ? \'genomic\' : null;\n+} );\n+\n+\n+\n+jQuery.extend( jQuery.fn.dataTableExt.oSort, {\n+    "genomic-pre": function ( a ) {\n+        var matches = a.match(/^(\\d+)\\.\\.(\\d+)/);\n+        console.log(a + " " + matches[1]);\n+        return parseInt(matches[1]);\n+    },\n+\n+    "genomic-asc": function ( a, b ) {\n+        return ((a < b) ? -1 : ((a > b) ? 1 : 0));\n+    },\n+\n+    "genomic-desc": function ( a, b ) {\n+        return ((a < b) ? 1 : ((a > b) ? -1 : 0));\n+    }\n+} );\n+\n+            $(\'table\').DataTable();\n+        });\n+    </script>\n+  </body>\n+</html>\n'
b
diff -r 6a4d1bd8ac1d -r 32e011fa615c test-data/PhageTable_Out.tabular
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test-data/PhageTable_Out.tabular Wed Apr 26 03:42:32 2023 +0000
b
b'@@ -0,0 +1,79 @@\n+# Organism ID\tID\tType\tName\tLocation\tBoundary\tBoundary\tStrand\tCDS Length (AA)\tShine-Dalgarno sequence\tShine-Dalgarno spacing\tStart Codon\tStop Codon\tDistance to next upstream gene on same strand\tNext gene upstream\tOwner\tCreated\tLast Modified\tUser entered Notes\tDescription\tProduct\tDBxrefs\t\n+NC_001416.1_phage_lambda\t1185c0aa-14c4-45c3-ab0d-889bbfdbf137\tgene\tterminase small subunit\t181..736\t181\t736\t+\t181\tGGA\t7\tATG\tTAA\tNone\tNone\tjolenerr@tamu.edu\t2020-05-13\t2020-05-27\tNone\tNone\tNone\tNone\t\n+NC_001416.1_phage_lambda\t6d452f5a-9508-40c1-9d43-d98831ca8919\tgene\tterminase large subunit\t701..2636\t701\t2636\t+\t641\tGGGT\t6\tGTG\tTGA\t-36\t1185c0aa-14c4-45c3-ab0d-889bbfdbf137\tjolenerr@tamu.edu\t2020-05-13\t2020-05-27\tNone\tNone\tNone\tNone\t\n+NC_001416.1_phage_lambda\t38175e36-b9e9-48ea-9d1c-7f115fae7194\tgene\thead completion protein\t2622..2839\t2622\t2839\t+\t68\tGGAG\t7\tATG\tTGA\t-15\t6d452f5a-9508-40c1-9d43-d98831ca8919\tjolenerr@tamu.edu\t2020-05-13\t2020-05-28\tInterPro domain IPR004174\tNone\tNone\tNone\t\n+NC_001416.1_phage_lambda\t0695da41-cce8-4f07-a27a-eb42ac82ae56\tgene\tportal protein B\t2824..4437\t2824\t4437\t+\t533\tAGGA\t8\tATG\tTAA\t-16\t38175e36-b9e9-48ea-9d1c-7f115fae7194\tjolenerr@tamu.edu\t2020-05-13\t2020-05-27\tNone\tNone\tNone\tNone\t\n+NC_001416.1_phage_lambda\t422aab41-ac49-4818-b87e-9331023dcacf\tgene\tcapsid assembly protein\t4406..5737\t4406\t5737\t+\t673\t"GGAG, AGGAGG"\t"-609, 6"\t"GTG (4417..5737:1), GTG (5035..5737:1)"\tTAA\t-32\t0695da41-cce8-4f07-a27a-eb42ac82ae56\tjolenerr@tamu.edu\t2020-05-13\t2020-05-27\tNone\tNone\tNone\tNone\t\n+NC_001416.1_phage_lambda\t274304d3-079e-4e08-aa11-219235e1aacb\tgene\tcapsid decoration protein\t5739..6079\t5739\t6079\t+\t110\tGGA\t5\tATG\tTAA\t1\t422aab41-ac49-4818-b87e-9331023dcacf\tjolenerr@tamu.edu\t2020-05-13\t2020-05-28\tNone\tNone\tNone\tNone\t\n+NC_001416.1_phage_lambda\t4f815f9e-5092-4db3-b722-0d912c116293\tgene\tmajor capsid protein\t6099..7160\t6099\t7160\t+\t350\tAGG\t6\tGTG\tTAA\t19\t274304d3-079e-4e08-aa11-219235e1aacb\tjolenerr@tamu.edu\t2020-05-13\t2020-05-27\tNone\tNone\tNone\tNone\t\n+NC_001416.1_phage_lambda\t4da662d1-028a-43e2-9b50-f48ad03bdd64\tgene\tDNA packaging protein FI\t7173..7600\t7173\t7600\t+\t136\tGGGG\t13\tGTG\tTAA\t12\t4f815f9e-5092-4db3-b722-0d912c116293\tjolenerr@tamu.edu\t2020-05-13\t2020-05-27\tNone\tNone\tNone\tNone\t\n+NC_001416.1_phage_lambda\t638d1ac5-aea4-4988-b46d-01cf79793dfb\tgene\thead-tail connector protein FII\t7603..7965\t7603\t7965\t+\t117\tGGAG\t5\tGTG\tTGA\t2\t4da662d1-028a-43e2-9b50-f48ad03bdd64\tjolenerr@tamu.edu\t2020-05-13\t2020-05-27\tNone\tNone\tNone\tNone\t\n+NC_001416.1_phage_lambda\tb541056e-bf91-4113-bce7-86bab4d27f76\tgene\ttail completion protein Z\t7968..8555\t7968\t8555\t+\t192\tGGGG\t5\tATG\tTGA\t2\t638d1ac5-aea4-4988-b46d-01cf79793dfb\tjolenerr@tamu.edu\t2020-05-13\t2020-05-27\tNone\tNone\tNone\tNone\t\n+NC_001416.1_phage_lambda\tfca8745c-c433-4845-9430-c8a9dd4dc37b\tgene\ttail tube terminator protein U\t8535..8947\t8535\t8947\t+\t131\tAGGA\t13\tATG\tTGA\t-21\tb541056e-bf91-4113-bce7-86bab4d27f76\tjolenerr@tamu.edu\t2020-05-13\t2020-05-27\tNone\tNone\tNone\tNone\t\n+NC_001416.1_phage_lambda\td9ee7b0e-b4a7-48cf-8c4b-aa95ca8863e0\tgene\ttail tube protein V\t8946..9695\t8946\t9695\t+\t246\tGAGG\t5\tATG\tTAA\t-2\tfca8745c-c433-4845-9430-c8a9dd4dc37b\tjolenerr@tamu.edu\t2020-05-13\t2020-05-27\tNone\tNone\tNone\tNone\t\n+NC_001416.1_phage_lambda\t0b5a859a-39f6-4c49-91b7-0b2def466374\tgene\ttape measure chaperone GT\t9699..10133\t9699\t10133\t+\t275\t"GGAG, GGAG"\t"8, 8"\t"ATG (9710..10133:1), ATG (9710..10115:1)"\tTGA\t3\td9ee7b0e-b4a7-48cf-8c4b-aa95ca8863e0\tjolenerr@tamu.edu\t2020-05-13\t2020-05-27\tNone\tNone\tNone\tNone\t\n+NC_001416.1_phage_lambda\tc6a11d3b-7b9d-446f-ba16-3a642f3f6fbd\tgene\ttape measure chaperone GT\t10115..10549\t10115\t10549\t+\t144\tNone\tNone\tGTG\tTGA\t-19\t0b5a859a-39f6-4c49-91b7-0b2def466374\tjolenerr@tamu.edu\t2020-05-13\t2020-05-27\tNone\tNone\tNone\tNone\t\n+NC_001416.1_phage_lambda\t7ef9ffad-d420-4a1c-9a8d-2312dc04b558\tgene\ttape measure protein H\t10528..13103\t10528\t13103\t+\t853\tAGGAGG\t8\tATG\tTGA\t-22\tc6a11d3b-7b9d-446f-ba16-3a642f3f6fbd\tjolenerr@tamu.edu\t2020-05-13\t2020-05-27\tNone\tNone\tNone\tNone\t\n+NC_001416.1_phage_lambda\t55fe46da-9e0e-4454-aff9-f'..b'002363577\t42080..42272\t42080\t42272\t+\t60\tAGGAG\t5\tGTG\tTGA\t-44\tc402b694-7971-451b-aa24-4e70550eef2d\tjolenerr@tamu.edu\t2020-05-13\t2020-05-13\tNone\tNone\tNone\tNone\t\n+NC_001416.1_phage_lambda\t49135134-fa86-4df5-a0d7-b629ff152305\tgene\tCDS.0.1596_0.0419290277112\t42230..42439\t42230\t42439\t+\t66\tGGA\t6\tGTG\tTAA\t-43\t61a0d8bd-5b58-4077-bc04-2bcf4dee8939\tjolenerr@tamu.edu\t2020-05-13\t2020-05-13\tNone\tNone\tNone\tNone\t\n+NC_001416.1_phage_lambda\ta8c3d9d2-cc66-4394-a821-ec0db2e411de\tgene\tNC_001416.1_phage_lambda.gene_51\t42419..43043\t42419\t43043\t+\t204\tGAGG\t6\tATG\tTGA\t-21\t49135134-fa86-4df5-a0d7-b629ff152305\tjolenerr@tamu.edu\t2020-05-13\t2020-05-13\tNone\tNone\tNone\tNone\t\n+NC_001416.1_phage_lambda\t0cea2875-2757-4c9b-97f5-7b0ec545394a\tgene\tNC_001416.1_phage_lambda.gene_52\t43032..43246\t43032\t43246\t+\t68\tGAG\t5\tATG\tTGA\t-12\ta8c3d9d2-cc66-4394-a821-ec0db2e411de\tjolenerr@tamu.edu\t2020-05-13\t2020-05-13\tNone\tNone\tNone\tNone\t\n+NC_001416.1_phage_lambda\t8ce6427a-594a-4320-955b-be6dd71ab223\tgene\tNC_001416.1_phage_lambda.gene_53\t43135..43889\t43135\t43889\t+\t221\tGGAG\t8\tATG\tTGA\t-112\t0cea2875-2757-4c9b-97f5-7b0ec545394a\tjolenerr@tamu.edu\t2020-05-13\t2020-05-13\tNone\tNone\tNone\tNone\t\n+NC_001416.1_phage_lambda\t1653554c-4be1-453b-a377-86e20759321a\tgene\tlate gene regulator\t43875..44509\t43875\t44509\t+\t207\tGGAG\t7\tATG\tTAG\t-15\t8ce6427a-594a-4320-955b-be6dd71ab223\tjolenerr@tamu.edu\t2020-05-13\t2020-05-27\tNone\tNone\tNone\tNone\t\n+NC_001416.1_phage_lambda\t6c3ab9c5-a407-47b8-89df-fa997262a409\tgene\tCDS.0.1630_0.132755174165\t44612..44815\t44612\t44815\t+\t64\tGGT\t6\tATG\tTAA\t102\t1653554c-4be1-453b-a377-86e20759321a\tjolenerr@tamu.edu\t2020-05-13\t2020-05-13\tNone\tNone\tNone\tNone\t\n+NC_001416.1_phage_lambda\t94064736-88a6-4e82-8c89-8226f57526e8\tgene\tantiholin S107\t45175..45509\t45175\t45509\t+\t213\t"GGGGG, GGGGG"\t"6, 6"\t"ATG (45185..45509:1), ATG (45191..45509:1)"\tTAA\t359\t6c3ab9c5-a407-47b8-89df-fa997262a409\tjolenerr@tamu.edu\t2020-05-13\t2020-05-13\tNone\tNone\tNone\tNone\t\n+NC_001416.1_phage_lambda\tf8ba23df-ca21-4307-8f5a-ef8243f01acc\tgene\tendolysin R\t45483..45969\t45483\t45969\t+\t158\tGGAG\t6\tATG\tTGA\t-27\t94064736-88a6-4e82-8c89-8226f57526e8\tjolenerr@tamu.edu\t2020-05-13\t2020-05-13\tNone\tNone\tNone\tNone\t\n+NC_001416.1_phage_lambda\t4795342c-7959-44d9-925a-781f17371e7a\tgene\ti-spanin Rz\t45953..46427\t45953\t46427\t+\t153\tGAG\t10\tATG\tTAG\t-17\tf8ba23df-ca21-4307-8f5a-ef8243f01acc\tjolenerr@tamu.edu\t2020-05-13\t2020-05-13\tNone\tNone\tNone\tNone\t\n+NC_001416.1_phage_lambda\t18e578e1-38cb-4846-96e8-0190a5a26369\tgene\to-spanin Rz1\t46174..46368\t46174\t46368\t+\t60\tAGGAG\t7\tATG\tTGA\t-254\t4795342c-7959-44d9-925a-781f17371e7a\tjolenerr@tamu.edu\t2020-05-13\t2020-05-13\tNone\tNone\tNone\tNone\t\n+NC_001416.1_phage_lambda\t6eedd5c5-3c44-404a-af31-acee26522035\tgene\tbor\t46459..46763\t46459\t46763\t-\t97\tGGA\t8\tATG\tTAA\t132\t2da1b434-7877-4aa2-b88b-24922f70988b\tjolenerr@tamu.edu\t2020-05-13\t2020-05-13\tNone\tNone\tNone\tNone\t\n+NC_001416.1_phage_lambda\t2da1b434-7877-4aa2-b88b-24922f70988b\tgene\tCDS.0.3497_0.36888415839\t46896..47029\t46896\t47029\t-\t32\tGGGG\t10\tATG\tTAA\t12\t5d4e2403-4389-497c-a220-9740cd9935cc\tjolenerr@tamu.edu\t2020-05-13\t2020-05-13\tNone\tNone\tNone\tNone\t\n+NC_001416.1_phage_lambda\t5d4e2403-4389-497c-a220-9740cd9935cc\tgene\tCDS.0.2625_0.768191840841\t47042..47463\t47042\t47463\t-\t136\tAGGT\t7\tATG\tTAA\t645\te0b009e4-729e-4d97-b818-ec0cbebcb1f4\tjolenerr@tamu.edu\t2020-05-13\t2020-05-13\tNone\tNone\tNone\tNone\t\n+NC_001416.1_phage_lambda\t752f31a0-018a-4223-a7f9-72685604b7a3\tgene\tCDS.0.864_0.667986446525\t47518..47652\t47518\t47652\t+\t40\tGGT\t9\tTTG\tTAA\t1149\t18e578e1-38cb-4846-96e8-0190a5a26369\tjolenerr@tamu.edu\t2020-05-13\t2020-05-13\tNone\tNone\tNone\tNone\t\n+NC_001416.1_phage_lambda\te3d29177-18a8-4bc0-8d91-15e5760f3bd3\tgene\tNC_001416.1_phage_lambda.gene_60\t47727..47944\t47727\t47944\t+\t68\tGGAG\t7\tATG\tTGA\t74\t752f31a0-018a-4223-a7f9-72685604b7a3\tjolenerr@tamu.edu\t2020-05-13\t2020-05-13\tNone\tNone\tNone\tNone\t\n+NC_001416.1_phage_lambda\te0b009e4-729e-4d97-b818-ec0cbebcb1f4\tgene\tNC_001416.1_phage_lambda.orf00102\t48109..48314\t48109\t48314\t-\t64\tGGGG\t7\tATG\tTGA\tNone\tNone\tjolenerr@tamu.edu\t2020-05-13\t2020-05-13\tNone\tNone\tNone\tNone\t\n'