File size: 6,775 Bytes
88ff990
 
4e6e288
88ff990
 
12477af
88ff990
de7d48b
 
 
 
 
 
 
 
4e6e288
 
 
1f48402
4e6e288
de7d48b
4e6e288
1f48402
 
 
 
de7d48b
 
12477af
 
 
 
 
 
 
 
 
de7d48b
 
 
12477af
 
6259813
12477af
de7d48b
 
 
 
 
 
 
581bd58
12477af
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4e6e288
 
12477af
de7d48b
 
 
12477af
6259813
12477af
de7d48b
6259813
de7d48b
12477af
 
 
 
de7d48b
 
 
 
 
12477af
de7d48b
12477af
 
 
de7d48b
12477af
 
 
 
de7d48b
12477af
 
 
de7d48b
12477af
 
 
 
de7d48b
12477af
 
 
 
de7d48b
12477af
de7d48b
 
268f142
de7d48b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12477af
 
 
 
 
 
6259813
de7d48b
 
 
 
 
 
 
50acac4
6259813
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
import pandas as pd
import gspread
import gradio as gr
from oauth2client.service_account import ServiceAccountCredentials
from datetime import datetime
from math import radians, cos, sin, asin, sqrt

# ------------------ 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"])

def haversine(coord1, coord2):
    lon1, lat1 = map(radians, map(float, coord1.split(',')[::-1]))
    lon2, lat2 = map(radians, map(float, coord2.split(',')[::-1]))
    dlon = lon2 - lon1
    dlat = lat2 - lat1
    a = sin(dlat/2)**2 + cos(lat1) * cos(lat2) * sin(dlon/2)**2
    c = 2 * asin(sqrt(a))
    return 6371 * c

# ------------------ FIELD SALES ------------------
def load_field_sales():
    df = load_tab("Field Sales")
    if df.empty or "Date" not in df.columns:
        return pd.DataFrame()

    df['Date'] = pd.to_datetime(df['Date'], 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)

    # Distance calc
    distances = [0]
    for i in range(1, len(df)):
        try:
            prev = df.loc[i-1, 'Location']
            curr = df.loc[i, 'Location']
            if pd.notna(prev) and pd.notna(curr):
                distances.append(round(haversine(prev, curr), 2))
            else:
                distances.append(0)
        except:
            distances.append(0)
    df["Distance Travelled (km)"] = distances

    return df

# ------------------ TELESALES ------------------
def load_telesales():
    df = load_tab("TeleSales")
    if df.empty or "Rep Email" not in df.columns:
        return pd.DataFrame()

    df["Date"] = pd.to_datetime(df.get("Date", datetime.today()), errors='coerce')
    df["DateStr"] = df["Date"].dt.date.astype(str)
    return df

# ------------------ OEM ------------------
def load_oem():
    df = load_tab("OEM Visit")
    if df.empty or "Rep" not in df.columns:
        return pd.DataFrame()
    df["Date"] = pd.to_datetime(df.get("Date", datetime.today()), errors='coerce')
    df["DateStr"] = df["Date"].dt.date.astype(str)
    return df

# ------------------ SUMMARY ------------------
def generate_summary(date_str):
    df = load_field_sales()
    if df.empty:
        return pd.DataFrame([["No Field Sales data"]], columns=["Message"])*5

    df_day = df[df['DateStr'] == date_str.strip()]
    all_reps = sorted(df['Rep'].dropna().unique())
    col = "Current/Prospect Customer"

    # --- Visits Breakdown
    total_visits = df_day.groupby("Rep").size().reset_index(name="Total Visits")
    current = df_day[df_day[col] == "Current"]
    prospect = df_day[df_day[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]
    })
    inactive = pd.DataFrame({'Inactive Reps': [rep for rep in all_reps if rep not in total_visits["Rep"].tolist()]})

    # --- Field Summary per Rep
    rep_summary = df_day.groupby("Rep").agg({
        "Order Value": "sum",
        "Order Received": lambda x: (x == "Yes").sum(),
        "Current/Prospect Customer": lambda x: (x == "Current").sum(),
        "Distance Travelled (km)": "sum"
    }).rename(columns={
        "Order Value": "Total Order Value",
        "Order Received": "Orders Received",
        "Current/Prospect Customer": "Current Customers",
        "Distance Travelled (km)": "Total Distance (km)"
    }).reset_index()

    # --- TeleSales Summary
    df_ts = load_telesales()
    df_ts_day = df_ts[df_ts['DateStr'] == date_str.strip()]
    ts_summary = df_ts_day.groupby("Rep Email").size().reset_index(name="Total Calls Made") if not df_ts_day.empty else pd.DataFrame([["No Telesales"]], columns=["Info"])

    # --- OEM Summary
    df_oem = load_oem()
    df_oem_day = df_oem[df_oem['DateStr'] == date_str.strip()]
    oem_summary = df_oem_day.groupby("Rep").size().reset_index(name="Total OEM Visits") if not df_oem_day.empty else pd.DataFrame([["No OEM Visits"]], columns=["Info"])

    return total_visits, breakdown, inactive, rep_summary, ts_summary, oem_summary

# ------------------ 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")
                field_summary = gr.Dataframe(label="πŸš— Field Sales Summary")
                ts_summary = gr.Dataframe(label="πŸ“ž Telesales Summary")
                oem_summary = gr.Dataframe(label="🏭 OEM Visit Summary")

                date_summary.change(fn=generate_summary, inputs=date_summary,
                                    outputs=[visits, breakdown, inactive, field_summary, ts_summary, oem_summary])

    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()