Spaces:
Sleeping
Sleeping
Major UI improvements: interactive plots and better downloads
Browse filesPrediction Plot:
- Now uses interactive Plotly (zoom, pan, hover tooltips)
- CRISPR regions highlighted with shaded areas
- Hover shows exact position and score
Downloads (appear after analysis in collapsible accordion):
- PNG and PDF plot exports
- CSV file with all position/probability data
- Summary text file with results
Embeddings Tab:
- Downloads also in collapsible accordion
- Only visible after running analysis
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
app.py
CHANGED
|
@@ -63,8 +63,8 @@ EMBEDDING_CRISPR_EXAMPLE = """GACAGGTACAAGAAGGAGTATGCATCAATGTGGTCGTGTGGAACAAACGC
|
|
| 63 |
EMBEDDING_RANDOM_EXAMPLE = """ATGCGATCGATCGATCGATCGATCGTAGCTAGCTAGCTAGCTAGCTGATCGATCGATCGTAGCTAGCTAGCTGATCGATCGATCGATCGATCGTAGCTAGCTAGCTAGCTAGCTGATCGATCGATCGTAGCTAGCTAGCTGATCGATCGATCGATCGATCGTAGCTAGCTAGCTAGCTAGCTGATCGATCGATCGTAGCTAGCTAGCTGATCGATCGATCGATCGATCGTAGCTAGCTAGCTAGCTAGCTGATCGATCGATCGTAGCTAGCTAGCTGATCGATCGATCGATCGATCGTAGCTAGCTAGCTAGCTAGCTGATCGATCGATCGTAGCTAGCTAGCTGATCGATCGATCGATCGATCGTAGCTAGCTAGCTAGCTAGCTGATCGATCGATCGTAGCTAGCTAGCTGATCGATCGATCGATCGATCGTAGCTAGCTAGCTAGCTAGCTGATCGATCGATCGTAGCTAGCTAGCTGATCGATCGATCGATCGATCGTAGCTAGCTAGCTAGCTAGCTGATCGATCGATCGTAGCTAGCTAGCTGATCGATCGATCGATCGATCGTAGCTAGCTAGCTAGCTAGCTGATCGATCGATCGTAGCTAGCTAGCTGATCGATCGATCGATCGATCGTAGCTAGCTAGCTAGCTAGCTGATCGATCGATCGTAGCTAGCTAGCTGATCGATCGATCGATCGATCGTAGCTAGCTAGCTAGCTAGCTGATCGATCGATCGTAGCTAGCTAGCTGATCGATCGATCGATCGATCGTAGCTAGCTAGCTAGCTAGCTGATCGATCGATCGTAGCTAGCTAGCTGATCGATCGATCGATCGATCGTAGCTAGCTAGCTAGCTAGCTGATCGATCGATCGTAGCTAGCTAGCTGATCGATCGATCGATCGATCGTAGCTAGCTAGCTAGCTAGCTGATCGATCGATCGTAGCTAGCTAGCTGATCGATCGATCGATCGATCGTAGCTAGCTAGCTAGCTAGCTGATCGATCGATCGTAGCTAGCTAGCTGATCGATCGATCGATCGATCGTAGCTAGCTAGCTAGCTAGCTGATCGATCGATCGTAGCTAGCTAGCTGATCGATCGATCGATCGATCGTAGCTAGCTAGCTAGCTAGCTGATCGATCGATCGTAGCTAGCTAGCTGATCGATCGATCGATCGATCGTAGCTAGCTAGCTAGCTAGCTGATCGATCGATCGTAGCTAGCTAGCTGATCGATCGATCGATCGATCGTAGCTAGCTAGCTAGCTAGCTGATCGATCGATCGTAGCTAGCTAGCTGATCGATCGATCGATCGATCGTAGCTAGCTAGCTAGCTAGCTGATCGATCGATCGTAGCTAGCTAGCTGATCGATCGATCGATCGATCGTAGCTAGCTAGCTAGCTAGCTGATCGATCGATCGTAGCTAGCTAGCTGATCGATCGATCGATCGATCGTAGCTAGCTAGCTAGCTAGCTGATCGATCGATCGTAGCTAGCTAGCTGATCGATCGATCGATCGATCGTAGCTAGCTAGCTAGCTAGCTGATCGATCGATCGTAGCTAGCTAGCTGATCGATCGATCGATCGATCGTAGCTAGCTAGCTAGCTAGCTGATCGATCGATCGTAGCTAGCTAGCTGATCGATCGATCGATCGATCGTAGCTAGCTAGCTAGCTAGCTGATCGATCGATCGTAGCTAGCTAGCTGATCGATCGATCGATCGATCGTAGCTAGCTAGCTAGCTAGCTGATCGATCGATCGTAGCTAGCTAGCTGATCGATCGATCGATCGATCGTAGCTAGCTAGCTAGCTAGCTGATCGATCGATCGTAGCTAGCTAGCTGATCGATCGATCGATCGATCGTAGCTAGCTAGCTAGCTAGCTGATCGATCGATCGTAGCTAGCTAGCTGATCGATCGATCGATCGATCGTAGCTAGCTAGCTAGCTAGCTGATCGATCGATCGTAGCTAGCTAGCTGATCGATCGATCGATCGATCGTAGCTAGCTAGCTAGCTAGCTGATCGATCGATCGTAGCTAGCTAGCTGATCGATCGATCGATCGATCGTAGCTAGCTAGCTAGCTAGCTGATCGATCGATCGTAGCTAGCTAGCTGATCGATCGATCGATCGATCGTAGCTAGCTAGCTAGCTAGCTGATCGATCGATCGTAGCTAGCTAGCTGATCGATCGATCGATCGATCGTAGCTAGCTAGCTAGCTAGCTGATCGATCGATCGTAGCTAGCTAGCTGATCGATCGATCGATCGATCGTAGCTAGCTAGCTAGCTAGCTGATCGATCGATCGTAGCTAGCTAGCTGATCGATCGATCGATCGATCGTAGCTAGCTAGCTAGCTAGCTGATCGATCGATCGTAGCTAGCTAGCTGATCGATCGATCGATCGATCGTAGCTAGCTAGCTAGCTAGCTGATCGATCGATCGTAGCTAGCTAGCTGATCGATCGATCGATCGATCGTAGCTAGCTAGCTAGCTAGCTGATCGATCGATCGTAGCTAGCTAGCT"""
|
| 64 |
|
| 65 |
|
| 66 |
-
def create_prediction_plot(positions, probabilities, threshold=0.3):
|
| 67 |
-
"""Create a matplotlib figure showing the prediction curve."""
|
| 68 |
fig, ax = plt.subplots(figsize=(12, 4))
|
| 69 |
|
| 70 |
# Plot probability curve
|
|
@@ -92,6 +92,60 @@ def create_prediction_plot(positions, probabilities, threshold=0.3):
|
|
| 92 |
return fig
|
| 93 |
|
| 94 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 95 |
def create_embedding_heatmap(embedding, title="Sequence Embedding", cols=30):
|
| 96 |
"""Create a heatmap visualization of the embedding vector."""
|
| 97 |
embedding = np.array(embedding)
|
|
@@ -543,23 +597,62 @@ def create_interactive_state_plot(embeddings, n_clusters=8, stride=100, use_3d=F
|
|
| 543 |
|
| 544 |
def predict(sequence: str, stride: int = 100, threshold: float = 0.3):
|
| 545 |
"""Predict CRISPR array probability for each position."""
|
|
|
|
|
|
|
|
|
|
| 546 |
sequence = strip_fasta_header(sequence.strip())
|
| 547 |
|
| 548 |
is_valid, error = validate_sequence(sequence)
|
| 549 |
if not is_valid:
|
| 550 |
-
return None, f"**Error**: {error}", None, None, None
|
| 551 |
|
| 552 |
result = predict_sequence(sequence, stride=stride, aggregation="mean")
|
| 553 |
|
| 554 |
-
#
|
| 555 |
-
|
| 556 |
|
| 557 |
-
#
|
| 558 |
-
|
| 559 |
|
| 560 |
-
#
|
| 561 |
-
|
|
|
|
|
|
|
| 562 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 563 |
summary = f"""## Results
|
| 564 |
|
| 565 |
| Metric | Value |
|
|
@@ -576,7 +669,7 @@ def predict(sequence: str, stride: int = 100, threshold: float = 0.3):
|
|
| 576 |
for r in regions:
|
| 577 |
summary += f"- **Region {r['region_id']}**: positions {r['start']:,}-{r['end']:,} ({r['length']} bp), score: {r['mean_score']:.3f}\n"
|
| 578 |
|
| 579 |
-
return fig, summary, regions, png_path, pdf_path
|
| 580 |
|
| 581 |
|
| 582 |
def detect(sequence: str, threshold: float = 0.3, min_length: int = 160):
|
|
@@ -721,7 +814,7 @@ Detect CRISPR arrays in DNA sequences using a BERT-based deep learning model (43
|
|
| 721 |
""")
|
| 722 |
|
| 723 |
with gr.Tab("Predict & Visualize"):
|
| 724 |
-
gr.Markdown("Paste a DNA sequence to get per-position CRISPR probability scores with visualization.")
|
| 725 |
with gr.Row():
|
| 726 |
with gr.Column(scale=1):
|
| 727 |
seq_input = gr.Textbox(
|
|
@@ -752,18 +845,28 @@ Detect CRISPR arrays in DNA sequences using a BERT-based deep learning model (43
|
|
| 752 |
lambda: NON_CRISPR_EXAMPLE, outputs=seq_input
|
| 753 |
)
|
| 754 |
result_summary = gr.Markdown()
|
| 755 |
-
gr.
|
| 756 |
-
|
| 757 |
-
|
| 758 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 759 |
with gr.Column(scale=2):
|
| 760 |
-
plot_output = gr.Plot(label="CRISPR Score Profile")
|
| 761 |
regions_output = gr.JSON(label="Detected Regions", visible=False)
|
| 762 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 763 |
predict_btn.click(
|
| 764 |
-
|
| 765 |
inputs=[seq_input, stride_input, threshold_input],
|
| 766 |
-
outputs=[plot_output, result_summary, regions_output, pred_download_png, pred_download_pdf]
|
| 767 |
)
|
| 768 |
|
| 769 |
with gr.Tab("Embeddings"):
|
|
@@ -809,10 +912,10 @@ Visualize how the model's internal representation changes across the sequence. T
|
|
| 809 |
- Downstream: 2364-2964 bp (random)
|
| 810 |
""")
|
| 811 |
embed_summary = gr.Markdown()
|
| 812 |
-
gr.
|
| 813 |
-
|
| 814 |
-
|
| 815 |
-
|
| 816 |
with gr.Column(scale=2):
|
| 817 |
embed_plot = gr.Plot(label="Embedding Visualization (Interactive)")
|
| 818 |
|
|
@@ -823,10 +926,14 @@ Visualize how the model's internal representation changes across the sequence. T
|
|
| 823 |
outputs=[use_3d]
|
| 824 |
)
|
| 825 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 826 |
embed_btn.click(
|
| 827 |
-
|
| 828 |
inputs=[embed_seq, embed_mode, use_3d],
|
| 829 |
-
outputs=[embed_plot, embed_summary, download_png, download_pdf]
|
| 830 |
)
|
| 831 |
|
| 832 |
with gr.Tab("About"):
|
|
|
|
| 63 |
EMBEDDING_RANDOM_EXAMPLE = """ATGCGATCGATCGATCGATCGATCGTAGCTAGCTAGCTAGCTAGCTGATCGATCGATCGTAGCTAGCTAGCTGATCGATCGATCGATCGATCGTAGCTAGCTAGCTAGCTAGCTGATCGATCGATCGTAGCTAGCTAGCTGATCGATCGATCGATCGATCGTAGCTAGCTAGCTAGCTAGCTGATCGATCGATCGTAGCTAGCTAGCTGATCGATCGATCGATCGATCGTAGCTAGCTAGCTAGCTAGCTGATCGATCGATCGTAGCTAGCTAGCTGATCGATCGATCGATCGATCGTAGCTAGCTAGCTAGCTAGCTGATCGATCGATCGTAGCTAGCTAGCTGATCGATCGATCGATCGATCGTAGCTAGCTAGCTAGCTAGCTGATCGATCGATCGTAGCTAGCTAGCTGATCGATCGATCGATCGATCGTAGCTAGCTAGCTAGCTAGCTGATCGATCGATCGTAGCTAGCTAGCTGATCGATCGATCGATCGATCGTAGCTAGCTAGCTAGCTAGCTGATCGATCGATCGTAGCTAGCTAGCTGATCGATCGATCGATCGATCGTAGCTAGCTAGCTAGCTAGCTGATCGATCGATCGTAGCTAGCTAGCTGATCGATCGATCGATCGATCGTAGCTAGCTAGCTAGCTAGCTGATCGATCGATCGTAGCTAGCTAGCTGATCGATCGATCGATCGATCGTAGCTAGCTAGCTAGCTAGCTGATCGATCGATCGTAGCTAGCTAGCTGATCGATCGATCGATCGATCGTAGCTAGCTAGCTAGCTAGCTGATCGATCGATCGTAGCTAGCTAGCTGATCGATCGATCGATCGATCGTAGCTAGCTAGCTAGCTAGCTGATCGATCGATCGTAGCTAGCTAGCTGATCGATCGATCGATCGATCGTAGCTAGCTAGCTAGCTAGCTGATCGATCGATCGTAGCTAGCTAGCTGATCGATCGATCGATCGATCGTAGCTAGCTAGCTAGCTAGCTGATCGATCGATCGTAGCTAGCTAGCTGATCGATCGATCGATCGATCGTAGCTAGCTAGCTAGCTAGCTGATCGATCGATCGTAGCTAGCTAGCTGATCGATCGATCGATCGATCGTAGCTAGCTAGCTAGCTAGCTGATCGATCGATCGTAGCTAGCTAGCTGATCGATCGATCGATCGATCGTAGCTAGCTAGCTAGCTAGCTGATCGATCGATCGTAGCTAGCTAGCTGATCGATCGATCGATCGATCGTAGCTAGCTAGCTAGCTAGCTGATCGATCGATCGTAGCTAGCTAGCTGATCGATCGATCGATCGATCGTAGCTAGCTAGCTAGCTAGCTGATCGATCGATCGTAGCTAGCTAGCTGATCGATCGATCGATCGATCGTAGCTAGCTAGCTAGCTAGCTGATCGATCGATCGTAGCTAGCTAGCTGATCGATCGATCGATCGATCGTAGCTAGCTAGCTAGCTAGCTGATCGATCGATCGTAGCTAGCTAGCTGATCGATCGATCGATCGATCGTAGCTAGCTAGCTAGCTAGCTGATCGATCGATCGTAGCTAGCTAGCTGATCGATCGATCGATCGATCGTAGCTAGCTAGCTAGCTAGCTGATCGATCGATCGTAGCTAGCTAGCTGATCGATCGATCGATCGATCGTAGCTAGCTAGCTAGCTAGCTGATCGATCGATCGTAGCTAGCTAGCTGATCGATCGATCGATCGATCGTAGCTAGCTAGCTAGCTAGCTGATCGATCGATCGTAGCTAGCTAGCTGATCGATCGATCGATCGATCGTAGCTAGCTAGCTAGCTAGCTGATCGATCGATCGTAGCTAGCTAGCTGATCGATCGATCGATCGATCGTAGCTAGCTAGCTAGCTAGCTGATCGATCGATCGTAGCTAGCTAGCTGATCGATCGATCGATCGATCGTAGCTAGCTAGCTAGCTAGCTGATCGATCGATCGTAGCTAGCTAGCTGATCGATCGATCGATCGATCGTAGCTAGCTAGCTAGCTAGCTGATCGATCGATCGTAGCTAGCTAGCTGATCGATCGATCGATCGATCGTAGCTAGCTAGCTAGCTAGCTGATCGATCGATCGTAGCTAGCTAGCTGATCGATCGATCGATCGATCGTAGCTAGCTAGCTAGCTAGCTGATCGATCGATCGTAGCTAGCTAGCTGATCGATCGATCGATCGATCGTAGCTAGCTAGCTAGCTAGCTGATCGATCGATCGTAGCTAGCTAGCTGATCGATCGATCGATCGATCGTAGCTAGCTAGCTAGCTAGCTGATCGATCGATCGTAGCTAGCTAGCTGATCGATCGATCGATCGATCGTAGCTAGCTAGCTAGCTAGCTGATCGATCGATCGTAGCTAGCTAGCTGATCGATCGATCGATCGATCGTAGCTAGCTAGCTAGCTAGCTGATCGATCGATCGTAGCTAGCTAGCTGATCGATCGATCGATCGATCGTAGCTAGCTAGCTAGCTAGCTGATCGATCGATCGTAGCTAGCTAGCT"""
|
| 64 |
|
| 65 |
|
| 66 |
+
def create_prediction_plot(positions, probabilities, threshold=0.3, regions=None):
|
| 67 |
+
"""Create a matplotlib figure showing the prediction curve (for PNG/PDF export)."""
|
| 68 |
fig, ax = plt.subplots(figsize=(12, 4))
|
| 69 |
|
| 70 |
# Plot probability curve
|
|
|
|
| 92 |
return fig
|
| 93 |
|
| 94 |
|
| 95 |
+
def create_interactive_prediction_plot(positions, probabilities, threshold=0.3, regions=None):
|
| 96 |
+
"""Create an interactive Plotly figure showing the prediction curve."""
|
| 97 |
+
fig = go.Figure()
|
| 98 |
+
|
| 99 |
+
# Main probability curve with fill
|
| 100 |
+
fig.add_trace(go.Scatter(
|
| 101 |
+
x=positions,
|
| 102 |
+
y=probabilities,
|
| 103 |
+
mode='lines',
|
| 104 |
+
name='CRISPR Score',
|
| 105 |
+
line=dict(color='royalblue', width=1),
|
| 106 |
+
fill='tozeroy',
|
| 107 |
+
fillcolor='rgba(65, 105, 225, 0.2)',
|
| 108 |
+
hovertemplate='Position: %{x:,} bp<br>Score: %{y:.3f}<extra></extra>'
|
| 109 |
+
))
|
| 110 |
+
|
| 111 |
+
# Add threshold line
|
| 112 |
+
fig.add_hline(
|
| 113 |
+
y=threshold,
|
| 114 |
+
line_dash="dash",
|
| 115 |
+
line_color="red",
|
| 116 |
+
annotation_text=f"Threshold ({threshold})",
|
| 117 |
+
annotation_position="top right"
|
| 118 |
+
)
|
| 119 |
+
|
| 120 |
+
# Highlight detected CRISPR regions
|
| 121 |
+
if regions:
|
| 122 |
+
for r in regions:
|
| 123 |
+
fig.add_vrect(
|
| 124 |
+
x0=r['start'], x1=r['end'],
|
| 125 |
+
fillcolor="rgba(255, 0, 0, 0.15)",
|
| 126 |
+
layer="below",
|
| 127 |
+
line_width=0,
|
| 128 |
+
annotation_text=f"Region {r['region_id']}",
|
| 129 |
+
annotation_position="top left",
|
| 130 |
+
annotation_font_size=10
|
| 131 |
+
)
|
| 132 |
+
|
| 133 |
+
fig.update_layout(
|
| 134 |
+
title=dict(text='CRISPR Array Detection Score', font=dict(size=16)),
|
| 135 |
+
xaxis_title='Position (bp)',
|
| 136 |
+
yaxis_title='CRISPR Probability',
|
| 137 |
+
yaxis=dict(range=[0, 1], gridcolor='lightgray'),
|
| 138 |
+
xaxis=dict(range=[0, max(positions) if positions else 1000], gridcolor='lightgray'),
|
| 139 |
+
hovermode='x unified',
|
| 140 |
+
showlegend=True,
|
| 141 |
+
legend=dict(yanchor="top", y=0.99, xanchor="right", x=0.99),
|
| 142 |
+
height=400,
|
| 143 |
+
plot_bgcolor='white'
|
| 144 |
+
)
|
| 145 |
+
|
| 146 |
+
return fig
|
| 147 |
+
|
| 148 |
+
|
| 149 |
def create_embedding_heatmap(embedding, title="Sequence Embedding", cols=30):
|
| 150 |
"""Create a heatmap visualization of the embedding vector."""
|
| 151 |
embedding = np.array(embedding)
|
|
|
|
| 597 |
|
| 598 |
def predict(sequence: str, stride: int = 100, threshold: float = 0.3):
|
| 599 |
"""Predict CRISPR array probability for each position."""
|
| 600 |
+
import tempfile
|
| 601 |
+
import csv
|
| 602 |
+
|
| 603 |
sequence = strip_fasta_header(sequence.strip())
|
| 604 |
|
| 605 |
is_valid, error = validate_sequence(sequence)
|
| 606 |
if not is_valid:
|
| 607 |
+
return None, f"**Error**: {error}", None, None, None, None, None
|
| 608 |
|
| 609 |
result = predict_sequence(sequence, stride=stride, aggregation="mean")
|
| 610 |
|
| 611 |
+
# Detect regions first (needed for plot annotations)
|
| 612 |
+
regions = detect_crispr_regions(sequence, threshold=threshold, min_length=100, stride=stride)
|
| 613 |
|
| 614 |
+
# Create interactive Plotly plot
|
| 615 |
+
fig = create_interactive_prediction_plot(result.positions, result.probabilities, threshold, regions)
|
| 616 |
|
| 617 |
+
# Create static matplotlib plot for PNG/PDF export
|
| 618 |
+
static_fig = create_prediction_plot(result.positions, result.probabilities, threshold, regions)
|
| 619 |
+
png_path, pdf_path = save_figure_to_file(static_fig, "crispr_prediction")
|
| 620 |
+
plt.close(static_fig)
|
| 621 |
|
| 622 |
+
# Create CSV with prediction data
|
| 623 |
+
temp_dir = tempfile.gettempdir()
|
| 624 |
+
csv_path = os.path.join(temp_dir, "crispr_predictions.csv")
|
| 625 |
+
with open(csv_path, 'w', newline='') as f:
|
| 626 |
+
writer = csv.writer(f)
|
| 627 |
+
writer.writerow(['position', 'probability', 'above_threshold'])
|
| 628 |
+
for pos, prob in zip(result.positions, result.probabilities):
|
| 629 |
+
writer.writerow([pos, f"{prob:.4f}", prob >= threshold])
|
| 630 |
+
|
| 631 |
+
# Create summary text file
|
| 632 |
+
summary_path = os.path.join(temp_dir, "crispr_summary.txt")
|
| 633 |
+
summary_text = f"""CRISPR Array Detection Summary
|
| 634 |
+
==============================
|
| 635 |
+
|
| 636 |
+
Sequence length: {result.sequence_length:,} bp
|
| 637 |
+
Windows processed: {result.num_windows}
|
| 638 |
+
Stride: {stride} bp
|
| 639 |
+
Threshold: {threshold}
|
| 640 |
+
|
| 641 |
+
Overall score: {result.overall_score:.4f}
|
| 642 |
+
Max score: {max(result.probabilities):.4f}
|
| 643 |
+
Min score: {min(result.probabilities):.4f}
|
| 644 |
+
|
| 645 |
+
Detected CRISPR Regions: {len(regions)}
|
| 646 |
+
"""
|
| 647 |
+
if regions:
|
| 648 |
+
summary_text += "\nRegion Details:\n"
|
| 649 |
+
for r in regions:
|
| 650 |
+
summary_text += f" Region {r['region_id']}: {r['start']:,}-{r['end']:,} bp ({r['length']} bp), mean score: {r['mean_score']:.3f}\n"
|
| 651 |
+
|
| 652 |
+
with open(summary_path, 'w') as f:
|
| 653 |
+
f.write(summary_text)
|
| 654 |
+
|
| 655 |
+
# Markdown summary for display
|
| 656 |
summary = f"""## Results
|
| 657 |
|
| 658 |
| Metric | Value |
|
|
|
|
| 669 |
for r in regions:
|
| 670 |
summary += f"- **Region {r['region_id']}**: positions {r['start']:,}-{r['end']:,} ({r['length']} bp), score: {r['mean_score']:.3f}\n"
|
| 671 |
|
| 672 |
+
return fig, summary, regions, png_path, pdf_path, csv_path, summary_path
|
| 673 |
|
| 674 |
|
| 675 |
def detect(sequence: str, threshold: float = 0.3, min_length: int = 160):
|
|
|
|
| 814 |
""")
|
| 815 |
|
| 816 |
with gr.Tab("Predict & Visualize"):
|
| 817 |
+
gr.Markdown("Paste a DNA sequence to get per-position CRISPR probability scores with interactive visualization.")
|
| 818 |
with gr.Row():
|
| 819 |
with gr.Column(scale=1):
|
| 820 |
seq_input = gr.Textbox(
|
|
|
|
| 845 |
lambda: NON_CRISPR_EXAMPLE, outputs=seq_input
|
| 846 |
)
|
| 847 |
result_summary = gr.Markdown()
|
| 848 |
+
with gr.Accordion("Downloads", open=False, visible=False) as download_accordion:
|
| 849 |
+
gr.Markdown("**Plot exports:**")
|
| 850 |
+
with gr.Row():
|
| 851 |
+
pred_download_png = gr.File(label="PNG", interactive=False)
|
| 852 |
+
pred_download_pdf = gr.File(label="PDF", interactive=False)
|
| 853 |
+
gr.Markdown("**Data exports:**")
|
| 854 |
+
with gr.Row():
|
| 855 |
+
pred_download_csv = gr.File(label="Predictions (CSV)", interactive=False)
|
| 856 |
+
pred_download_summary = gr.File(label="Summary (TXT)", interactive=False)
|
| 857 |
with gr.Column(scale=2):
|
| 858 |
+
plot_output = gr.Plot(label="CRISPR Score Profile (Interactive)")
|
| 859 |
regions_output = gr.JSON(label="Detected Regions", visible=False)
|
| 860 |
|
| 861 |
+
def predict_and_show_downloads(*args):
|
| 862 |
+
results = predict(*args)
|
| 863 |
+
# Return results plus visibility update for accordion
|
| 864 |
+
return results + (gr.update(visible=True),)
|
| 865 |
+
|
| 866 |
predict_btn.click(
|
| 867 |
+
predict_and_show_downloads,
|
| 868 |
inputs=[seq_input, stride_input, threshold_input],
|
| 869 |
+
outputs=[plot_output, result_summary, regions_output, pred_download_png, pred_download_pdf, pred_download_csv, pred_download_summary, download_accordion]
|
| 870 |
)
|
| 871 |
|
| 872 |
with gr.Tab("Embeddings"):
|
|
|
|
| 912 |
- Downstream: 2364-2964 bp (random)
|
| 913 |
""")
|
| 914 |
embed_summary = gr.Markdown()
|
| 915 |
+
with gr.Accordion("Downloads", open=False, visible=False) as embed_download_accordion:
|
| 916 |
+
with gr.Row():
|
| 917 |
+
download_png = gr.File(label="PNG", interactive=False)
|
| 918 |
+
download_pdf = gr.File(label="PDF", interactive=False)
|
| 919 |
with gr.Column(scale=2):
|
| 920 |
embed_plot = gr.Plot(label="Embedding Visualization (Interactive)")
|
| 921 |
|
|
|
|
| 926 |
outputs=[use_3d]
|
| 927 |
)
|
| 928 |
|
| 929 |
+
def embed_and_show_downloads(*args):
|
| 930 |
+
results = get_embedding(*args)
|
| 931 |
+
return results + (gr.update(visible=True),)
|
| 932 |
+
|
| 933 |
embed_btn.click(
|
| 934 |
+
embed_and_show_downloads,
|
| 935 |
inputs=[embed_seq, embed_mode, use_3d],
|
| 936 |
+
outputs=[embed_plot, embed_summary, download_png, download_pdf, embed_download_accordion]
|
| 937 |
)
|
| 938 |
|
| 939 |
with gr.Tab("About"):
|