""" app.py — Gradio Blocks UI for Research Draft. Provides a clean, academic interface with two roles: • Student — upload PDF, generate abstract, view result. • Researcher — same as Student + view history, export results. Launch: python app.py """ import gradio as gr from abstract_service import generate_abstract_from_pdf from history_manager import ( get_history_as_table, clear_history, export_latest_to_txt, export_full_history_to_txt, ) # --------------------------------------------------------------------------- # CSS — minimal overrides for a clean academic look # --------------------------------------------------------------------------- CUSTOM_CSS = """ .main-title { text-align: center; margin-bottom: 0.2em; } .sub-title { text-align: center; color: #555; font-size: 0.95em; margin-top: 0; } """ # --------------------------------------------------------------------------- # Event handlers # --------------------------------------------------------------------------- def on_role_change(role: str): """Toggle visibility of the Researcher-only section.""" return gr.Column(visible=(role == "Researcher")) def on_generate(pdf_file, role: str): """ Handle the Generate Abstract button click. Returns: (abstract_text, status_message) """ if pdf_file is None: raise gr.Error("Please upload a PDF file first.") # Gradio file path — handle both v5 (.name) and v6 (.path) file_path = getattr(pdf_file, "path", None) or getattr(pdf_file, "name", None) if not file_path: raise gr.Error("Could not read the uploaded file path.") try: result = generate_abstract_from_pdf(file_path=file_path, role=role) except ValueError as exc: raise gr.Error(str(exc)) except RuntimeError as exc: raise gr.Error(str(exc)) except FileNotFoundError as exc: raise gr.Error(str(exc)) except Exception as exc: raise gr.Error(f"Unexpected error: {exc}") return result["abstract"], f"✅ {result['status']}" def on_view_history(): """Load history rows for the Dataframe.""" rows = get_history_as_table() if not rows: gr.Info("No history entries yet.") return rows def on_clear_history(): """Wipe the persistent history file and return an empty table.""" clear_history() gr.Info("History cleared.") return [] def on_export_latest(): """Export the latest abstract to a .txt download.""" path = export_latest_to_txt() if path is None: raise gr.Error("Nothing to export — history is empty.") return path def on_export_full(): """Export the entire history to a .txt download.""" path = export_full_history_to_txt() if path is None: raise gr.Error("Nothing to export — history is empty.") return path # --------------------------------------------------------------------------- # UI layout (Gradio Blocks) # --------------------------------------------------------------------------- with gr.Blocks(title="Research Draft") as demo: # ── Header ──────────────────────────────────────────────────────────── gr.Markdown( "
" "AI-powered academic abstract generation · Local & Private" "
", ) # ── Role selector ───────────────────────────────────────────────────── with gr.Row(): role_dropdown = gr.Dropdown( choices=["Student", "Researcher"], value="Student", label="Select your role", scale=1, interactive=True, ) gr.Markdown("---") # ── Main generation section (visible to all roles) ──────────────────── gr.Markdown("### Upload & Generate") with gr.Row(): with gr.Column(scale=2): pdf_input = gr.File( label="Upload Research Paper (PDF)", file_types=[".pdf"], file_count="single", ) generate_btn = gr.Button( "🔍 Generate Abstract", variant="primary", size="lg", ) status_box = gr.Textbox( label="Status", interactive=False, lines=1, placeholder="Upload a PDF and click Generate…", ) with gr.Column(scale=3): abstract_output = gr.Textbox( label="Generated Abstract", interactive=False, lines=12, buttons=["copy"], placeholder="Your generated abstract will appear here…", ) # ── Researcher-only section ─────────────────────────────────────────── with gr.Column(visible=False) as researcher_section: gr.Markdown("---") gr.Markdown("### 🔬 Researcher Tools") with gr.Row(): view_history_btn = gr.Button("📋 View History") clear_history_btn = gr.Button("🗑️ Clear History") export_latest_btn = gr.Button("📥 Export Latest") export_full_btn = gr.Button("📥 Export Full History") history_table = gr.Dataframe( headers=["Timestamp", "Role", "Filename", "Abstract (preview)"], datatype=["str", "str", "str", "str"], interactive=False, wrap=True, label="Generation History", ) export_file = gr.File( label="Download Export", interactive=False, ) # ── Footer ──────────────────────────────────────────────────────────── gr.Markdown( "