saim1309's picture
Update app.py
cbaf4a5 verified
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.")
# Path to SQLite DB β€” override via DB_PATH env var if your file has a different name
DB_PATH = os.environ.get("DB_PATH", "getscene_ai.sqlite")
# ── Helper functions ──────────────────────────────────────────────────────────
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 prepare_download():
"""Return the SQLite file path for Gradio to serve as a download."""
if not os.path.exists(DB_PATH):
return None, f"❌ Database file not found at: `{DB_PATH}`. Check your DB_PATH env var."
size_mb = os.path.getsize(DB_PATH) / (1024 * 1024)
return DB_PATH, f"βœ… Ready β€” `{os.path.basename(DB_PATH)}` ({size_mb:.2f} MB). Click the file below to save it."
def check_login(username, password):
if username == ADMIN_USER and password == ADMIN_PASS:
return (
gr.update(visible=False), # Hide login panel
gr.update(visible=True), # Show dashboard
"" # Clear error
)
else:
return (
gr.update(visible=True),
gr.update(visible=False),
"❌ Invalid username or password."
)
def logout():
return (
gr.update(visible=True), # Show login panel
gr.update(visible=False), # Hide dashboard
"", # Clear username field
"" # Clear password field
)
# ── UI ────────────────────────────────────────────────────────────────────────
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("")
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 & Embed ──────────────────────────────
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])
# ── Tab 4: Download Database ─────────────────────────
with gr.TabItem("⬇️ Download Database"):
gr.Markdown("### Download SQLite Database")
gr.Markdown(
"Click the button below to download the raw `database.db` SQLite file."
)
download_btn = gr.Button("⬇️ Download database.db", variant="primary")
download_status = gr.Markdown("")
db_file_output = gr.File(
label="πŸ“¦ database.db β€” click to save",
visible=False,
interactive=False
)
def on_download_click():
path, msg = prepare_download()
if path:
return gr.update(value=path, visible=True), msg
else:
return gr.update(visible=False), msg
download_btn.click(
on_download_click,
inputs=None,
outputs=[db_file_output, download_status]
)
# ── WIRING ────────────────────────────────────────────────────
login_btn.click(
check_login,
inputs=[login_user, login_pass],
outputs=[login_panel, dashboard, login_error]
)
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__":
demo.launch(server_name="0.0.0.0")