Spaces:
Sleeping
Sleeping
| import gradio as gr | |
| import pandas as pd | |
| import os | |
| # ========== Constants ========== | |
| EXCEL_FILE = "data_store.xlsx" | |
| DATA_SHEET = "DATA" | |
| USER_SHEET = "USERS" | |
| # ========== Load or Create Data ========== | |
| def load_data(): | |
| if os.path.exists(EXCEL_FILE): | |
| xls = pd.read_excel(EXCEL_FILE, sheet_name=None) | |
| df_data = xls.get(DATA_SHEET, pd.DataFrame(columns=["USERNAME", "COUNTRYCODE", "SBUCODE", "BRANDCODE", "CONCEPTCODE", "CHANNEL", "REMARK"])) | |
| df_users = xls.get(USER_SHEET, pd.DataFrame([{"USERNAME": "ADMIN", "PASSWORD": "ADMIN"}])) | |
| else: | |
| df_data = pd.DataFrame(columns=["USERNAME", "COUNTRYCODE", "SBUCODE", "BRANDCODE", "CONCEPTCODE", "CHANNEL", "REMARK"]) | |
| df_users = pd.DataFrame([{"USERNAME": "ADMIN", "PASSWORD": "ADMIN"}]) | |
| save_data(df_data, df_users) | |
| return df_data, df_users | |
| def save_data(data_df, users_df): | |
| with pd.ExcelWriter(EXCEL_FILE, engine='openpyxl', mode='w') as writer: | |
| data_df.to_excel(writer, index=False, sheet_name=DATA_SHEET) | |
| users_df.to_excel(writer, index=False, sheet_name=USER_SHEET) | |
| # ========== Initial Data ========== | |
| df, users_df = load_data() | |
| # ========== Helpers ========== | |
| def to_upper(df_input): | |
| return df_input.applymap(lambda x: str(x).upper().strip() if pd.notnull(x) else x) | |
| def get_active_data(): | |
| return df[df["REMARK"].str.upper() != "DELETE"].reset_index(drop=True) | |
| def login(username, password): | |
| username, password = username.strip().upper(), password.strip() | |
| is_valid = not users_df[(users_df["USERNAME"] == username) & (users_df["PASSWORD"] == password)].empty | |
| if is_valid: | |
| return "", gr.update(visible=False), gr.update(visible=True), get_active_data(), show_users() | |
| return "Invalid login", gr.update(visible=True), gr.update(visible=False), pd.DataFrame(), pd.DataFrame() | |
| def bulk_submit(dataframe): | |
| global df | |
| new_df = pd.DataFrame(dataframe).dropna(how="all") | |
| if new_df.empty: | |
| return get_active_data() | |
| new_df = to_upper(new_df) | |
| required = ["USERNAME", "COUNTRYCODE", "SBUCODE", "BRANDCODE", "CONCEPTCODE", "CHANNEL", "REMARK"] | |
| new_df = new_df[required].dropna() | |
| pk_cols = required | |
| merged = pd.merge(new_df, df[pk_cols], on=pk_cols, how="left", indicator=True) | |
| df_filtered = new_df[merged["_merge"] == "left_only"] | |
| df = pd.concat([df, df_filtered], ignore_index=True) | |
| save_data(df, users_df) | |
| return get_active_data() | |
| def delete_selected_rows(selected_df): | |
| global df | |
| try: | |
| if selected_df.empty: | |
| return "Tidak ada baris yang dipilih", get_active_data() | |
| for _, row in selected_df.iterrows(): | |
| mask = ( | |
| (df["USERNAME"] == row["USERNAME"]) & | |
| (df["COUNTRYCODE"] == row["COUNTRYCODE"]) & | |
| (df["SBUCODE"] == row["SBUCODE"]) & | |
| (df["BRANDCODE"] == row["BRANDCODE"]) & | |
| (df["CONCEPTCODE"] == row["CONCEPTCODE"]) & | |
| (df["CHANNEL"] == row["CHANNEL"]) & | |
| (df["REMARK"].str.upper() != "DELETE") | |
| ) | |
| df.loc[mask, "REMARK"] = "DELETE" | |
| save_data(df, users_df) | |
| return "Berhasil dihapus.", get_active_data() | |
| except Exception as e: | |
| return f"Error: {e}", get_active_data() | |
| def add_user(username, password): | |
| global users_df | |
| username, password = username.strip().upper(), password.strip() | |
| if not username or not password: | |
| return "Username/password tidak boleh kosong", show_users() | |
| if username in users_df["USERNAME"].values: | |
| return "Username sudah ada", show_users() | |
| users_df = pd.concat([users_df, pd.DataFrame([{"USERNAME": username, "PASSWORD": password}])], ignore_index=True) | |
| save_data(df, users_df) | |
| return f"User {username} ditambahkan", show_users() | |
| def delete_user(username): | |
| global users_df | |
| username = username.strip().upper() | |
| if username == "ADMIN": | |
| return "ADMIN tidak boleh dihapus", show_users() | |
| users_df = users_df[users_df["USERNAME"] != username].reset_index(drop=True) | |
| save_data(df, users_df) | |
| return f"User {username} dihapus", show_users() | |
| def show_users(): | |
| return users_df.copy() | |
| def show_guide(): | |
| return """ | |
| ### Panduan Penggunaan Aplikasi | |
| 1. **Login** | |
| - Gunakan username dan password. | |
| 2. **Input Data** | |
| - Lihat data aktif. | |
| - Input data baru dengan kolom lengkap. | |
| - Pilih baris dan klik "Delete Selected Rows" untuk tandai delete. | |
| 3. **Manajemen User** | |
| - Tambah dan hapus user (kecuali ADMIN). | |
| 4. **Data yang dihapus tidak benar-benar dihapus**, hanya ditandai `REMARK = DELETE`. | |
| """ | |
| # ========== UI ========== | |
| with gr.Blocks() as app: | |
| gr.Markdown("## Login") | |
| login_box = gr.Column(visible=True) | |
| with login_box: | |
| user = gr.Textbox(label="Username") | |
| pw = gr.Textbox(label="Password", type="password") | |
| btn_login = gr.Button("Login") | |
| login_msg = gr.Textbox(visible=True, interactive=False, show_label=False) | |
| tab_section = gr.Tabs(visible=False) | |
| with tab_section: | |
| with gr.Tab("Input Data"): | |
| gr.Markdown("### Data Aktif") | |
| df_list = gr.Dataframe( | |
| label="List Data Aktif", | |
| interactive=False, | |
| column_widths=["auto"]*7, | |
| wrap=True, | |
| show_copy_button=True, | |
| # show_download_button=True, | |
| show_fullscreen_button=True, | |
| show_search=True, | |
| show_row_numbers=True, | |
| pinned_columns=["USERNAME"] | |
| ) | |
| selected_df = gr.Dataframe(visible=False) | |
| gr.Markdown("---") | |
| gr.Markdown("### Input Data Baru") | |
| df_input = gr.Dataframe( | |
| headers=["USERNAME", "COUNTRYCODE", "SBUCODE", "BRANDCODE", "CONCEPTCODE", "CHANNEL", "REMARK"], | |
| row_count=5, | |
| col_count=(7, "fixed"), | |
| label="Form Input" | |
| ) | |
| btn_submit = gr.Button("Submit Data") | |
| btn_delete = gr.Button("Delete Selected Rows") | |
| delete_msg = gr.Textbox(visible=True, interactive=False, show_label=False) | |
| with gr.Tab("User Management"): | |
| gr.Markdown("### Kelola User") | |
| new_user = gr.Textbox(label="New Username") | |
| new_pass = gr.Textbox(label="New Password") | |
| btn_add = gr.Button("Add User") | |
| del_user = gr.Textbox(label="Delete Username") | |
| btn_del = gr.Button("Delete User") | |
| user_msg = gr.Textbox(visible=True, interactive=False, show_label=False) | |
| user_table = gr.Dataframe(label="User List", interactive=False) | |
| with gr.Tab("User Guide"): | |
| gr.Markdown(show_guide()) | |
| # ==== Events ==== | |
| btn_login.click(fn=login, inputs=[user, pw], outputs=[login_msg, login_box, tab_section, df_list, user_table]) | |
| btn_submit.click(fn=bulk_submit, inputs=[df_input], outputs=[df_list]) | |
| df_list.select(fn=lambda df, evt: df.iloc[[evt.index]] if evt.index is not None else pd.DataFrame(), | |
| inputs=[df_list], outputs=[selected_df]) | |
| btn_delete.click(fn=delete_selected_rows, inputs=[selected_df], outputs=[delete_msg, df_list]) | |
| btn_add.click(fn=add_user, inputs=[new_user, new_pass], outputs=[user_msg, user_table]) | |
| btn_del.click(fn=delete_user, inputs=[del_user], outputs=[user_msg, user_table]) | |
| app.launch() |