File size: 5,312 Bytes
44e7320
 
 
 
 
 
 
 
 
 
 
35f7fbb
44e7320
 
 
 
 
 
87d485d
44e7320
 
 
 
 
 
 
 
 
87d485d
 
35f7fbb
 
 
 
 
 
 
 
 
87d485d
35f7fbb
 
 
 
 
 
 
 
65e2013
 
44e7320
 
65e2013
 
44e7320
 
 
a137762
 
 
 
 
44e7320
 
 
 
a137762
 
 
 
 
 
 
 
 
 
 
 
 
44e7320
a137762
 
 
 
 
 
 
 
 
 
 
 
 
87d485d
35f7fbb
87d485d
35f7fbb
 
 
87d485d
 
 
 
35f7fbb
87d485d
 
35f7fbb
87d485d
 
 
35f7fbb
87d485d
 
 
35f7fbb
87d485d
 
 
44e7320
 
 
 
35f7fbb
44e7320
35f7fbb
 
 
 
 
 
44e7320
35f7fbb
 
 
 
44e7320
35f7fbb
 
44e7320
35f7fbb
 
44e7320
 
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
import pandas as pd
import gspread
from oauth2client.service_account import ServiceAccountCredentials
import gradio as gr
import plotly.express as px

# === Google Sheets Auth ===
scope = ["https://spreadsheets.google.com/feeds", "https://www.googleapis.com/auth/drive"]
creds = ServiceAccountCredentials.from_json_keyfile_name("tough-star.json", scope)
client = gspread.authorize(creds)

# === Load and clean sheet data ===
sheet_url = "https://docs.google.com/spreadsheets/d/1bpeFS6yihb6niCavpwjWmVEypaSkGxONGg2jZfKX_sA"
sheet = client.open_by_url(sheet_url).worksheet("Calls")
data = sheet.get_all_records()
df = pd.DataFrame(data)

df['Timestamp'] = pd.to_datetime(df['Timestamp'], dayfirst=True, errors='coerce')
df['Date'] = df['Timestamp'].dt.date.astype(str)
df['Time'] = df['Timestamp'].dt.time

location_split = df['Location'].str.split(',', expand=True)
df['Latitude'] = pd.to_numeric(location_split[0], errors='coerce')
df['Longitude'] = pd.to_numeric(location_split[1], errors='coerce')
df = df.dropna(subset=['Date', 'Rep Name', 'Latitude', 'Longitude'])
df = df[(df['Latitude'] != 0) & (df['Longitude'] != 0)]
df = df.sort_values(by=['Rep Name', 'Timestamp'])
df['Time Diff (min)'] = df.groupby(['Rep Name', 'Date'])['Timestamp'].diff().dt.total_seconds().div(60).fillna(0)
df['Visit Order'] = df.groupby(['Rep Name', 'Date']).cumcount() + 1

# === Helper: All unique reps in dataset ===
all_reps = sorted(df['Rep Name'].dropna().unique())

# === Tab 1: Summary ===
def generate_summary(date_str):
    day_df = df[df['Date'] == date_str]

    # Active reps and their total stops
    active = day_df.groupby('Rep Name').size().reset_index(name='Total Visits')

    # Detect inactive reps
    active_list = active['Rep Name'].tolist()
    inactive_list = [rep for rep in all_reps if rep not in active_list]
    inactive_df = pd.DataFrame({'Inactive Reps': inactive_list})

    return active, inactive_df

# === Tab 2: KAMs ===
def get_reps(date_str):
    reps = df[df['Date'] == date_str]['Rep Name'].dropna().unique()
    return sorted(reps)

def show_map(date_str, rep):
    subset = df[(df['Date'] == date_str) & (df['Rep Name'] == rep)]
    if subset.empty:
        return "No valid data", None

    subset = subset.sort_values(by='Timestamp').copy()
    subset['Visit Order'] = range(1, len(subset) + 1)
    center_lat = subset['Latitude'].mean()
    center_lon = subset['Longitude'].mean()

    fig = px.line_mapbox(
        subset,
        lat="Latitude", lon="Longitude",
        hover_name="Dealership Name",
        hover_data={"Time": True, "Time Diff (min)": True, "Visit Order": True},
        height=700,
        zoom=13,
        center={"lat": center_lat, "lon": center_lon}
    )

    scatter = px.scatter_mapbox(
        subset,
        lat="Latitude", lon="Longitude",
        color="Visit Order",
        hover_name="Dealership Name",
        hover_data=["Time", "Time Diff (min)"],
        color_continuous_scale="Bluered"
    )
    for trace in scatter.data:
        fig.add_trace(trace)

    fig.add_trace(px.scatter_mapbox(
        pd.DataFrame([subset.iloc[0]]),
        lat="Latitude", lon="Longitude",
        text=["Start"], color_discrete_sequence=["green"]).data[0])
    fig.add_trace(px.scatter_mapbox(
        pd.DataFrame([subset.iloc[-1]]),
        lat="Latitude", lon="Longitude",
        text=["End"], color_discrete_sequence=["red"]).data[0])

    fig.update_layout(mapbox_style="open-street-map", title=f"๐Ÿ“ {rep}'s Route on {date_str}")

    # Final table (without photo)
    table = subset[[
        'Visit Order', 'Dealership Name', 'Time', 'Time Diff (min)', 
        'Type of call', 'Sales or service'
    ]].rename(columns={
        'Dealership Name': '๐Ÿงญ Dealer',
        'Time': '๐Ÿ•’ Time',
        'Time Diff (min)': 'โฑ๏ธ Time Spent',
        'Type of call': '๐Ÿ“ž Call Type',
        'Sales or service': '๐Ÿ’ผ Category'
    })

    # Summary footer
    total_time = round(table['โฑ๏ธ Time Spent'].sum(), 2)
    summary_row = pd.DataFrame([{
        'Visit Order': '',
        '๐Ÿงญ Dealer': f"๐Ÿงฎ Total Time: {total_time} min",
        '๐Ÿ•’ Time': '',
        'โฑ๏ธ Time Spent': '',
        '๐Ÿ“ž Call Type': '',
        '๐Ÿ’ผ Category': ''
    }])
    table = pd.concat([table, summary_row], ignore_index=True)

    return table, fig

# === Gradio UI ===
with gr.Blocks() as app:
    gr.Markdown("## ๐Ÿ—‚๏ธ Carfind Rep Tracker")

    with gr.Tab("๐Ÿ“Š Summary"):
        date_summary = gr.Dropdown(label="Select Date", choices=sorted(df['Date'].unique(), reverse=True))
        active_table = gr.Dataframe(label="โœ… Active Reps (with total visits)")
        inactive_table = gr.Dataframe(label="โš ๏ธ Inactive Reps")

        date_summary.change(fn=generate_summary, inputs=date_summary, outputs=[active_table, inactive_table])

    with gr.Tab("๐Ÿ‘ค KAM's"):
        date_picker = gr.Dropdown(label="Select Date", choices=sorted(df['Date'].unique(), reverse=True))
        rep_picker = gr.Dropdown(label="Select Rep")
        btn = gr.Button("Show Route")

        table = gr.Dataframe(label="Call Table")
        map_plot = gr.Plot(label="Map")

        date_picker.change(fn=get_reps, inputs=date_picker, outputs=rep_picker)
        btn.click(fn=show_map, inputs=[date_picker, rep_picker], outputs=[table, map_plot])

app.launch()