re-type commited on
Commit
434da56
·
verified ·
1 Parent(s): 905619b

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +38 -31
app.py CHANGED
@@ -217,28 +217,19 @@ def cleanup_file(file_path: str) -> None:
217
  logger.warning(f"Failed to clean up {file_path}: {cleanup_error}")
218
 
219
  def phylogenetic_placement(sequence: str, mafft_cmd: str, iqtree_cmd: str):
220
- query_fasta = None # Predefine to avoid scoping issues
221
  try:
222
- # Input validation
223
  if len(sequence.strip()) < 100:
224
  return False, "Sequence too short (<100 bp).", None, None
225
-
226
- # Setup file paths
227
  query_id = f"QUERY_{uuid.uuid4().hex[:8]}"
228
  query_fasta = os.path.join(QUERY_OUTPUT_DIR, f"{query_id}.fa")
229
  aligned_with_query = os.path.join(QUERY_OUTPUT_DIR, f"{query_id}_aligned.fa")
230
  output_prefix = os.path.join(QUERY_OUTPUT_DIR, f"{query_id}_placed_tree")
231
-
232
- # Check reference files
233
  if not os.path.exists(ALIGNMENT_PATH) or not os.path.exists(TREE_PATH):
234
  cleanup_file(query_fasta)
235
  return False, "Reference alignment or tree not found.", None, None
236
-
237
- # Write query FASTA
238
  query_record = SeqRecord(Seq(sequence.upper()), id=query_id, description="")
239
  SeqIO.write([query_record], query_fasta, "fasta")
240
-
241
- # Run MAFFT
242
  with open(aligned_with_query, "w") as output_file:
