| import gradio as gr |
| import pandas as pd |
| from processing import process_eeg |
| from visuals import plot_eeg_signals, plot_hypnogram, plot_frequency_spectra, plot_band_waveforms |
| from rag import setup_rag, chat_with_llm, update_vectorstore_with_user_results |
| import os |
| import logging |
|
|
| |
| logging.basicConfig( |
| level=logging.INFO, |
| format='%(asctime)s - %(levelname)s - %(message)s', |
| handlers=[ |
| logging.FileHandler('neuronap.log'), |
| logging.StreamHandler() |
| ] |
| ) |
| logger = logging.getLogger(__name__) |
|
|
| def analyze_eeg(file): |
| logger.info("Starting EEG analysis") |
| if file is None: |
| logger.error("No CSV file uploaded") |
| raise ValueError("No CSV file uploaded.") |
| file_path = file.name |
| logger.info(f"Processing file: {file_path}") |
| eeg_uv, eeg_uv_bp, eeg_uv_notched, hypno, stats, features_df, hmm_labels, fs, time_s = process_eeg(file_path) |
| logger.info("EEG processing completed") |
| signals_img = plot_eeg_signals(time_s, eeg_uv, eeg_uv_bp, eeg_uv_notched) |
| hypno_img = plot_hypnogram(hypno) |
| spectra_img = plot_frequency_spectra(eeg_uv_notched, fs) |
| bands_img = plot_band_waveforms(eeg_uv_notched, fs) |
| |
| |
| key_metrics = { |
| 'TST': 'Total Sleep Time (minutes)', |
| 'SE': 'Sleep Efficiency (%)', |
| 'REM': 'REM Sleep (minutes)', |
| 'N3': 'Deep Sleep (N3) (minutes)', |
| 'WASO': 'Wake After Sleep Onset (minutes)' |
| } |
| |
| |
| stats_preview = {k: stats.get(k) for k in key_metrics.keys() if k in stats} |
| stats_preview_str = "\n".join([ |
| f"• {key_metrics[k]}: {v:.2f} {'%' if k in ['SE', '%N1', '%N2', '%N3', '%REM', '%NREM', 'SME'] else 'minutes'}" |
| for k, v in stats_preview.items() |
| ]) |
| |
| |
| stats_full_str = "\n".join([ |
| f"• {k.replace('_', ' ').title()}: {v:.2f} {'%' if k in ['SE', '%N1', '%N2', '%N3', '%REM', '%NREM', 'SME'] else 'minutes'}" |
| for k, v in stats.items() |
| ]) |
| |
| |
| stats_full = pd.DataFrame(list(stats.items()), columns=['Metric', 'Value']).to_html(classes='stats-table', index=False) |
| |
| |
| user_results = f"Sleep Statistics:\n{stats_full_str}\nHypnogram: {hypno}\n" |
| logger.info("Updating vectorstore with user results") |
| vectorstore = update_vectorstore_with_user_results(user_results) |
| qa_chain = setup_rag() |
| |
| |
| features_preview = features_df.head(5).to_html(classes='compact-table') |
| features_full = features_df.to_html(classes='compact-table') |
| clusters_preview = pd.DataFrame({'Cluster': hmm_labels}).head(5).to_html(classes='compact-table') |
| clusters_full = pd.DataFrame({'Cluster': hmm_labels}).to_html(classes='compact-table') |
| |
| logger.info("Analysis completed, returning outputs") |
| return ( |
| signals_img, hypno_img, spectra_img, bands_img, stats_preview_str, stats_full, |
| features_preview, features_full, clusters_preview, clusters_full, |
| qa_chain, |
| "clean_eeg.csv", "features.csv", "eeg_clusters.csv", |
| "eeg_signal_plot.pdf", "hypnogram_plot.pdf", "eeg_fft_welch.pdf", "eeg_bands_plot.pdf" |
| ) |
|
|
| def handle_chat(query, qa_chain): |
| logger.info(f"Handling chat query: {query}") |
| if not query or qa_chain is None: |
| logger.warning("Invalid query or no QA chain available") |
| return "Please analyze an EEG file first and provide a query." |
| return chat_with_llm(query, qa_chain) |
|
|
| with gr.Blocks(title="NeuroNap", css=".compact-table { font-size: 12px; max-height: 300px; overflow-y: auto; } .stats-table { font-size: 14px; border-collapse: collapse; width: 50%; } .stats-table th, .stats-table td { border: 1px solid #ddd; padding: 8px; text-align: left; }") as demo: |
| logger.info("Initializing Gradio interface") |
| qa_chain_state = gr.State(None) |
| gr.Markdown("# NeuroNap: EEG Sleep Monitoring System") |
| with gr.Column(): |
| with gr.Row(): |
| file_input = gr.File(label="Upload EEG CSV (e.g., EEGDATA.CSV)") |
| analyze_btn = gr.Button("Analyze") |
| with gr.Row(): |
| signals_output = gr.Image(label="EEG Signals") |
| hypno_output = gr.Image(label="Hypnogram") |
| with gr.Row(): |
| spectra_output = gr.Image(label="Frequency Spectra") |
| bands_output = gr.Image(label="Frequency Bands") |
| with gr.Group(): |
| gr.Markdown("### Sleep Statistics") |
| stats_preview_output = gr.Textbox(label="Key Sleep Statistics") |
| with gr.Accordion("Show All Sleep Statistics", open=False): |
| stats_full_output = gr.HTML(label="Full Statistics") |
| with gr.Group(): |
| gr.Markdown("### Extracted Features") |
| features_preview_output = gr.HTML(label="Features Preview") |
| with gr.Accordion("Show Full Features Table", open=False): |
| features_full_output = gr.HTML(label="Full Features") |
| with gr.Group(): |
| gr.Markdown("### HMM Clusters") |
| clusters_preview_output = gr.HTML(label="Clusters Preview") |
| with gr.Accordion("Show Full Clusters Table", open=False): |
| clusters_full_output = gr.HTML(label="Full Clusters") |
| with gr.Group(): |
| gr.Markdown("### Chat with NeuroNap") |
| chat_input = gr.Textbox(label="Ask about your sleep data", lines=2) |
| chat_btn = gr.Button("Send") |
| chat_output = gr.Textbox(label="LLM Response", lines=4) |
| with gr.Group(): |
| gr.Markdown("### Downloads") |
| with gr.Row(): |
| clean_csv = gr.File(label="Download clean_eeg.csv") |
| features_csv = gr.File(label="Download features.csv") |
| clusters_csv = gr.File(label="Download eeg_clusters.csv") |
| with gr.Row(): |
| signals_pdf = gr.File(label="Download eeg_signal_plot.pdf") |
| hypno_pdf = gr.File(label="Download hypnogram_plot.pdf") |
| spectra_pdf = gr.File(label="Download eeg_fft_welch.pdf") |
| bands_pdf = gr.File(label="Download eeg_bands_plot.pdf") |
| |
| analyze_btn.click( |
| analyze_eeg, |
| inputs=file_input, |
| outputs=[ |
| signals_output, hypno_output, spectra_output, bands_output, |
| stats_preview_output, stats_full_output, |
| features_preview_output, features_full_output, clusters_preview_output, clusters_full_output, |
| qa_chain_state, clean_csv, features_csv, clusters_csv, signals_pdf, hypno_pdf, spectra_pdf, bands_pdf |
| ] |
| ) |
| chat_btn.click(handle_chat, inputs=[chat_input, qa_chain_state], outputs=chat_output) |
|
|
| logger.info("Launching Gradio interface") |
| demo.launch(share=True) |