import gradio as gr import pandas as pd import os from translator_utils import process_rows, revert_rows, download_dataset, LOCAL_FILENAME, ENV_HF_TOKEN, read_csv_robust def load_data(): """Load the dataset safely using robust reader.""" if not os.path.exists(LOCAL_FILENAME): try: download_dataset() except: return pd.DataFrame() df = read_csv_robust(LOCAL_FILENAME) if df.empty: return df try: if 'Judul_Inggris' not in df.columns: df['Judul_Inggris'] = "" if 'Isi_Inggris' not in df.columns: df['Isi_Inggris'] = "" df = df.fillna("") return df except: return pd.DataFrame() def force_refresh_data(): """Forces a re-download and reload of the data.""" try: download_dataset(force=True) return load_data(), "Data refreshed successfully." except Exception as e: return pd.DataFrame(), f"Failed to refresh data: {e}" def get_pending_data(full_df): """Filter for rows that are NOT fully translated.""" if full_df.empty: return full_df mask = (full_df['Judul_Inggris'].str.strip() == "") | (full_df['Isi_Inggris'].str.strip() == "") df_view = full_df[mask].copy() df_view.reset_index(inplace=True) df_view.rename(columns={'index': 'ID'}, inplace=True) df_view.insert(0, "Select", False) return df_view def get_history_data(full_df, date_filter=""): """Filter for rows that ARE fully translated.""" if full_df.empty: return full_df mask = (full_df['Judul_Inggris'].str.strip() != "") & (full_df['Isi_Inggris'].str.strip() != "") df_view = full_df[mask].copy() df_view.reset_index(inplace=True) df_view.rename(columns={'index': 'ID'}, inplace=True) if date_filter: df_view = df_view[df_view['Tanggal'].str.contains(date_filter, case=False, na=False)] # Add 'Select' column for Revert functionality df_view.insert(0, "Select", False) return df_view def on_translate_selected(df_data, token, progress=gr.Progress()): """Callback when user clicks 'Translate Selected'.""" selected_rows = df_data[df_data["Select"] == True] if selected_rows.empty: return gr.DataFrame(), gr.DataFrame(), "No rows selected!" try: indices = selected_rows['ID'].tolist() indices = [int(i) for i in indices] except KeyError: return gr.DataFrame(), gr.DataFrame(), "Error: ID column missing." updated_df, msg = process_rows(indices, token=token, progress=progress) pending = get_pending_data(updated_df) history = get_history_data(updated_df) return pending, history, msg def on_revert_selected(df_data, token, progress=gr.Progress()): """Callback when user clicks 'Revert Selected'.""" selected_rows = df_data[df_data["Select"] == True] if selected_rows.empty: return gr.DataFrame(), gr.DataFrame(), "No rows selected!" try: indices = selected_rows['ID'].tolist() indices = [int(i) for i in indices] except KeyError: return gr.DataFrame(), gr.DataFrame(), "Error: ID column missing." # Call revert updated_df, msg = revert_rows(indices, token=token, progress=progress) # Refresh views pending = get_pending_data(updated_df) history = get_history_data(updated_df) return pending, history, msg def filter_history(date_input): df = load_data() return get_history_data(df, date_input) def refresh_views(): df, msg = force_refresh_data() pending = get_pending_data(df) history = get_history_data(df) return pending, history, msg def auto_select_pending(df_view): """Automatically selects the first 10 rows in the pending view.""" if df_view.empty: return df_view, "No rows to select." # Set 'Select' to True for the first 10 rows limit = min(10, len(df_view)) df_view.loc[:limit-1, "Select"] = True return df_view, f"Auto-selected top {limit} rows." # CSS to ensure columns are readable custom_css = """ /* Fix minimum width for content columns */ table td:nth-child(5), table td:nth-child(6), table td:nth-child(7), table td:nth-child(8) { min-width: 300px !important; white-space: pre-wrap !important; } /* Ensure row ID is small */ table td:nth-child(2) { width: 50px !important; } """ with gr.Blocks(title="Auto AI News Translator") as app: gr.Markdown("# Auto AI News Translator") gr.Markdown("Translate news from Indonesian to English (with Chunking). Select rows to translate.") # Inject default value from Env Var hf_token = gr.Textbox( label="HF Token (Optional for Persistence)", placeholder="hf_... (Leave empty if set in ENV)", type="password", value=ENV_HF_TOKEN ) with gr.Row(): status_output = gr.Textbox(label="Status", value="Ready", interactive=False, scale=4) refresh_btn = gr.Button("🔄 Refresh Data (Force Redownload)", scale=1) with gr.Tabs(): with gr.TabItem("Translate Pending"): gr.Markdown("Select rows to translate and click Submit.") with gr.Row(): auto_select_btn = gr.Button("Auto-Select Next 10", size="sm") translate_btn = gr.Button("Translate Selected Rows", variant="primary") pending_df_comp = gr.DataFrame( value=get_pending_data(load_data()), label="Pending Translations", interactive=True, wrap=True, datatype=["bool", "number"] + ["str"] * 6 ) auto_select_btn.click( fn=auto_select_pending, inputs=[pending_df_comp], outputs=[pending_df_comp, status_output] ) with gr.TabItem("History (Revert Supported)"): gr.Markdown("Select rows to delete translation and move back to Pending.") date_filter = gr.Textbox(label="Filter by Date (YYYY-MM-DD)") # History now has checkbox history_df_comp = gr.DataFrame( value=get_history_data(load_data()), label="Translation History", interactive=True, wrap=True, datatype=["bool", "number"] + ["str"] * 6 ) with gr.Row(): date_filter.change(fn=filter_history, inputs=[date_filter], outputs=[history_df_comp]) revert_btn = gr.Button("Revert Selected (Delete & Move to Pending)", variant="stop") translate_btn.click( fn=on_translate_selected, inputs=[pending_df_comp, hf_token], outputs=[pending_df_comp, history_df_comp, status_output] ) revert_btn.click( fn=on_revert_selected, inputs=[history_df_comp, hf_token], outputs=[pending_df_comp, history_df_comp, status_output] ) refresh_btn.click( fn=refresh_views, outputs=[pending_df_comp, history_df_comp, status_output] ) if __name__ == "__main__": app.launch(css=custom_css)