243
  result = subprocess.run(
244
  [mafft_cmd, "--add", query_fasta, "--reorder", ALIGNMENT_PATH],
@@ -251,8 +242,6 @@ def phylogenetic_placement(sequence: str, mafft_cmd: str, iqtree_cmd: str):
251
  if not os.path.exists(aligned_with_query) or os.path.getsize(aligned_with_query) == 0:
252
  cleanup_file(query_fasta)
253
  return False, "MAFFT alignment failed.", None, None
254
-
255
- # Run IQ-TREE
256
  result = subprocess.run(
257
  [iqtree_cmd, "-s", aligned_with_query, "-g", TREE_PATH, "-m", "GTR+G", "-pre", output_prefix, "-redo"],
258
  capture_output=True,
@@ -264,16 +253,14 @@ def phylogenetic_placement(sequence: str, mafft_cmd: str, iqtree_cmd: str):
264
  if not os.path.exists(treefile):
265
  cleanup_file(query_fasta)
266
  return False, "IQ-TREE placement failed.", aligned_with_query, None
267
-
268
- # Success
269
  success_msg = f"Placement completed!\nQuery ID: {query_id}\nAlignment: {os.path.basename(aligned_with_query)}\nTree: {os.path.basename(treefile)}"
270
  cleanup_file(query_fasta)
271
  return True, success_msg, aligned_with_query, treefile
272
-
273
  except Exception as main_error:
274
  logger.error(f"Phylogenetic placement failed: {main_error}", exc_info=True)
275
  cleanup_file(query_fasta)
276
  return False, f"Error: {str(main_error)}", None, None
 
277
  def analyze_sequence_for_tree(sequence: str, matching_percentage: float):
278
  try:
279
  logger.debug("Starting tree analysis...")
@@ -453,11 +440,12 @@ async def run_pipeline_from_file(fasta_file_obj, similarity_score, build_ml_tree
453
  result = run_pipeline(dna_input, similarity_score, build_ml_tree)
454
  cleanup_file(temp_file_path)
455
  return result
456
- except Exception as main_error: # Renamed from 'e'
457
  logger.error(f"Pipeline from file error: {main_error}", exc_info=True)
458
  cleanup_file(temp_file_path)
459
  error_msg = f"❌ Error: {str(main_error)}"
460
  return error_msg, "", "", "", "", None, None, None, None, error_msg, error_msg, None, None
 
461
  class AnalysisRequest(BaseModel):
462
  sequence: str
463
  similarity_score: float = 95.0
@@ -537,7 +525,7 @@ async def analyze_sequence(request: AnalysisRequest):
537
  success=False, error_message=str(e)
538
  )
539
 
540
- @app.post("/analyze-file")
541
  async def analyze_file(
542
  file: UploadFile = File(...),
543
  similarity_score: float = Form(95.0),
@@ -550,11 +538,7 @@ async def analyze_file(
550
  temp_file.write(content)
551
  temp_file_path = temp_file.name
552
  result = await run_pipeline_from_file(temp_file_path, similarity_score, build_ml_tree)
553
- if temp_file_path and os.path.exists(temp_file_path):
554
- try:
555
- os.unlink(temp_file_path)
556
- except Exception as cleanup_error:
557
- logger.warning(f"Failed to clean up {temp_file_path}: {cleanup_error}")
558
  return AnalysisResponse(
559
  boundary_output=result[0] or "",
560
  keras_output=result[1] or "",
@@ -565,20 +549,16 @@ async def analyze_file(
565
  report_html_path=result[12],
566
  success=True
567
  )
568
- except Exception as main_error: # Renamed from 'e'
569
  logger.error(f"Analyze-file error: {main_error}", exc_info=True)
570
- if temp_file_path and os.path.exists(temp_file_path):
571
- try:
572
- os.unlink(temp_file_path)
573
- except Exception as cleanup_error:
574
- logger.warning(f"Failed to clean up {temp_file_path}: {cleanup_error}")
575
  return AnalysisResponse(
576
  boundary_output="", keras_output="", ml_tree_output="",
577
  tree_analysis_output="", summary_output="",
578
  tree_html_path=None, report_html_path=None,
579
  success=False, error_message=str(main_error)
580
  )
581
-
582
  @app.get("/download/{file_type}/{query_id}")
583
  async def download_file(file_type: str, query_id: str):
584
  try:
@@ -779,21 +759,48 @@ def create_gradio_interface():
779
  outputs=gr.Textbox(label="Error"),
780
  title="🧬 Gene Analysis Pipeline (Error Mode)"
781
  )
 
782
  # --- Application Startup ---
783
  def run_application():
784
  try:
785
  main_gradio_app = create_gradio_interface()
786
  main_gradio_app = gr.mount_gradio_app(app, main_gradio_app, path="/gradio")
787
- # ... (logging and uvicorn.run)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
788
  except Exception as main_error:
789
  logger.error(f"Application startup failed: {main_error}", exc_info=True)
790
  try:
791
  logger.info("🔄 Falling back to Gradio-only mode...")
792
  fallback_gradio_app = create_gradio_interface()
793
- # ... (launch fallback)
 
 
 
 
 
794
  except Exception as fallback_error:
795
  logger.error(f"Fallback failed: {fallback_error}", exc_info=True)
796
  print("❌ Application failed to start. Check logs for details.")
 
797
  if __name__ == "__main__":
798
  print("🧬 Gene Analysis Pipeline Starting...")
799
  print("=" * 50)
 
217
  logger.warning(f"Failed to clean up {file_path}: {cleanup_error}")
218
 
219
  def phylogenetic_placement(sequence: str, mafft_cmd: str, iqtree_cmd: str):
220
+ query_fasta = None
221
  try:
 
222
  if len(sequence.strip()) < 100:
223
  return False, "Sequence too short (<100 bp).", None, None
 
 
224
  query_id = f"QUERY_{uuid.uuid4().hex[:8]}"
225
  query_fasta = os.path.join(QUERY_OUTPUT_DIR, f"{query_id}.fa")
226
  aligned_with_query = os.path.join(QUERY_OUTPUT_DIR, f"{query_id}_aligned.fa")
227
  output_prefix = os.path.join(QUERY_OUTPUT_DIR, f"{query_id}_placed_tree")
 
 
228
  if not os.path.exists(ALIGNMENT_PATH) or not os.path.exists(TREE_PATH):
229
  cleanup_file(query_fasta)
230
  return False, "Reference alignment or tree not found.", None, None
 
 
231
  query_record = SeqRecord(Seq(sequence.upper()), id=query_id, description="")
232
  SeqIO.write([query_record], query_fasta, "fasta")
 
 
233
  with open(aligned_with_query, "w") as output_file:
234
  result = subprocess.run(
235
  [mafft_cmd, "--add", query_fasta, "--reorder", ALIGNMENT_PATH],
 
242
  if not os.path.exists(aligned_with_query) or os.path.getsize(aligned_with_query) == 0:
243
  cleanup_file(query_fasta)
244
  return False, "MAFFT alignment failed.", None, None
 
 
245
  result = subprocess.run(
246
  [iqtree_cmd, "-s", aligned_with_query, "-g", TREE_PATH, "-m", "GTR+G", "-pre", output_prefix, "-redo"],
247
  capture_output=True,
 
253
  if not os.path.exists(treefile):
254
  cleanup_file(query_fasta)
255
  return False, "IQ-TREE placement failed.", aligned_with_query, None
 
 
256
  success_msg = f"Placement completed!\nQuery ID: {query_id}\nAlignment: {os.path.basename(aligned_with_query)}\nTree: {os.path.basename(treefile)}"
257
  cleanup_file(query_fasta)
258
  return True, success_msg, aligned_with_query, treefile
 
259
  except Exception as main_error:
260
  logger.error(f"Phylogenetic placement failed: {main_error}", exc_info=True)
261
  cleanup_file(query_fasta)
262
  return False, f"Error: {str(main_error)}", None, None
263
+
264
  def analyze_sequence_for_tree(sequence: str, matching_percentage: float):
265
  try:
266
  logger.debug("Starting tree analysis...")
 
440
  result = run_pipeline(dna_input, similarity_score, build_ml_tree)
441
  cleanup_file(temp_file_path)
442
  return result
443
+ except Exception as main_error:
444
  logger.error(f"Pipeline from file error: {main_error}", exc_info=True)
445
  cleanup_file(temp_file_path)
446
  error_msg = f"❌ Error: {str(main_error)}"
447
  return error_msg, "", "", "", "", None, None, None, None, error_msg, error_msg, None, None
448
+
449
  class AnalysisRequest(BaseModel):
450
  sequence: str
451
  similarity_score: float = 95.0
 
525
  success=False, error_message=str(e)
526
  )
527
 
528
+ @app.post("/analyze-file", response_model=AnalysisResponse)
529
  async def analyze_file(
530
  file: UploadFile = File(...),
531
  similarity_score: float = Form(95.0),
 
538
  temp_file.write(content)
539
  temp_file_path = temp_file.name
540
  result = await run_pipeline_from_file(temp_file_path, similarity_score, build_ml_tree)
541
+ cleanup_file(temp_file_path)
 
 
 
 
542
  return AnalysisResponse(
543
  boundary_output=result[0] or "",
544
  keras_output=result[1] or "",
 
549
  report_html_path=result[12],
550
  success=True
551
  )
552
+ except Exception as main_error:
553
  logger.error(f"Analyze-file error: {main_error}", exc_info=True)
554
+ cleanup_file(temp_file_path)
 
 
 
 
555
  return AnalysisResponse(
556
  boundary_output="", keras_output="", ml_tree_output="",
557
  tree_analysis_output="", summary_output="",
558
  tree_html_path=None, report_html_path=None,
559
  success=False, error_message=str(main_error)
560
  )
561
+
562
  @app.get("/download/{file_type}/{query_id}")
563
  async def download_file(file_type: str, query_id: str):
564
  try:
 
759
  outputs=gr.Textbox(label="Error"),
760
  title="🧬 Gene Analysis Pipeline (Error Mode)"
761
  )
762
+
763
  # --- Application Startup ---
764
  def run_application():
765
  try:
766
  main_gradio_app = create_gradio_interface()
767
  main_gradio_app = gr.mount_gradio_app(app, main_gradio_app, path="/gradio")
768
+ logger.info("🧬 Gene Analysis Pipeline Starting...")
769
+ logger.info("=" * 50)
770
+ logger.info("🔍 Checking system components...")
771
+ logger.info(f"🤖 Boundary Model: {'✅ Loaded' if boundary_model else '❌ Missing'}")
772
+ logger.info(f"🧠 Keras Model: {'✅ Loaded' if keras_model else '❌ Missing'}")
773
+ logger.info(f"🌳 Tree Analyzer: {'✅ Loaded' if analyzer else '❌ Missing'}")
774
+ mafft_available, iqtree_available, _, _ = check_tool_availability()
775
+ logger.info(f"🧬 MAFFT: {'✅ Available' if mafft_available else '❌ Missing'}")
776
+ logger.info(f"🌲 IQ-TREE: {'✅ Available' if iqtree_available else '❌ Missing'}")
777
+ logger.info("=" * 50)
778
+ logger.info("🚀 Starting Gene Analysis Pipeline...")
779
+ logger.warning("⚠️ Running without request queuing. Concurrent requests may block.")
780
+ logger.info("📊 FastAPI docs available at: http://localhost:7860/docs")
781
+ logger.info("🧬 Gradio interface available at: http://localhost:7860/gradio")
782
+ uvicorn.run(
783
+ app,
784
+ host="0.0.0.0",
785
+ port=7860,
786
+ log_level="info",
787
+ access_log=True
788
+ )
789
  except Exception as main_error:
790
  logger.error(f"Application startup failed: {main_error}", exc_info=True)
791
  try:
792
  logger.info("🔄 Falling back to Gradio-only mode...")
793
  fallback_gradio_app = create_gradio_interface()
794
+ logger.info("🧬 Gradio interface available at: http://localhost:7860")
795
+ fallback_gradio_app.launch(
796
+ server_name="0.0.0.0",
797
+ server_port=7860,
798
+ prevent_thread_lock=True
799
+ )
800
  except Exception as fallback_error:
801
  logger.error(f"Fallback failed: {fallback_error}", exc_info=True)
802
  print("❌ Application failed to start. Check logs for details.")
803
+
804
  if __name__ == "__main__":
805
  print("🧬 Gene Analysis Pipeline Starting...")
806
  print("=" * 50)