File size: 4,838 Bytes
44e7320
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
87d485d
44e7320
 
 
 
 
 
 
 
 
 
87d485d
 
 
 
 
 
 
 
44e7320
65e2013
 
44e7320
 
65e2013
 
44e7320
 
 
a137762
 
 
87d485d
a137762
 
 
44e7320
 
 
 
a137762
 
 
 
 
 
 
 
 
 
 
 
 
44e7320
a137762
 
 
 
 
 
 
 
 
 
 
 
 
87d485d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
44e7320
 
 
65e2013
 
44e7320
 
 
 
65e2013
 
 
 
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
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 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)

# === Parse and clean ===
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)

# Add Visit Order
df['Visit Order'] = df.groupby(['Rep Name', 'Date']).cumcount() + 1

# Construct image thumbnail URLs from Google Drive folder
drive_folder_url = "https://drive.google.com/uc?id="
df['Image ID'] = df['Image'].str.extract(r'Calls_Images/([^.]+)')
df['Image URL'] = df['Image ID'].apply(lambda x: f"{drive_folder_url}{x}" if pd.notna(x) else "")

# === Functions ===
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 and zoom
    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}")

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

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

    return table, fig

# === Gradio UI ===
def update(date_str):
    return gr.Dropdown(choices=get_reps(date_str), label="Select Rep")

with gr.Blocks() as app:
    gr.Markdown("## ๐Ÿ—บ๏ธ Rep Route & Visit Visualizer")

    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=update, inputs=date_picker, outputs=rep_picker)
    btn.click(fn=show_map, inputs=[date_picker, rep_picker], outputs=[table, map_plot])

app.launch()