re-type commited on
Commit
8782d3c
·
verified ·
1 Parent(s): 9c6bf2a

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +180 -75
app.py CHANGED
@@ -520,9 +520,12 @@ async def analyze_file(
520
  def create_gradio_interface():
521
  logger.info("Attempting to create Gradio interface...")
522
  try:
 
 
 
523
  with gr.Blocks(
524
  title="🧬 Gene Analysis Pipeline",
525
- theme=gr.themes.Default(), # Use proper theme object
526
  css="""
527
  .gradio-container { max-width: 1200px !important; }
528
  .status-box { padding: 10px; border-radius: 5px; margin: 5px 0; }
@@ -533,26 +536,36 @@ def create_gradio_interface():
533
  ) as iface:
534
  logger.info("Gradio Blocks initialized")
535
  gr.Markdown("# 🧬 Gene Analysis Pipeline")
 
 
536
  with gr.Row():
537
  with gr.Column():
 
 
 
 
 
538
  status_display = gr.HTML(value=f"""
539
  <div class="status-box">
540
  <h3>🔧 System Status</h3>
541
  <p>🤖 Boundary Model: {'✅ Loaded' if boundary_model else '❌ Missing'}</p>
542
  <p>🧠 Keras Model: {'✅ Loaded' if keras_model else '❌ Missing'}</p>
543
  <p>🌳 Tree Analyzer: {'✅ Loaded' if analyzer else '❌ Missing'}</p>
544
- <p>🧬 MAFFT: {'✅ Available' if check_tool_availability()[0] else '❌ Missing'}</p>
545
- <p>🌲 IQ-TREE: {'✅ Available' if check_tool_availability()[1] else '❌ Missing'}</p>
546
  </div>
547
  """)
 
 
548
  with gr.Tabs():
549
  with gr.TabItem("📝 Text Input"):
550
  with gr.Row():
551
  with gr.Column(scale=2):
552
  dna_input = gr.Textbox(
553
- label="🧬 DNA Sequence - Paste your DNA sequence here",
554
  placeholder="Enter DNA sequence (ATCG format)...",
555
- lines=5
 
556
  )
557
  with gr.Column(scale=1):
558
  similarity_score = gr.Slider(
@@ -560,18 +573,19 @@ def create_gradio_interface():
560
  maximum=99,
561
  value=95.0,
562
  step=1.0,
563
- label="🎯 Similarity Threshold (%) - Minimum similarity for tree analysis"
564
  )
565
  build_ml_tree = gr.Checkbox(
566
- label="🌲 Build ML Tree - Generate phylogenetic placement (slower)",
567
  value=False
568
  )
569
  analyze_btn = gr.Button("🔬 Analyze Sequence", variant="primary")
 
570
  with gr.TabItem("📁 File Upload"):
571
  with gr.Row():
572
  with gr.Column(scale=2):
573
  file_input = gr.File(
574
- label="📄 Upload FASTA File - Upload a FASTA file containing your sequence",
575
  file_types=[".fasta", ".fa", ".fas", ".txt"]
576
  )
577
  with gr.Column(scale=1):
@@ -580,14 +594,18 @@ def create_gradio_interface():
580
  maximum=99,
581
  value=95.0,
582
  step=1.0,
583
- label="🎯 Similarity Threshold (%) - Minimum similarity for tree analysis"
584
  )
585
  file_build_ml_tree = gr.Checkbox(
586
- label="🌲 Build ML Tree - Generate phylogenetic placement (slower)",
587
  value=False
588
  )
589
  analyze_file_btn = gr.Button("🔬 Analyze File", variant="primary")
 
 
590
  gr.Markdown("## 📊 Analysis Results")
 
 
591
  with gr.Row():
592
  with gr.Column():
593
  boundary_output = gr.Textbox(
@@ -611,47 +629,95 @@ def create_gradio_interface():
611
  interactive=False,
612
  lines=2
613
  )
 
 
614
  summary_output = gr.Textbox(
615
  label="📋 Summary",
616
  interactive=False,
617
  lines=8
618
  )
 
 
619
  with gr.Row():
620
  aligned_file = gr.File(label="📄 Alignment File", visible=False)
621
  tree_file = gr.File(label="🌲 Tree File", visible=False)
 
 
622
  with gr.Tabs():
623
  with gr.TabItem("🌳 Interactive Tree"):
624
  tree_html = gr.HTML(
625
- label="Phylogenetic Tree",
626
  value="<div style='text-align: center; padding: 20px; color: #666;'>No tree generated yet.</div>"
627
  )
628
  with gr.TabItem("📊 Detailed Report"):
629
  report_html = gr.HTML(
630
- label="Analysis Report",
631
  value="<div style='text-align: center; padding: 20px; color: #666;'>No report generated yet.</div>"
632
  )
633
 
634
- # Event handlers
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
635
  analyze_btn.click(
636
- fn=run_pipeline,
637
  inputs=[dna_input, similarity_score, build_ml_tree],
638
  outputs=[
639
- boundary_output, keras_output, ml_tree_output, tree_analysis_output,
640
- summary_output, aligned_file, tree_file, gr.File(visible=False),
641
- gr.File(visible=False), tree_html, report_html
 
 
 
 
 
 
 
 
642
  ]
643
  )
644
 
645
  analyze_file_btn.click(
646
- fn=run_pipeline_from_file,
647
  inputs=[file_input, file_similarity_score, file_build_ml_tree],
648
  outputs=[
649
- boundary_output, keras_output, ml_tree_output, tree_analysis_output,
650
- summary_output, aligned_file, tree_file, gr.File(visible=False),
651
- gr.File(visible=False), tree_html, report_html
 
 
 
 
 
 
 
 
652
  ]
653
  )
654
 
 
655
  gr.Markdown("""
656
  ## 📖 Instructions
657
 
@@ -685,26 +751,81 @@ def create_gradio_interface():
685
 
686
  except Exception as e:
687
  logger.error(f"❌ Failed to create Gradio interface: {e}")
688
- # Fallback simple interface
 
 
689
  try:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
690
  simple_iface = gr.Interface(
691
- fn=lambda x: f"Error creating interface: {str(e)}",
692
- inputs=gr.Textbox(label="Input"),
693
- outputs=gr.Textbox(label="Output"),
694
- title="🧬 Gene Analysis Pipeline - Error"
 
 
 
 
 
 
 
 
695
  )
696
  logger.info("⚠️ Fallback interface created")
697
  return simple_iface
 
698
  except Exception as fallback_error:
699
  logger.error(f"❌ Even fallback interface failed: {fallback_error}")
700
  return None
701
 
702
- # --- App Mounting and Launch ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
703
  def main():
704
- """Main function to launch the application."""
705
  logger.info("🚀 Starting Gene Analysis Pipeline...")
706
 
707
- # Check if we should run in simple mode (environment variable)
708
  simple_mode = os.environ.get("SIMPLE_MODE", "false").lower() == "true"
709
 
710
  if simple_mode:
@@ -713,48 +834,48 @@ def main():
713
  return
714
 
715
  try:
716
- # Create Gradio interface
717
  gradio_app = create_gradio_interface()
718
 
719
  if gradio_app is None:
720
- logger.error("❌ Failed to create Gradio interface")
 
721
  return
722
 
723
- # Mount Gradio app to FastAPI
724
  try:
725
- # Use the proper Gradio mounting method
726
- gradio_app = gr.mount_gradio_app(app, gradio_app, path="/gradio")
727
  logger.info("✅ Gradio mounted to FastAPI at /gradio")
728
- except Exception as e:
729
- logger.error(f"❌ Failed to mount Gradio: {e}")
730
- # Try alternative approach - launch separately
731
- try:
732
- # Launch Gradio in a separate thread
733
- import threading
734
-
735
- def launch_gradio():
 
736
  gradio_app.launch(
737
  server_name="0.0.0.0",
738
- server_port=7861, # Different port to avoid conflicts
739
  share=False,
740
  debug=False,
741
  prevent_thread_lock=True
742
  )
743
-
744
- gradio_thread = threading.Thread(target=launch_gradio, daemon=True)
745
- gradio_thread.start()
746
- logger.info("✅ Gradio launched on separate port 7861")
747
- except Exception as alt_error:
748
- logger.error(f"❌ Alternative Gradio launch failed: {alt_error}")
749
 
750
  # Launch configuration
751
  port = int(os.environ.get("PORT", 7860))
752
  host = "0.0.0.0"
753
 
754
  logger.info(f"🌐 Launching FastAPI on {host}:{port}")
755
- logger.info(f"📱 FastAPI docs: http://{host}:{port}/docs")
756
- logger.info(f"🧬 Gradio interface: http://{host}:7861 (if mounted separately)")
757
- logger.info(f"🧬 Gradio interface: http://{host}:{port}/gradio (if mounted successfully)")
758
 
759
  # Launch the server
760
  uvicorn.run(
@@ -763,34 +884,18 @@ def main():
763
  port=port,
764
  log_level="info",
765
  access_log=True,
766
- reload=False # Set to True for development
767
  )
768
 
769
  except Exception as e:
770
- logger.error(f"❌ Failed to launch application: {e}")
771
- # Fallback: Launch only Gradio
772
  try:
773
- logger.info("🔄 Attempting fallback Gradio-only launch...")
774
  launch_gradio_only()
775
- except Exception as fallback_error:
776
- logger.error(f"❌ Fallback launch failed: {fallback_error}")
777
-
778
- if __name__ == "__main__":
779
- main()
780
-
781
- # Alternative entry points for different deployment scenarios
782
- def launch_gradio_only():
783
- """Launch only Gradio interface (for simpler deployments)"""
784
- logger.info("🚀 Launching Gradio-only mode...")
785
- gradio_app = create_gradio_interface()
786
- if gradio_app:
787
- gradio_app.launch(
788
- server_name="0.0.0.0",
789
- server_port=int(os.environ.get("PORT", 7860)),
790
- share=False,
791
- debug=False
792
- )
793
-
794
  def launch_fastapi_only():
795
  """Launch only FastAPI (for API-only deployments)"""
796
  logger.info("🚀 Launching FastAPI-only mode...")
 
520
  def create_gradio_interface():
521
  logger.info("Attempting to create Gradio interface...")
522
  try:
523
+ # Use a more basic theme to avoid compatibility issues
524
+ theme = gr.themes.Base()
525
+
526
  with gr.Blocks(
527
  title="🧬 Gene Analysis Pipeline",
528
+ theme=theme,
529
  css="""
530
  .gradio-container { max-width: 1200px !important; }
531
  .status-box { padding: 10px; border-radius: 5px; margin: 5px 0; }
 
536
  ) as iface:
537
  logger.info("Gradio Blocks initialized")
538
  gr.Markdown("# 🧬 Gene Analysis Pipeline")
539
+
540
+ # System Status
541
  with gr.Row():
542
  with gr.Column():
543
+ try:
544
+ mafft_avail, iqtree_avail, _, _ = check_tool_availability()
545
+ except:
546
+ mafft_avail, iqtree_avail = False, False
547
+
548
  status_display = gr.HTML(value=f"""
549
  <div class="status-box">
550
  <h3>🔧 System Status</h3>
551
  <p>🤖 Boundary Model: {'✅ Loaded' if boundary_model else '❌ Missing'}</p>
552
  <p>🧠 Keras Model: {'✅ Loaded' if keras_model else '❌ Missing'}</p>
553
  <p>🌳 Tree Analyzer: {'✅ Loaded' if analyzer else '❌ Missing'}</p>
554
+ <p>🧬 MAFFT: {'✅ Available' if mafft_avail else '❌ Missing'}</p>
555
+ <p>🌲 IQ-TREE: {'✅ Available' if iqtree_avail else '❌ Missing'}</p>
556
  </div>
557
  """)
558
+
559
+ # Input Tabs
560
  with gr.Tabs():
561
  with gr.TabItem("📝 Text Input"):
562
  with gr.Row():
563
  with gr.Column(scale=2):
564
  dna_input = gr.Textbox(
565
+ label="🧬 DNA Sequence",
566
  placeholder="Enter DNA sequence (ATCG format)...",
567
+ lines=5,
568
+ max_lines=10
569
  )
570
  with gr.Column(scale=1):
571
  similarity_score = gr.Slider(
 
573
  maximum=99,
574
  value=95.0,
575
  step=1.0,
576
+ label="🎯 Similarity Threshold (%)"
577
  )
578
  build_ml_tree = gr.Checkbox(
579
+ label="🌲 Build ML Tree",
580
  value=False
581
  )
582
  analyze_btn = gr.Button("🔬 Analyze Sequence", variant="primary")
583
+
584
  with gr.TabItem("📁 File Upload"):
585
  with gr.Row():
586
  with gr.Column(scale=2):
587
  file_input = gr.File(
588
+ label="📄 Upload FASTA File",
589
  file_types=[".fasta", ".fa", ".fas", ".txt"]
590
  )
591
  with gr.Column(scale=1):
 
594
  maximum=99,
595
  value=95.0,
596
  step=1.0,
597
+ label="🎯 Similarity Threshold (%)"
598
  )
599
  file_build_ml_tree = gr.Checkbox(
600
+ label="🌲 Build ML Tree",
601
  value=False
602
  )
603
  analyze_file_btn = gr.Button("🔬 Analyze File", variant="primary")
604
+
605
+ # Results Section
606
  gr.Markdown("## 📊 Analysis Results")
607
+
608
+ # Main Results
609
  with gr.Row():
610
  with gr.Column():
611
  boundary_output = gr.Textbox(
 
629
  interactive=False,
630
  lines=2
631
  )
632
+
633
+ # Summary
634
  summary_output = gr.Textbox(
635
  label="📋 Summary",
636
  interactive=False,
637
  lines=8
638
  )
639
+
640
+ # File outputs (hidden by default)
641
  with gr.Row():
642
  aligned_file = gr.File(label="📄 Alignment File", visible=False)
643
  tree_file = gr.File(label="🌲 Tree File", visible=False)
644
+
645
+ # Visualization Tabs
646
  with gr.Tabs():
647
  with gr.TabItem("🌳 Interactive Tree"):
648
  tree_html = gr.HTML(
 
649
  value="<div style='text-align: center; padding: 20px; color: #666;'>No tree generated yet.</div>"
650
  )
651
  with gr.TabItem("📊 Detailed Report"):
652
  report_html = gr.HTML(
 
653
  value="<div style='text-align: center; padding: 20px; color: #666;'>No report generated yet.</div>"
654
  )
655
 
656
+ # Create a wrapper function to handle the outputs properly
657
+ def run_pipeline_wrapper(*args):
658
+ try:
659
+ result = run_pipeline(*args)
660
+ # Ensure we return exactly 11 outputs
661
+ if len(result) < 11:
662
+ # Pad with default values
663
+ result = list(result) + [None] * (11 - len(result))
664
+ return result[:11] # Truncate if too many
665
+ except Exception as e:
666
+ logger.error(f"Pipeline wrapper error: {e}")
667
+ error_msg = f"❌ Error: {str(e)}"
668
+ return (error_msg, "", "", "", "", None, None, None, None, error_msg, error_msg)
669
+
670
+ def run_pipeline_from_file_wrapper(*args):
671
+ try:
672
+ result = run_pipeline_from_file(*args)
673
+ # Ensure we return exactly 11 outputs
674
+ if len(result) < 11:
675
+ # Pad with default values
676
+ result = list(result) + [None] * (11 - len(result))
677
+ return result[:11] # Truncate if too many
678
+ except Exception as e:
679
+ logger.error(f"Pipeline file wrapper error: {e}")
680
+ error_msg = f"❌ Error: {str(e)}"
681
+ return (error_msg, "", "", "", "", None, None, None, None, error_msg, error_msg)
682
+
683
+ # Event handlers with explicit output specification
684
  analyze_btn.click(
685
+ fn=run_pipeline_wrapper,
686
  inputs=[dna_input, similarity_score, build_ml_tree],
687
  outputs=[
688
+ boundary_output,
689
+ keras_output,
690
+ ml_tree_output,
691
+ tree_analysis_output,
692
+ summary_output,
693
+ aligned_file,
694
+ tree_file,
695
+ gr.File(visible=False), # dummy output 1
696
+ gr.File(visible=False), # dummy output 2
697
+ tree_html,
698
+ report_html
699
  ]
700
  )
701
 
702
  analyze_file_btn.click(
703
+ fn=run_pipeline_from_file_wrapper,
704
  inputs=[file_input, file_similarity_score, file_build_ml_tree],
705
  outputs=[
706
+ boundary_output,
707
+ keras_output,
708
+ ml_tree_output,
709
+ tree_analysis_output,
710
+ summary_output,
711
+ aligned_file,
712
+ tree_file,
713
+ gr.File(visible=False), # dummy output 1
714
+ gr.File(visible=False), # dummy output 2
715
+ tree_html,
716
+ report_html
717
  ]
718
  )
719
 
720
+ # Instructions
721
  gr.Markdown("""
722
  ## 📖 Instructions
723
 
 
751
 
752
  except Exception as e:
753
  logger.error(f"❌ Failed to create Gradio interface: {e}")
754
+ logger.error(f"Error details: {type(e).__name__}: {str(e)}")
755
+
756
+ # Enhanced fallback interface
757
  try:
758
+ def simple_analysis(sequence):
759
+ if not sequence:
760
+ return "Please enter a DNA sequence"
761
+
762
+ try:
763
+ # Basic sequence validation
764
+ sequence = sequence.upper().strip()
765
+ valid_chars = set('ATCGN')
766
+ if not all(c in valid_chars for c in sequence):
767
+ return "Invalid characters in sequence. Use only A, T, C, G, N"
768
+
769
+ # Try basic analysis
770
+ if len(sequence) < 10:
771
+ return "Sequence too short (minimum 10 bp)"
772
+
773
+ # Simple analysis result
774
+ result = f"""
775
+ ✅ Sequence Analysis Complete
776
+ Length: {len(sequence)} bp
777
+ GC Content: {((sequence.count('G') + sequence.count('C')) / len(sequence) * 100):.1f}%
778
+
779
+ Note: Full analysis pipeline unavailable due to interface error.
780
+ Error: {str(e)}
781
+ """
782
+ return result
783
+
784
+ except Exception as analysis_error:
785
+ return f"Analysis error: {str(analysis_error)}"
786
+
787
  simple_iface = gr.Interface(
788
+ fn=simple_analysis,
789
+ inputs=gr.Textbox(
790
+ label="🧬 DNA Sequence",
791
+ placeholder="Enter DNA sequence (ATCG format)...",
792
+ lines=5
793
+ ),
794
+ outputs=gr.Textbox(
795
+ label="📊 Analysis Result",
796
+ lines=10
797
+ ),
798
+ title="🧬 Gene Analysis Pipeline - Fallback Mode",
799
+ description=f"Simplified interface due to error: {str(e)}"
800
  )
801
  logger.info("⚠️ Fallback interface created")
802
  return simple_iface
803
+
804
  except Exception as fallback_error:
805
  logger.error(f"❌ Even fallback interface failed: {fallback_error}")
806
  return None
807
 
808
+ # Additional helper function to handle Gradio mounting issues
809
+ def safe_gradio_mount(app, gradio_app, path="/gradio"):
810
+ """Safely mount Gradio app with error handling"""
811
+ try:
812
+ # Try the standard mounting approach
813
+ if hasattr(gr, 'mount_gradio_app'):
814
+ return gr.mount_gradio_app(app, gradio_app, path=path)
815
+ else:
816
+ # Alternative approach for older Gradio versions
817
+ gradio_app.mount_path = path
818
+ return app.mount(path, gradio_app.app)
819
+ except Exception as e:
820
+ logger.error(f"Failed to mount Gradio: {e}")
821
+ raise e
822
+
823
+ # Enhanced main function with better error handling
824
  def main():
825
+ """Main function to launch the application with enhanced error handling."""
826
  logger.info("🚀 Starting Gene Analysis Pipeline...")
827
 
828
+ # Check if we should run in simple mode
829
  simple_mode = os.environ.get("SIMPLE_MODE", "false").lower() == "true"
830
 
831
  if simple_mode:
 
834
  return
835
 
836
  try:
837
+ # Create Gradio interface with enhanced error handling
838
  gradio_app = create_gradio_interface()
839
 
840
  if gradio_app is None:
841
+ logger.error("❌ Failed to create Gradio interface, launching API-only mode")
842
+ launch_fastapi_only()
843
  return
844
 
845
+ # Try to mount Gradio app to FastAPI
846
  try:
847
+ safe_gradio_mount(app, gradio_app, path="/gradio")
 
848
  logger.info("✅ Gradio mounted to FastAPI at /gradio")
849
+ except Exception as mount_error:
850
+ logger.error(f"❌ Failed to mount Gradio: {mount_error}")
851
+ logger.info("🔄 Launching Gradio and FastAPI separately...")
852
+
853
+ # Launch Gradio in a separate thread
854
+ import threading
855
+
856
+ def launch_gradio_separate():
857
+ try:
858
  gradio_app.launch(
859
  server_name="0.0.0.0",
860
+ server_port=7861,
861
  share=False,
862
  debug=False,
863
  prevent_thread_lock=True
864
  )
865
+ except Exception as gradio_error:
866
+ logger.error(f"❌ Separate Gradio launch failed: {gradio_error}")
867
+
868
+ gradio_thread = threading.Thread(target=launch_gradio_separate, daemon=True)
869
+ gradio_thread.start()
870
+ logger.info("✅ Gradio launched on separate port 7861")
871
 
872
  # Launch configuration
873
  port = int(os.environ.get("PORT", 7860))
874
  host = "0.0.0.0"
875
 
876
  logger.info(f"🌐 Launching FastAPI on {host}:{port}")
877
+ logger.info(f"📱 API docs available at: http://{host}:{port}/docs")
878
+ logger.info(f"🧬 Gradio interface at: http://{host}:{port}/gradio")
 
879
 
880
  # Launch the server
881
  uvicorn.run(
 
884
  port=port,
885
  log_level="info",
886
  access_log=True,
887
+ reload=False
888
  )
889
 
890
  except Exception as e:
891
+ logger.error(f"❌ Main application launch failed: {e}")
892
+ # Final fallback: Launch only Gradio
893
  try:
894
+ logger.info("🔄 Final fallback: Gradio-only launch...")
895
  launch_gradio_only()
896
+ except Exception as final_error:
897
+ logger.error(f"❌ Final fallback failed: {final_error}")
898
+ logger.error("❌ All launch methods failed. Check your environment and dependencies.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
899
  def launch_fastapi_only():
900
  """Launch only FastAPI (for API-only deployments)"""
901
  logger.info("🚀 Launching FastAPI-only mode...")