# ============================================================================= # app.py (main entry point) # ============================================================================= import logging import os # Ensure logs folder exists os.makedirs("logs", exist_ok=True) # Configure logging logging.basicConfig( level=logging.DEBUG, # or INFO if you want less noise format="%(asctime)s [%(levelname)s] %(name)s: %(message)s", handlers=[ logging.FileHandler("logs/app.log", mode="a"), # Log to file logging.StreamHandler() # Still show in Spaces Logs tab ] ) logger = logging.getLogger(__name__) # Example usage logger.info("App started") logger.debug("This is debug info") import gradio as gr from data_handler import DataHandler from gradio_callbacks import GradioCallbacks from gradio_components import ( create_qa_tab, create_filters_tab, create_about_tab ) def main(): # Initialize data data_handler = DataHandler() callbacks = GradioCallbacks(data_handler) data = data_handler.get_data() # Create Gradio app with gr.Blocks( theme=gr.themes.Monochrome(), title="FCAS Research Methods Evidence Mapping", css=""" @import url('https://fonts.googleapis.com/css2?family=Libre+Franklin:wght@400;700&display=swap'); :root { --primary: #012069; --secondary: #5E548E; --accent: #231942; --text: #686868; --background: #F9F9F9; --heading-font: 'Libre Franklin', Sans-serif; --body-font: 'Helvetica Neue', Arial, Helvetica, sans-serif; } .gradio-container { max-width: 1200px !important; font-family: var(--body-font); background-color: var(--background); color: var(--text); } /* Make Gradio primary buttons navy blue with white text */ .gr-button, .gr-button-primary, button[class*="primary"] { background: #012069 !important; color: #fff !important; border: none !important; font-family: var(--body-font); } h1, h2, h3, .main-header { font-family: var(--heading-font); color: var(--primary); } .main-header { background: var(--secondary); color: var(--background); padding: 2rem; border-radius: 10px; margin-bottom: 2rem; } .gr-button { background: var(--accent); color: var(--background); font-family: var(--body-font); } /* Synthesis and references styling */ #synthesis_md, #references_md { background: linear-gradient(90deg, #ffffff 0%, #fbfdff 100%); border: 1px solid rgba(1,32,105,0.06); padding: 16px; border-radius: 8px; box-shadow: 0 1px 4px rgba(2,6,23,0.02); margin-bottom: 0.75rem; } #synthesis_md h1, #synthesis_md h2, #synthesis_md h3 { color: var(--primary); margin-top: 0; } """ ) as app: # Shared state for Q&A synthesis qa_synthesis_state = gr.State("") with gr.Tabs(): create_qa_tab(callbacks, qa_synthesis_state) create_filters_tab(callbacks, data['countries_list'], data['sectors_list']) create_about_tab() return app if __name__ == "__main__": import argparse parser = argparse.ArgumentParser(description='FCAS app launcher and utilities') parser.add_argument('--gemini-multi', action='store_true', help='Run a one-off Gemini multi-speaker TTS generation (CLI mode)') parser.add_argument('--text', type=str, default=None, help='Text to synthesize (use with --gemini-multi)') parser.add_argument('--file', type=str, default=None, help='Path to a text file to synthesize (use with --gemini-multi)') parser.add_argument('--use-stub', action='store_true', help='Set USE_TTS_STUB=1 for offline testing') args = parser.parse_args() if args.gemini_multi: # CLI-only path: run simplified Gemini multi-speaker generation and exit if args.use_stub: os.environ['USE_TTS_STUB'] = '1' # Load data handler and callbacks like the app would dh = DataHandler() cb = GradioCallbacks(dh) # Obtain text to synthesize synth_text = None if args.text: synth_text = args.text elif args.file: try: with open(args.file, 'r', encoding='utf-8') as f: synth_text = f.read() except Exception as e: logger.error('Failed to read input file: %s', e) raise else: logger.error('No --text or --file provided for --gemini-multi. Use --text "..." or --file path.') raise SystemExit(2) logger.info('Running Gemini multi-speaker generation (CLI)') out_path, err, provider, tts_used = cb.generate_podcast_episode(synth_text) print('Result:', (out_path, err, provider, tts_used)) raise SystemExit(0) else: # Auto-run mode: if AUTO_RUN_GEMINI=1 is set in the environment, run a single # Gemini multi-speaker generation (useful for CI or scripting) and exit. if os.environ.get('AUTO_RUN_GEMINI', '0') in ('1', 'true', 'True'): # Load text from PODCAST_TEXT env or from podcast_input.txt if present synth_text = os.environ.get('PODCAST_TEXT') if not synth_text and os.path.exists('podcast_input.txt'): try: with open('podcast_input.txt', 'r', encoding='utf-8') as f: synth_text = f.read() except Exception as e: logger.error('Failed to read podcast_input.txt: %s', e) if not synth_text: logger.error('AUTO_RUN_GEMINI set but no PODCAST_TEXT or podcast_input.txt found.') raise SystemExit(2) dh = DataHandler() cb = GradioCallbacks(dh) logger.info('AUTO_RUN_GEMINI: running Gemini multi-speaker generation') out_path, err, provider, tts_used = cb.generate_podcast_episode(synth_text) print('Result:', (out_path, err, provider, tts_used)) raise SystemExit(0) app = main() app.launch( share=True, server_name="0.0.0.0", server_port=7860, show_error=True )