Spaces:
Sleeping
Sleeping
| import gradio as gr | |
| import pandas as pd | |
| import os | |
| from database import ( | |
| fetch_all_faq_metadata, fetch_all_podcast_metadata, | |
| add_faq_entry, update_faq_entry, delete_faq_entry, | |
| bulk_update_faqs, bulk_update_podcasts | |
| ) | |
| from utils import recalculate_all_embeddings | |
| from config import OPENAI_API_KEY | |
| from dotenv import load_dotenv | |
| # Load environment variables | |
| load_dotenv() | |
| # Basic Admin Credentials (MUST be set in Hugging Face Secrets or .env) | |
| ADMIN_USER = os.environ.get("ADMIN_USER", "admin") | |
| ADMIN_PASS = os.environ.get("ADMIN_PASS") | |
| if not ADMIN_PASS: | |
| raise ValueError("CRITICAL SECURITY ERROR: ADMIN_PASS environment variable is not set.") | |
| def get_faqs(): | |
| data = fetch_all_faq_metadata() | |
| return pd.DataFrame(data) | |
| def get_podcasts(): | |
| data = fetch_all_podcast_metadata() | |
| return pd.DataFrame(data) | |
| def handle_faq_upload(file): | |
| if file is None: | |
| return "No file uploaded." | |
| try: | |
| df = pd.read_csv(file.name) if file.name.endswith('.csv') else pd.read_excel(file.name) | |
| bulk_update_faqs(df.to_dict('records')) | |
| return f"Successfully uploaded {len(df)} FAQs. Don't forget to Sync & Embed!" | |
| except Exception as e: | |
| return f"Error: {e}" | |
| def handle_podcast_upload(file): | |
| if file is None: | |
| return "No file uploaded." | |
| try: | |
| df = pd.read_csv(file.name) if file.name.endswith('.csv') else pd.read_excel(file.name) | |
| bulk_update_podcasts(df.to_dict('records')) | |
| return f"Successfully uploaded {len(df)} Podcasts. Don't forget to Sync & Embed!" | |
| except Exception as e: | |
| return f"Error: {e}" | |
| def run_sync(): | |
| try: | |
| recalculate_all_embeddings() | |
| return "Sync Complete! All missing embeddings have been generated." | |
| except Exception as e: | |
| return f"Sync Failed: {e}" | |
| def check_login(username, password): | |
| """Validate credentials and return visibility updates.""" | |
| if username == ADMIN_USER and password == ADMIN_PASS: | |
| return ( | |
| gr.update(visible=False), # Hide login panel | |
| gr.update(visible=True), # Show dashboard | |
| "" # Clear error message | |
| ) | |
| else: | |
| return ( | |
| gr.update(visible=True), # Keep login panel visible | |
| gr.update(visible=False), # Keep dashboard hidden | |
| "β Invalid username or password." | |
| ) | |
| def logout(): | |
| """Log out and show login panel again.""" | |
| return ( | |
| gr.update(visible=True), # Show login panel | |
| gr.update(visible=False), # Hide dashboard | |
| "", # Clear username | |
| "" # Clear password | |
| ) | |
| with gr.Blocks(title="Get Scene Admin Dashboard", theme=gr.themes.Base()) as demo: | |
| # ββ LOGIN PANEL ββββββββββββββββββββββββββββββββββββββββββββββ | |
| with gr.Column(visible=True, elem_id="login_panel") as login_panel: | |
| gr.Markdown("# Get Scene Admin\nPlease log in to continue.") | |
| login_user = gr.Textbox(label="Username", placeholder="Enter username") | |
| login_pass = gr.Textbox(label="Password", placeholder="Enter password", type="password") | |
| login_error = gr.Markdown("", visible=True) | |
| login_btn = gr.Button("Log In", variant="primary") | |
| # ββ DASHBOARD (hidden until login) βββββββββββββββββββββββββββ | |
| with gr.Column(visible=False) as dashboard: | |
| with gr.Row(): | |
| gr.Markdown("# Get Scene Admin Dashboard") | |
| logout_btn = gr.Button("Log Out", variant="stop", scale=0) | |
| gr.Markdown("Manage FAQs, Podcasts, and Knowledge Embeddings.") | |
| with gr.Tabs(): | |
| # Tab 1: FAQs | |
| with gr.TabItem("Manage FAQs"): | |
| with gr.Row(): | |
| faq_df = gr.Dataframe( | |
| value=get_faqs, | |
| headers=["id", "question", "answer"], | |
| datatype=["number", "str", "str"], | |
| interactive=True, | |
| label="FAQ Database" | |
| ) | |
| with gr.Row(): | |
| with gr.Column(): | |
| gr.Markdown("### Add New FAQ") | |
| new_q = gr.Textbox(label="Question") | |
| new_a = gr.TextArea(label="Answer") | |
| add_btn = gr.Button("Add Entry", variant="primary") | |
| with gr.Column(): | |
| gr.Markdown("### Bulk Upload") | |
| faq_file = gr.File(label="Upload CSV/Excel (Columns: Question, Answer)") | |
| upload_faq_btn = gr.Button("Bulk Upload FAQs") | |
| faq_upload_status = gr.Textbox(label="Status", interactive=False) | |
| def add_and_refresh(q, a): | |
| add_faq_entry(q, a) | |
| return get_faqs(), "", "" | |
| add_btn.click(add_and_refresh, [new_q, new_a], [faq_df, new_q, new_a]) | |
| upload_faq_btn.click(handle_faq_upload, [faq_file], [faq_upload_status]) | |
| # Tab 2: Podcasts | |
| with gr.TabItem("Podcasts"): | |
| pod_df = gr.Dataframe( | |
| value=get_podcasts, | |
| headers=["id", "guest_name", "youtube_url", "summary"], | |
| datatype=["number", "str", "str", "str"], | |
| label="Podcast Episodes" | |
| ) | |
| gr.Markdown("### Bulk Upload Podcasts") | |
| pod_file = gr.File(label="Upload CSV/Excel (Columns: Guest Name, YouTube URL, Summary)") | |
| upload_pod_btn = gr.Button("Bulk Upload Podcasts") | |
| pod_upload_status = gr.Textbox(label="Status", interactive=False) | |
| upload_pod_btn.click(handle_podcast_upload, [pod_file], [pod_upload_status]) | |
| # Tab 3: Sync | |
| with gr.TabItem("Sync & Embed"): | |
| gr.Markdown("### Recalculate Embeddings") | |
| gr.Markdown("When you change text or upload new data, the 'embeddings' (AI understanding) must be recalculated for the chatbot to recognize the new information.") | |
| sync_btn = gr.Button("π Sync & Recalculate Embeddings", variant="primary", scale=2) | |
| sync_status = gr.Textbox(label="Sync Status", interactive=False) | |
| sync_btn.click(run_sync, None, [sync_status]) | |
| # ββ WIRING ββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| login_btn.click( | |
| check_login, | |
| inputs=[login_user, login_pass], | |
| outputs=[login_panel, dashboard, login_error] | |
| ) | |
| # Allow pressing Enter in password field to log in | |
| login_pass.submit( | |
| check_login, | |
| inputs=[login_user, login_pass], | |
| outputs=[login_panel, dashboard, login_error] | |
| ) | |
| logout_btn.click( | |
| logout, | |
| inputs=None, | |
| outputs=[login_panel, dashboard, login_user, login_pass] | |
| ) | |
| if __name__ == "__main__": | |
| # No auth= parameter β login is handled inside the app | |
| demo.launch(server_name="0.0.0.0") |