Separate Genome atlas and Data bank into styled tabs
Browse files- FEATURE_BACKLOG.md +5 -1
- README.md +1 -1
- app.css +26 -0
- app.py +54 -48
- taxonomy.py +2 -2
FEATURE_BACKLOG.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
| 2 |
|
| 3 |
Track desired features that are not yet implemented. Add new ideas here and update each item's status as work progresses. These entries describe future work; implementation details and priorities remain open unless explicitly agreed.
|
| 4 |
|
| 5 |
-
Last updated: 2026-
|
| 6 |
|
| 7 |
## Overview
|
| 8 |
|
|
@@ -143,3 +143,7 @@ Desired behavior:
|
|
| 143 |
## Adding and updating entries
|
| 144 |
|
| 145 |
Give each new feature a stable ID, a goal, a status, desired behavior, and open decisions or dependencies. Link implementation work when it starts. Once shipped, mark the entry implemented and record any remaining scope rather than deleting its history.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2 |
|
| 3 |
Track desired features that are not yet implemented. Add new ideas here and update each item's status as work progresses. These entries describe future work; implementation details and priorities remain open unless explicitly agreed.
|
| 4 |
|
| 5 |
+
Last updated: 2026-10-05.
|
| 6 |
|
| 7 |
## Overview
|
| 8 |
|
|
|
|
| 143 |
## Adding and updating entries
|
| 144 |
|
| 145 |
Give each new feature a stable ID, a goal, a status, desired behavior, and open decisions or dependencies. Link implementation work when it starts. Once shipped, mark the entry implemented and record any remaining scope rather than deleting its history.
|
| 146 |
+
|
| 147 |
+
## Atlas research sections
|
| 148 |
+
|
| 149 |
+
Planned: add RefSeq annotation exploration and wet-lab results beneath the eukaryotic tree in the **Genome atlas** tab. Decide on the comparison views and experimental summaries when those data are ready. Accession lookup, segment viewing, and downloads now live in the separate **Data bank** tab.
|
README.md
CHANGED
|
@@ -17,7 +17,7 @@ Future features and open design decisions are tracked in the [feature backlog](F
|
|
| 17 |
|
| 18 |
## GenBank taxonomy
|
| 19 |
|
| 20 |
-
The landing page opens with an interactive **tree of eukaryotic life**, using NCBI's current GenBank assembly summary and taxonomy. Curved branches and nodes are colored by the fraction of assemblies with published annotations: gray for none found in the inventory, increasingly dark teal for higher percentages. Counts and percentages appear beside the groups, with details on hover.
|
| 21 |
|
| 22 |
The viewer is restricted to **Eukaryota (NCBI taxid 2759) and its descendants**, including search, breadcrumbs, and parent navigation. The model predicts eukaryotic annotations; bacteria, archaea, and viruses are outside this display. The current eukaryotic snapshot contains **70,395 assemblies**, of which **17,561 (24.9%)** have published annotations. The underlying SQLite snapshot retains the full NCBI inventory for refresh/provenance, while the interface uses only the eukaryotic subtree and its denominators. Assemblies with unresolved taxonomy cannot be assigned to that subtree and are excluded.
|
| 23 |
|
|
|
|
| 17 |
|
| 18 |
## GenBank taxonomy
|
| 19 |
|
| 20 |
+
The landing page opens with an interactive **tree of eukaryotic life**, using NCBI's current GenBank assembly summary and taxonomy. Curved branches and nodes are colored by the fraction of assemblies with published annotations: gray for none found in the inventory, increasingly dark teal for higher percentages. Counts and percentages appear beside the groups, with details on hover. The default **Genome atlas** tab contains the tree and its lineage controls. A separate **Data bank** tab holds accession search, segment visualization, and downloads, using the existing smaller index. This leaves the atlas view available for future RefSeq exploration and wet-lab results.
|
| 21 |
|
| 22 |
The viewer is restricted to **Eukaryota (NCBI taxid 2759) and its descendants**, including search, breadcrumbs, and parent navigation. The model predicts eukaryotic annotations; bacteria, archaea, and viruses are outside this display. The current eukaryotic snapshot contains **70,395 assemblies**, of which **17,561 (24.9%)** have published annotations. The underlying SQLite snapshot retains the full NCBI inventory for refresh/provenance, while the interface uses only the eukaryotic subtree and its denominators. Assemblies with unresolved taxonomy cannot be assigned to that subtree and are excluded.
|
| 23 |
|
app.css
CHANGED
|
@@ -35,3 +35,29 @@
|
|
| 35 |
.gradio-container .action-button { width:100%; }
|
| 36 |
}
|
| 37 |
@media(prefers-reduced-motion:reduce) { .gradio-container .atlas-panel button { transition:none; } }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 35 |
.gradio-container .action-button { width:100%; }
|
| 36 |
}
|
| 37 |
@media(prefers-reduced-motion:reduce) { .gradio-container .atlas-panel button { transition:none; } }
|
| 38 |
+
|
| 39 |
+
/* Two distinct views, with the same quiet atlas palette. */
|
| 40 |
+
#atlas-navigation { border:0; background:transparent; }
|
| 41 |
+
#atlas-navigation > .tab-wrapper > [role="tablist"] { gap:8px; border-bottom:1px solid #dbe3d3; padding:0 0 12px; margin-bottom:16px; }
|
| 42 |
+
#atlas-navigation > .tab-wrapper > [role="tablist"] > button { border:1px solid transparent; border-radius:999px; padding:10px 20px; color:#647467; font:500 14px/1.4 Arial,Helvetica,sans-serif; background:transparent; }
|
| 43 |
+
#atlas-navigation > .tab-wrapper > [role="tablist"] > button:hover { background:#edf3e7; color:#173c30; }
|
| 44 |
+
#atlas-navigation > .tab-wrapper > [role="tablist"] > button[aria-selected="true"] { color:#fffefa; background:#245d45; border-color:#245d45; }
|
| 45 |
+
#atlas-overview, #atlas-data-bank { border:0; background:transparent; padding:0; }
|
| 46 |
+
.data-bank-heading { padding:14px 4px 12px; }
|
| 47 |
+
.data-bank-heading .data-bank-eyebrow { color:#647467; font:11px/1.4 Arial,Helvetica,sans-serif; letter-spacing:2px; margin:0 0 12px; }
|
| 48 |
+
.data-bank-heading h1 { color:#173c30; font:400 clamp(32px,5vw,48px)/1.1 Georgia,'Times New Roman',serif; letter-spacing:-1px; margin:0 0 12px; }
|
| 49 |
+
.data-bank-heading p { color:#647467; font:14px/1.6 Arial,Helvetica,sans-serif; margin:0; }
|
| 50 |
+
@media(max-width:700px) {
|
| 51 |
+
#atlas-navigation > .tab-wrapper > [role="tablist"] { gap:6px; }
|
| 52 |
+
#atlas-navigation > .tab-wrapper > [role="tablist"] > button { padding:10px 16px; }
|
| 53 |
+
}
|
| 54 |
+
|
| 55 |
+
#atlas-navigation > .tab-wrapper { border-bottom:0; }
|
| 56 |
+
#atlas-navigation > .tab-wrapper > .tab-container > button { padding:10px 20px; font:500 14px/1.4 Arial,Helvetica,sans-serif; border-width:1px; }
|
| 57 |
+
@media(max-width:700px) { #atlas-navigation > .tab-wrapper > .tab-container > button { padding:10px 16px; } }
|
| 58 |
+
/* Prevent a loaded Plotly chart from widening the page after a viewport resize. */
|
| 59 |
+
.gradio-container { width:100%!important; min-width:0!important; box-sizing:border-box; }
|
| 60 |
+
#atlas-navigation, #atlas-overview, #atlas-data-bank { min-width:0; max-width:100%; }
|
| 61 |
+
#atlas-navigation > .tab-wrapper > .tab-container > button { min-height:44px; height:auto; box-sizing:border-box; }
|
| 62 |
+
#atlas-navigation > .tab-wrapper { height:auto; min-height:64px; }
|
| 63 |
+
#atlas-navigation > .tab-wrapper > .tab-container { height:auto; min-height:56px; align-items:center; }
|
app.py
CHANGED
|
@@ -127,54 +127,60 @@ def build_app(catalog=None):
|
|
| 127 |
return str(target)
|
| 128 |
|
| 129 |
with gr.Blocks(title="GenBank Annotation Explorer", delete_cache=(3600, 3600)) as demo:
|
| 130 |
-
|
| 131 |
-
|
| 132 |
-
|
| 133 |
-
gr.
|
| 134 |
-
|
| 135 |
-
|
| 136 |
-
|
| 137 |
-
|
| 138 |
-
|
| 139 |
-
|
| 140 |
-
|
| 141 |
-
|
| 142 |
-
|
| 143 |
-
|
| 144 |
-
|
| 145 |
-
|
| 146 |
-
|
| 147 |
-
|
| 148 |
-
|
| 149 |
-
|
| 150 |
-
|
| 151 |
-
|
| 152 |
-
|
| 153 |
-
|
| 154 |
-
|
| 155 |
-
|
| 156 |
-
|
| 157 |
-
|
| 158 |
-
|
| 159 |
-
|
| 160 |
-
|
| 161 |
-
|
| 162 |
-
|
| 163 |
-
|
| 164 |
-
|
| 165 |
-
|
| 166 |
-
|
| 167 |
-
|
| 168 |
-
|
| 169 |
-
|
| 170 |
-
|
| 171 |
-
|
| 172 |
-
|
| 173 |
-
|
| 174 |
-
|
| 175 |
-
|
| 176 |
-
|
| 177 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 178 |
gr.HTML('<footer class="workspace-footer"><span>Hugging Face Bio · Genome Atlas</span>'
|
| 179 |
'<a href="https://huggingface.co/buckets/HuggingFaceBio/genbank-annotations" target="_blank" rel="noopener noreferrer">Explore the annotation collection ↗</a></footer>', apply_default_css=False)
|
| 180 |
outputs = [status, results, segment, file]
|
|
|
|
| 127 |
return str(target)
|
| 128 |
|
| 129 |
with gr.Blocks(title="GenBank Annotation Explorer", delete_cache=(3600, 3600)) as demo:
|
| 130 |
+
with gr.Tabs(selected="atlas", elem_id="atlas-navigation"):
|
| 131 |
+
with gr.Tab("Genome atlas", id="atlas", elem_id="atlas-overview"):
|
| 132 |
+
build_taxonomy_tab()
|
| 133 |
+
with gr.Tab("Data bank", id="data-bank", elem_id="atlas-data-bank"):
|
| 134 |
+
gr.HTML('<header class="data-bank-heading"><p class="data-bank-eyebrow">THE ANNOTATION COLLECTION</p>'
|
| 135 |
+
'<h1>Explore the data bank</h1><p>Find an accession, explore its coding landscape, and download the annotations.</p></header>',
|
| 136 |
+
apply_default_css=False)
|
| 137 |
+
with gr.Column(elem_classes="atlas-panel"):
|
| 138 |
+
gr.HTML(section_header("01", "Find an accession", "Start with an assembly or contig to explore its predicted coding regions."), apply_default_css=False)
|
| 139 |
+
gr.Markdown(f"**{len(catalog.records):,} indexed segments** · **{len(catalog.manifest['assemblies']):,} assemblies** · "
|
| 140 |
+
f"{catalog.manifest['bases']:,} bases\n\n"
|
| 141 |
+
f"Search covers this prototype {scope}. Results are model predictions and assembly coverage may be partial.",
|
| 142 |
+
elem_classes=["quiet-note", "scope-note"])
|
| 143 |
+
with gr.Row():
|
| 144 |
+
accession = gr.Textbox(label="Accession ID", placeholder="Assembly (GCA_…) or contig accession", scale=5)
|
| 145 |
+
search_button = gr.Button("Find annotations", variant="primary", scale=1, elem_classes="action-button")
|
| 146 |
+
examples = [first["assembly_accession"], first["record_name"]]
|
| 147 |
+
if remote_mode:
|
| 148 |
+
with closing(catalog.connect()) as conn:
|
| 149 |
+
examples += [r[0] for r in conn.execute("SELECT record_name FROM segments WHERE id IN (SELECT min(id) FROM segments GROUP BY object_path)")]
|
| 150 |
+
examples += ["JBPJTW010000350.1"]
|
| 151 |
+
gr.Examples(examples=[[e] for e in dict.fromkeys(examples)], inputs=accession, label="Try an accession", elem_id="annotation-examples")
|
| 152 |
+
status = gr.Markdown("Enter an accession or select an example. IDs are case-insensitive; version suffixes are optional.", elem_classes="quiet-note")
|
| 153 |
+
results = gr.Dataframe(value=catalog.table([]), interactive=False, label="Matching segments", elem_id="annotation-results")
|
| 154 |
+
segment = gr.Dropdown(choices=[], label="Segment to explore", interactive=True)
|
| 155 |
+
|
| 156 |
+
with gr.Column(elem_classes="atlas-panel"):
|
| 157 |
+
gr.HTML(section_header("02", "Explore the coding landscape", "View CDS probabilities or apply a threshold to see one label per base."), apply_default_css=False)
|
| 158 |
+
with gr.Row():
|
| 159 |
+
start = gr.Number(label="Start (0-based, inclusive)", precision=0)
|
| 160 |
+
end = gr.Number(label="End (exclusive)", precision=0)
|
| 161 |
+
view = gr.Button("Update region", elem_classes="action-button")
|
| 162 |
+
with gr.Row():
|
| 163 |
+
mode = gr.Radio(["Probabilities", "Binary labels"], value="Probabilities", label="Viewer mode")
|
| 164 |
+
threshold = gr.Slider(0, 1, value=0.5, step=0.01, label="CDS threshold (max strand P > threshold)")
|
| 165 |
+
plot = gr.Plot(label="CDS tracks", elem_id="annotation-plot")
|
| 166 |
+
note = gr.Markdown("Choose a segment above to bring its coding landscape into view.", elem_classes="quiet-note")
|
| 167 |
+
with gr.Accordion("Segment metadata and provenance", open=False, elem_classes="atlas-disclosure"):
|
| 168 |
+
metadata = gr.JSON(label="Source metadata")
|
| 169 |
+
|
| 170 |
+
with gr.Column(elem_classes="atlas-panel"):
|
| 171 |
+
gr.HTML(section_header("03", "Take the annotations with you", "Download the original segment, with its accession and record name in the filename."), apply_default_css=False)
|
| 172 |
+
with gr.Row():
|
| 173 |
+
download = gr.Button("Prepare segment download", variant="primary", scale=1, elem_classes="action-button")
|
| 174 |
+
file = gr.File(label="Original segment annotations (Parquet)", interactive=False, scale=3)
|
| 175 |
+
gr.Markdown("Full per-base probabilities are preserved in the download, including when the viewer shows a binned overview.", elem_classes="quiet-note")
|
| 176 |
+
with gr.Accordion("Browse the index and its sources", open=False, elem_classes="atlas-disclosure"):
|
| 177 |
+
gr.Markdown(f"Showing the first {len(all_ids):,} indexed segments. Search an accession to find other indexed records. "
|
| 178 |
+
"An indexed file does not imply complete coverage of its assembly.\n\n"
|
| 179 |
+
"Source: [HuggingFaceBio/genbank-annotations](https://huggingface.co/buckets/HuggingFaceBio/genbank-annotations). "
|
| 180 |
+
+ (f"Annotations load on demand from {len(catalog.manifest['sources'])} bucket files. "
|
| 181 |
+
f"Index updated {catalog.manifest['created_at'][:10]}." if remote_mode else "Offline sample."), elem_classes="quiet-note")
|
| 182 |
+
gr.Dataframe(value=catalog.table(all_ids), interactive=False)
|
| 183 |
+
gr.JSON(value=catalog.manifest, label="Index provenance" if remote_mode else "Sample provenance")
|
| 184 |
gr.HTML('<footer class="workspace-footer"><span>Hugging Face Bio · Genome Atlas</span>'
|
| 185 |
'<a href="https://huggingface.co/buckets/HuggingFaceBio/genbank-annotations" target="_blank" rel="noopener noreferrer">Explore the annotation collection ↗</a></footer>', apply_default_css=False)
|
| 186 |
outputs = [status, results, segment, file]
|
taxonomy.py
CHANGED
|
@@ -208,7 +208,7 @@ class Taxonomy:
|
|
| 208 |
|
| 209 |
|
| 210 |
def build_taxonomy_tab():
|
| 211 |
-
"""Render the
|
| 212 |
if not DATABASE.exists():
|
| 213 |
gr.Markdown("The eukaryotic tree will appear when the taxonomy snapshot is available.")
|
| 214 |
return
|
|
@@ -248,7 +248,7 @@ def build_taxonomy_tab():
|
|
| 248 |
"The tree layout is illustrative; **counts and colors use the actual assembly inventory**.\n\n"
|
| 249 |
"Coverage means the fraction of current GenBank assembly versions with published annotations, including partial assemblies. "
|
| 250 |
"It does not measure the fraction of bases annotated. Parent counts include descendants once each. "
|
| 251 |
-
"Exact accession versions are matched. The accession lookup
|
| 252 |
"Sources: [NCBI assembly summary](https://ftp.ncbi.nlm.nih.gov/genomes/ASSEMBLY_REPORTS/assembly_summary_genbank.txt), "
|
| 253 |
"[NCBI Taxonomy](https://ftp.ncbi.nlm.nih.gov/pub/taxonomy/taxdump.tar.gz), "
|
| 254 |
"and [published annotations](https://huggingface.co/buckets/HuggingFaceBio/genbank-annotations).")
|
|
|
|
| 208 |
|
| 209 |
|
| 210 |
def build_taxonomy_tab():
|
| 211 |
+
"""Render the tree and its controls inside the Genome atlas tab."""
|
| 212 |
if not DATABASE.exists():
|
| 213 |
gr.Markdown("The eukaryotic tree will appear when the taxonomy snapshot is available.")
|
| 214 |
return
|
|
|
|
| 248 |
"The tree layout is illustrative; **counts and colors use the actual assembly inventory**.\n\n"
|
| 249 |
"Coverage means the fraction of current GenBank assembly versions with published annotations, including partial assemblies. "
|
| 250 |
"It does not measure the fraction of bases annotated. Parent counts include descendants once each. "
|
| 251 |
+
"Exact accession versions are matched. The accession lookup in the Data bank tab currently searches a smaller indexed subset.\n\n"
|
| 252 |
"Sources: [NCBI assembly summary](https://ftp.ncbi.nlm.nih.gov/genomes/ASSEMBLY_REPORTS/assembly_summary_genbank.txt), "
|
| 253 |
"[NCBI Taxonomy](https://ftp.ncbi.nlm.nih.gov/pub/taxonomy/taxdump.tar.gz), "
|
| 254 |
"and [published annotations](https://huggingface.co/buckets/HuggingFaceBio/genbank-annotations).")
|