Spaces:
Sleeping
Sleeping
| import pandas as pd | |
| import gspread | |
| import gradio as gr | |
| from oauth2client.service_account import ServiceAccountCredentials | |
| from datetime import datetime | |
| # ------------------ AUTH ------------------ | |
| VALID_USERS = { | |
| "[email protected]": "Pass.123", | |
| "[email protected]": "Pass.123", | |
| "[email protected]": "Pass.123" | |
| } | |
| # ------------------ GOOGLE SHEET SETUP ------------------ | |
| scope = ["https://spreadsheets.google.com/feeds", "https://www.googleapis.com/auth/drive"] | |
| creds = ServiceAccountCredentials.from_json_keyfile_name("credentials.json", scope) | |
| client = gspread.authorize(creds) | |
| sheet_file = client.open("userAccess") | |
| # ------------------ HELPERS ------------------ | |
| def load_tab(sheet_name): | |
| try: | |
| df = pd.DataFrame(sheet_file.worksheet(sheet_name).get_all_records()) | |
| return df | |
| except: | |
| return pd.DataFrame([["β οΈ Could not load sheet."]], columns=["Error"]) | |
| # ------------------ FIELD SALES ------------------ | |
| def load_field_sales(): | |
| df = load_tab("Field Sales") | |
| if df.empty: | |
| return pd.DataFrame(columns=["Date", "Rep", "Order Value", "Order Received", "Current/Prospect Custor", "Customer Type & Status", "DateStr"]) | |
| df['Date'] = pd.to_datetime(df.get("Date", datetime.today()), errors='coerce') | |
| df = df.dropna(subset=["Date"]) | |
| df['DateStr'] = df['Date'].dt.date.astype(str) | |
| if "Order Value" not in df.columns: | |
| df["Order Value"] = 0 | |
| else: | |
| df["Order Value"] = pd.to_numeric(df["Order Value"], errors="coerce").fillna(0) | |
| return df | |
| def generate_summary(date_str): | |
| df = load_field_sales() | |
| if df.empty: | |
| return pd.DataFrame([["No data"]], columns=["Message"]), pd.DataFrame(), pd.DataFrame() | |
| all_reps = sorted(df['Rep'].dropna().unique()) | |
| day_df = df[df['DateStr'] == date_str] | |
| total_visits = day_df.groupby("Rep").size().reset_index(name="Total Visits") | |
| col = "Current/Prospect Custor" | |
| if col not in df.columns: | |
| df[col] = "" | |
| current = day_df[day_df[col] == "Current"] | |
| prospect = day_df[day_df[col] == "Prospect"] | |
| breakdown = pd.DataFrame({ | |
| "Rep": all_reps, | |
| "Current": [len(current[current["Rep"] == rep]) for rep in all_reps], | |
| "Prospect": [len(prospect[prospect["Rep"] == rep]) for rep in all_reps] | |
| }) | |
| active_list = total_visits['Rep'].tolist() | |
| inactive_list = [rep for rep in all_reps if rep not in active_list] | |
| inactive_df = pd.DataFrame({'Inactive Reps': inactive_list}) | |
| return total_visits, breakdown, inactive_df | |
| def get_order_summary(date_str): | |
| df = load_field_sales() | |
| if df.empty: | |
| return pd.DataFrame([["No data"]], columns=["Message"]) | |
| day_df = df[df['DateStr'] == date_str] | |
| if "Order Received" not in df.columns: | |
| df["Order Received"] = "" | |
| rep_group = day_df.groupby("Rep").agg({ | |
| "Order Received": lambda x: (x == "Yes").sum(), | |
| "Order Value": "sum" | |
| }).reset_index().rename(columns={ | |
| "Order Received": "Orders Received", | |
| "Order Value": "Total Order Value" | |
| }) | |
| return rep_group.sort_values(by="Total Order Value", ascending=False) | |
| def get_escalations(): | |
| df = load_field_sales() | |
| if df.empty: | |
| return pd.DataFrame([["No data in Field Sales"]], columns=["Message"]) | |
| col = "Customer Type & Status" | |
| if col in df.columns: | |
| flagged = df[df[col].str.contains("Second", na=False)] | |
| return flagged if not flagged.empty else pd.DataFrame([["No second-hand dealerships flagged."]], columns=["Message"]) | |
| else: | |
| return pd.DataFrame([["β οΈ Column 'Customer Type & Status' not found."]], columns=["Message"]) | |
| # ------------------ TELESALeS ------------------ | |
| def get_telesales_summary(): | |
| df = load_tab("TeleSales") | |
| if df.empty or "Rep Email" not in df.columns: | |
| return pd.DataFrame([["No data available"]], columns=["Message"]) | |
| df["Date"] = pd.to_datetime(df.get("Date", datetime.today()), errors='coerce') | |
| df["DateStr"] = df["Date"].dt.date.astype(str) | |
| grouped = df.groupby(["Rep Email", "DateStr"]).size().reset_index(name="Calls Made") | |
| return grouped.rename(columns={"Rep Email": "Rep"}) | |
| # ------------------ OEM VISITS ------------------ | |
| def get_oem_summary(): | |
| df = load_tab("OEM Visit") | |
| if df.empty or "Rep" not in df.columns: | |
| return pd.DataFrame([["No data available"]], columns=["Message"]) | |
| df["Date"] = pd.to_datetime(df.get("Date", datetime.today()), errors='coerce') | |
| df["DateStr"] = df["Date"].dt.date.astype(str) | |
| return df.groupby(["Rep", "DateStr"]).size().reset_index(name="OEM Visits") | |
| # ------------------ CUSTOMER REQUESTS ------------------ | |
| def get_requests(): | |
| df = load_tab("Customer Requests") | |
| return df if not df.empty else pd.DataFrame([["No requests yet."]], columns=["Message"]) | |
| # ------------------ CUSTOMER LISTINGS ------------------ | |
| def get_listings(): | |
| df = load_tab("CustomerListings") | |
| return df if not df.empty else pd.DataFrame([["No listings found."]], columns=["Message"]) | |
| # ------------------ USERS ------------------ | |
| def get_users(): | |
| df = load_tab("Users") | |
| return df if not df.empty else pd.DataFrame([["No users configured."]], columns=["Message"]) | |
| # ------------------ GRADIO APP ------------------ | |
| with gr.Blocks() as app: | |
| with gr.Row(): | |
| with gr.Column(visible=True) as login_ui: | |
| gr.Markdown("## π Login Required") | |
| email = gr.Textbox(label="Email") | |
| password = gr.Textbox(label="Password", type="password") | |
| login_btn = gr.Button("Login") | |
| login_msg = gr.Markdown("") | |
| with gr.Column(visible=False) as main_ui: | |
| gr.Markdown("## ποΈ CarMat Dashboard") | |
| df_initial = load_field_sales() | |
| unique_dates = sorted(df_initial["DateStr"].unique(), reverse=True) if not df_initial.empty else [] | |
| # --- Summary Tab --- | |
| with gr.Tab("π Summary"): | |
| date_summary = gr.Dropdown(label="Select Date", choices=unique_dates) | |
| visits = gr.Dataframe(label="β Total Visits") | |
| breakdown = gr.Dataframe(label="π·οΈ Current vs. Prospect") | |
| inactive = gr.Dataframe(label="β οΈ Inactive Reps") | |
| date_summary.change(fn=generate_summary, inputs=date_summary, outputs=[visits, breakdown, inactive]) | |
| # --- Orders Tab --- | |
| with gr.Tab("π¦ Orders"): | |
| order_date = gr.Dropdown(label="Select Date", choices=unique_dates) | |
| order_table = gr.Dataframe(label="π° Orders Summary") | |
| order_date.change(fn=get_order_summary, inputs=order_date, outputs=order_table) | |
| # --- Escalations --- | |
| with gr.Tab("π¨ Escalations"): | |
| esc_table = gr.Dataframe(value=get_escalations, label="Second-hand Dealerships") | |
| esc_btn = gr.Button("π Refresh") | |
| esc_btn.click(fn=get_escalations, outputs=esc_table) | |
| # --- TeleSales --- | |
| with gr.Tab("π TeleSales"): | |
| ts_table = gr.Dataframe(value=get_telesales_summary, label="TeleSales Summary") | |
| ts_refresh = gr.Button("π Refresh TeleSales") | |
| ts_refresh.click(fn=get_telesales_summary, outputs=ts_table) | |
| # --- OEM Visits --- | |
| with gr.Tab("π OEM Visits"): | |
| oem_table = gr.Dataframe(value=get_oem_summary, label="OEM Visit Summary") | |
| oem_refresh = gr.Button("π Refresh OEM") | |
| oem_refresh.click(fn=get_oem_summary, outputs=oem_table) | |
| # --- Requests --- | |
| with gr.Tab("π¬ Customer Requests"): | |
| req_table = gr.Dataframe(value=get_requests, label="Customer Requests", interactive=False) | |
| req_refresh = gr.Button("π Refresh Requests") | |
| req_refresh.click(fn=get_requests, outputs=req_table) | |
| # --- Dealerships --- | |
| with gr.Tab("π Dealership Directory"): | |
| listings_table = gr.Dataframe(value=get_listings, label="Customer Listings") | |
| listings_refresh = gr.Button("π Refresh Listings") | |
| listings_refresh.click(fn=get_listings, outputs=listings_table) | |
| # --- Users --- | |
| with gr.Tab("π€ Users"): | |
| users_table = gr.Dataframe(value=get_users, label="Users") | |
| users_refresh = gr.Button("π Refresh Users") | |
| users_refresh.click(fn=get_users, outputs=users_table) | |
| def do_login(user, pw): | |
| if VALID_USERS.get(user) == pw: | |
| return gr.update(visible=False), gr.update(visible=True), "" | |
| else: | |
| return gr.update(visible=True), gr.update(visible=False), "β Invalid login." | |
| login_btn.click(fn=do_login, inputs=[email, password], outputs=[login_ui, main_ui, login_msg]) | |
| app.launch() |