Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -140,7 +140,7 @@ def run_deep_learning_cascade(dna_sequence):
|
|
| 140 |
|
| 141 |
validation = "High Confidence Regulatory Element" if p_promoter >= 0.50 else "Weak Regulatory Signal"
|
| 142 |
final_label += f" | Validation: {validation}"
|
| 143 |
-
raw_scores["
|
| 144 |
|
| 145 |
return final_label, confidence, raw_scores
|
| 146 |
|
|
@@ -150,13 +150,13 @@ def run_deep_learning_cascade(dna_sequence):
|
|
| 150 |
def get_genomic_context(sequence, is_coding):
|
| 151 |
feature_type = "CODING" if is_coding else "PROMOTER"
|
| 152 |
try:
|
| 153 |
-
#
|
| 154 |
result_handle = NCBIWWW.qblast(
|
| 155 |
"blastn",
|
| 156 |
"nt",
|
| 157 |
sequence,
|
| 158 |
entrez_query="Homo sapiens[Organism] AND biomol_genomic[PROP]",
|
| 159 |
-
hitlist_size=
|
| 160 |
)
|
| 161 |
blast_record = NCBIXML.read(result_handle)
|
| 162 |
except Exception as e:
|
|
@@ -165,10 +165,17 @@ def get_genomic_context(sequence, is_coding):
|
|
| 165 |
if not blast_record.alignments:
|
| 166 |
return {"error": "No human genome match found for this sequence."}
|
| 167 |
|
| 168 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 169 |
hsp = alignment.hsps[0]
|
| 170 |
-
chrom_match = re.search(r"chromosome\s([0-9XYMT]+)", alignment.title, re.IGNORECASE)
|
| 171 |
-
chrom = chrom_match.group(1) if chrom_match else None
|
| 172 |
location_string = f"Chromosome {chrom}" if chrom else f"Accession {alignment.accession}"
|
| 173 |
|
| 174 |
start, end = hsp.sbjct_start, hsp.sbjct_end
|
|
@@ -182,7 +189,6 @@ def get_genomic_context(sequence, is_coding):
|
|
| 182 |
search_end = max(start, end) if feature_type == "CODING" else (end + 15000 if is_forward else end)
|
| 183 |
|
| 184 |
try:
|
| 185 |
-
# THE ENSEMBL FIX: Changed 'Content-Type' to 'Accept' for proper JSON retrieval!
|
| 186 |
response = requests.get(
|
| 187 |
f"https://rest.ensembl.org/overlap/region/human/{chrom}:{search_start}-{search_end}?feature=gene",
|
| 188 |
headers={"Accept": "application/json"}
|
|
@@ -210,7 +216,9 @@ def get_genomic_context(sequence, is_coding):
|
|
| 210 |
# ==============================
|
| 211 |
def gradio_inference(dna_sequence, run_mapping):
|
| 212 |
if len(dna_sequence.strip()) < 10:
|
| 213 |
-
|
|
|
|
|
|
|
| 214 |
|
| 215 |
# Run AI Models (GPU)
|
| 216 |
label, conf, raw_scores = run_deep_learning_cascade(dna_sequence)
|
|
@@ -223,34 +231,38 @@ def gradio_inference(dna_sequence, run_mapping):
|
|
| 223 |
else:
|
| 224 |
stats_lines.append(f"- {key}: {val}")
|
| 225 |
|
| 226 |
-
# Run Context Mapping (CPU / Network)
|
| 227 |
-
context_output = ""
|
| 228 |
-
if run_mapping:
|
| 229 |
-
is_coding = "GENE" in label
|
| 230 |
-
context = get_genomic_context(dna_sequence, is_coding)
|
| 231 |
-
|
| 232 |
-
if "error" in context:
|
| 233 |
-
context_output = f"β Mapping failed: {context['error']}"
|
| 234 |
-
elif "location" in context:
|
| 235 |
-
context_lines = [
|
| 236 |
-
f" Location: {context['location']}",
|
| 237 |
-
f" Strand: {context['strand']}",
|
| 238 |
-
f" Coordinates: {context['start']:,} β {context['end']:,}",
|
| 239 |
-
f" Notes: {context['metadata']}"
|
| 240 |
-
]
|
| 241 |
-
context_output = "\n".join(context_lines)
|
| 242 |
-
else:
|
| 243 |
-
context_output = "β οΈ Could not map sequence."
|
| 244 |
-
else:
|
| 245 |
-
context_output = "πΊοΈ Spatial mapping skipped (Enable checkbox to query NCBI)."
|
| 246 |
-
|
| 247 |
summary = (
|
| 248 |
f"β
Deep Scan Complete\n\n"
|
| 249 |
-
f"
|
| 250 |
-
f"
|
| 251 |
)
|
| 252 |
|
| 253 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 254 |
|
| 255 |
# --- CUSTOM CSS ---
|
| 256 |
custom_css = """
|
|
@@ -302,14 +314,21 @@ with gr.Blocks(theme=gr.themes.Soft(), title="𧬠The Genomic Oracle π§¬", css
|
|
| 302 |
submit_btn = gr.Button("π Initialize Deep Scan", elem_id="scan_btn")
|
| 303 |
|
| 304 |
with gr.Column(scale=1):
|
| 305 |
-
output_summary = gr.Textbox(label="
|
| 306 |
-
stats_panel = gr.Textbox(label="
|
| 307 |
-
mapping_section = gr.Accordion("
|
| 308 |
with mapping_section:
|
| 309 |
context_output = gr.Textbox(label="Mapping Results", lines=5, placeholder="Results will appear here...")
|
| 310 |
|
| 311 |
info_box = gr.Markdown("", elem_id="info_box")
|
| 312 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 313 |
submit_btn.click(
|
| 314 |
fn=lambda seq, map_flag: (
|
| 315 |
*gradio_inference(seq, map_flag)[:3],
|
|
|
|
| 140 |
|
| 141 |
validation = "High Confidence Regulatory Element" if p_promoter >= 0.50 else "Weak Regulatory Signal"
|
| 142 |
final_label += f" | Validation: {validation}"
|
| 143 |
+
raw_scores["Promoter Signal"] = p_promoter
|
| 144 |
|
| 145 |
return final_label, confidence, raw_scores
|
| 146 |
|
|
|
|
| 150 |
def get_genomic_context(sequence, is_coding):
|
| 151 |
feature_type = "CODING" if is_coding else "PROMOTER"
|
| 152 |
try:
|
| 153 |
+
# ask blast for 5 hits instead of 1 so we can hunt for the true chromosome
|
| 154 |
result_handle = NCBIWWW.qblast(
|
| 155 |
"blastn",
|
| 156 |
"nt",
|
| 157 |
sequence,
|
| 158 |
entrez_query="Homo sapiens[Organism] AND biomol_genomic[PROP]",
|
| 159 |
+
hitlist_size=5
|
| 160 |
)
|
| 161 |
blast_record = NCBIXML.read(result_handle)
|
| 162 |
except Exception as e:
|
|
|
|
| 165 |
if not blast_record.alignments:
|
| 166 |
return {"error": "No human genome match found for this sequence."}
|
| 167 |
|
| 168 |
+
# Loop through the top hits and grab the first one that is an actual Chromosome
|
| 169 |
+
alignment = blast_record.alignments[0] # Default to the top hit
|
| 170 |
+
chrom = None
|
| 171 |
+
for aln in blast_record.alignments:
|
| 172 |
+
chrom_match = re.search(r"chromosome\s([0-9XYMT]+)", aln.title, re.IGNORECASE)
|
| 173 |
+
if chrom_match:
|
| 174 |
+
alignment = aln
|
| 175 |
+
chrom = chrom_match.group(1)
|
| 176 |
+
break # We found the chromosome, stop searching!
|
| 177 |
+
|
| 178 |
hsp = alignment.hsps[0]
|
|
|
|
|
|
|
| 179 |
location_string = f"Chromosome {chrom}" if chrom else f"Accession {alignment.accession}"
|
| 180 |
|
| 181 |
start, end = hsp.sbjct_start, hsp.sbjct_end
|
|
|
|
| 189 |
search_end = max(start, end) if feature_type == "CODING" else (end + 15000 if is_forward else end)
|
| 190 |
|
| 191 |
try:
|
|
|
|
| 192 |
response = requests.get(
|
| 193 |
f"https://rest.ensembl.org/overlap/region/human/{chrom}:{search_start}-{search_end}?feature=gene",
|
| 194 |
headers={"Accept": "application/json"}
|
|
|
|
| 216 |
# ==============================
|
| 217 |
def gradio_inference(dna_sequence, run_mapping):
|
| 218 |
if len(dna_sequence.strip()) < 10:
|
| 219 |
+
# Using 'yield' to instantly push updates to the UI
|
| 220 |
+
yield ("β Sequence too short!", "", "", "β οΈ Please enter at least 10 base pairs.")
|
| 221 |
+
return
|
| 222 |
|
| 223 |
# Run AI Models (GPU)
|
| 224 |
label, conf, raw_scores = run_deep_learning_cascade(dna_sequence)
|
|
|
|
| 231 |
else:
|
| 232 |
stats_lines.append(f"- {key}: {val}")
|
| 233 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 234 |
summary = (
|
| 235 |
f"β
Deep Scan Complete\n\n"
|
| 236 |
+
f"Final Classification: {label}\n"
|
| 237 |
+
f"Confidence Score: {conf:.2%}"
|
| 238 |
)
|
| 239 |
|
| 240 |
+
if not run_mapping:
|
| 241 |
+
yield (summary, "\n".join(stats_lines), "Spatial mapping skipped (Enable checkbox to run BLAST).", "")
|
| 242 |
+
return
|
| 243 |
+
|
| 244 |
+
# π¨ STREAMING UPDATE: Pushes AI results instantly while showing a loading message for BLAST!
|
| 245 |
+
yield (summary, "\n".join(stats_lines), "β³ Querying NCBI BLAST... (This takes 1-3 minutes. Please wait.)", "")
|
| 246 |
+
|
| 247 |
+
# Run Context Mapping (CPU / Network)
|
| 248 |
+
is_coding = "GENE" in label
|
| 249 |
+
context = get_genomic_context(dna_sequence, is_coding)
|
| 250 |
+
|
| 251 |
+
if "error" in context:
|
| 252 |
+
context_output = f"β Mapping failed: {context['error']}"
|
| 253 |
+
elif "location" in context:
|
| 254 |
+
context_lines = [
|
| 255 |
+
f"Location: {context['location']}",
|
| 256 |
+
f"Strand: {context['strand']}",
|
| 257 |
+
f"Coordinates: {context['start']:,} β {context['end']:,}",
|
| 258 |
+
f"Notes: {context['metadata']}"
|
| 259 |
+
]
|
| 260 |
+
context_output = "\n".join(context_lines)
|
| 261 |
+
else:
|
| 262 |
+
context_output = "β οΈ Could not map sequence."
|
| 263 |
+
|
| 264 |
+
# π¨ FINAL UPDATE: Pushes the finished BLAST results!
|
| 265 |
+
yield (summary, "\n".join(stats_lines), context_output, "")
|
| 266 |
|
| 267 |
# --- CUSTOM CSS ---
|
| 268 |
custom_css = """
|
|
|
|
| 314 |
submit_btn = gr.Button("π Initialize Deep Scan", elem_id="scan_btn")
|
| 315 |
|
| 316 |
with gr.Column(scale=1):
|
| 317 |
+
output_summary = gr.Textbox(label="Classification Summary", lines=4)
|
| 318 |
+
stats_panel = gr.Textbox(label="Internal Pipeline Statistics", lines=4)
|
| 319 |
+
mapping_section = gr.Accordion("Genomic Context (BLAST/Ensembl)", open=False)
|
| 320 |
with mapping_section:
|
| 321 |
context_output = gr.Textbox(label="Mapping Results", lines=5, placeholder="Results will appear here...")
|
| 322 |
|
| 323 |
info_box = gr.Markdown("", elem_id="info_box")
|
| 324 |
|
| 325 |
+
# π¨ NEW UX FEATURE: Auto-open the accordion when the user checks the BLAST box!
|
| 326 |
+
run_mapping_cb.change(
|
| 327 |
+
fn=lambda is_checked: gr.Accordion(open=is_checked),
|
| 328 |
+
inputs=[run_mapping_cb],
|
| 329 |
+
outputs=[mapping_section]
|
| 330 |
+
)
|
| 331 |
+
|
| 332 |
submit_btn.click(
|
| 333 |
fn=lambda seq, map_flag: (
|
| 334 |
*gradio_inference(seq, map_flag)[:3],
|