IAMTFRMZA commited on
Commit
50acac4
ยท
verified ยท
1 Parent(s): 1f48402

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +86 -192
app.py CHANGED
@@ -3,209 +3,103 @@ import gspread
3
  import gradio as gr
4
  from oauth2client.service_account import ServiceAccountCredentials
5
  from datetime import datetime
 
 
 
6
 
7
- # ------------------ AUTH ------------------
8
- VALID_USERS = {
9
- "[email protected]": "Pass.123",
10
- "[email protected]": "Pass.123",
11
- "[email protected]": "Pass.123"
12
- }
13
-
14
- # ------------------ GOOGLE SHEET SETUP ------------------
15
  scope = ["https://spreadsheets.google.com/feeds", "https://www.googleapis.com/auth/drive"]
16
  creds = ServiceAccountCredentials.from_json_keyfile_name("credentials.json", scope)
17
  client = gspread.authorize(creds)
18
  sheet_file = client.open("userAccess")
19
 
20
- # ------------------ HELPERS ------------------
21
  def load_tab(sheet_name):
22
  try:
23
  df = pd.DataFrame(sheet_file.worksheet(sheet_name).get_all_records())
24
  return df
25
  except:
26
- return pd.DataFrame([["โš ๏ธ Could not load sheet."]], columns=["Error"])
27
-
28
- # ------------------ FIELD SALES ------------------
29
- def load_field_sales():
30
- df = load_tab("Field Sales")
31
- if df.empty:
32
- return pd.DataFrame(columns=["Date", "Rep", "Order Value", "Order Received", "Current/Prospect Custor", "Customer Type & Status", "DateStr"])
33
- df['Date'] = pd.to_datetime(df.get("Date", datetime.today()), errors='coerce')
34
- df = df.dropna(subset=["Date"])
35
- df['DateStr'] = df['Date'].dt.date.astype(str)
36
-
37
- if "Order Value" not in df.columns:
38
- df["Order Value"] = 0
39
- else:
40
- df["Order Value"] = pd.to_numeric(df["Order Value"], errors="coerce").fillna(0)
41
-
42
  return df
43
 
44
- def generate_summary(date_str):
45
- df = load_field_sales()
46
- if df.empty:
47
- return pd.DataFrame([["No data"]], columns=["Message"]), pd.DataFrame(), pd.DataFrame()
48
-
49
- all_reps = sorted(df['Rep'].dropna().unique())
50
- day_df = df[df['DateStr'] == date_str]
51
- total_visits = day_df.groupby("Rep").size().reset_index(name="Total Visits")
52
-
53
- col = "Current/Prospect Custor"
54
- if col not in df.columns:
55
- df[col] = ""
56
-
57
- current = day_df[day_df[col] == "Current"]
58
- prospect = day_df[day_df[col] == "Prospect"]
59
- breakdown = pd.DataFrame({
60
- "Rep": all_reps,
61
- "Current": [len(current[current["Rep"] == rep]) for rep in all_reps],
62
- "Prospect": [len(prospect[prospect["Rep"] == rep]) for rep in all_reps]
63
- })
64
-
65
- active_list = total_visits['Rep'].tolist()
66
- inactive_list = [rep for rep in all_reps if rep not in active_list]
67
- inactive_df = pd.DataFrame({'Inactive Reps': inactive_list})
68
- return total_visits, breakdown, inactive_df
69
-
70
- def get_order_summary(date_str):
71
- df = load_field_sales()
72
- if df.empty:
73
- return pd.DataFrame([["No data"]], columns=["Message"])
74
-
75
- day_df = df[df['DateStr'] == date_str]
76
- if "Order Received" not in df.columns:
77
- df["Order Received"] = ""
78
-
79
- rep_group = day_df.groupby("Rep").agg({
80
- "Order Received": lambda x: (x == "Yes").sum(),
81
- "Order Value": "sum"
82
- }).reset_index().rename(columns={
83
- "Order Received": "Orders Received",
84
- "Order Value": "Total Order Value"
85
- })
86
- return rep_group.sort_values(by="Total Order Value", ascending=False)
87
-
88
- def get_escalations():
89
- df = load_field_sales()
90
- if df.empty:
91
- return pd.DataFrame([["No data in Field Sales"]], columns=["Message"])
92
-
93
- col = "Customer Type & Status"
94
- if col in df.columns:
95
- flagged = df[df[col].str.contains("Second", na=False)]
96
- return flagged if not flagged.empty else pd.DataFrame([["No second-hand dealerships flagged."]], columns=["Message"])
97
- else:
98
- return pd.DataFrame([["โš ๏ธ Column 'Customer Type & Status' not found."]], columns=["Message"])
99
-
100
- # ------------------ TELESALeS ------------------
101
- def get_telesales_summary():
102
- df = load_tab("TeleSales")
103
- if df.empty or "Rep Email" not in df.columns:
104
- return pd.DataFrame([["No data available"]], columns=["Message"])
105
-
106
- df["Date"] = pd.to_datetime(df.get("Date", datetime.today()), errors='coerce')
107
- df["DateStr"] = df["Date"].dt.date.astype(str)
108
-
109
- grouped = df.groupby(["Rep Email", "DateStr"]).size().reset_index(name="Calls Made")
110
- return grouped.rename(columns={"Rep Email": "Rep"})
111
-
112
- # ------------------ OEM VISITS ------------------
113
- def get_oem_summary():
114
- df = load_tab("OEM Visit")
115
- if df.empty or "Rep" not in df.columns:
116
- return pd.DataFrame([["No data available"]], columns=["Message"])
117
-
118
- df["Date"] = pd.to_datetime(df.get("Date", datetime.today()), errors='coerce')
119
- df["DateStr"] = df["Date"].dt.date.astype(str)
120
- return df.groupby(["Rep", "DateStr"]).size().reset_index(name="OEM Visits")
121
-
122
- # ------------------ CUSTOMER REQUESTS ------------------
123
- def get_requests():
124
- df = load_tab("Customer Requests")
125
- return df if not df.empty else pd.DataFrame([["No requests yet."]], columns=["Message"])
126
-
127
- # ------------------ CUSTOMER LISTINGS ------------------
128
- def get_listings():
129
- df = load_tab("CustomerListings")
130
- return df if not df.empty else pd.DataFrame([["No listings found."]], columns=["Message"])
131
-
132
- # ------------------ USERS ------------------
133
- def get_users():
134
- df = load_tab("Users")
135
- return df if not df.empty else pd.DataFrame([["No users configured."]], columns=["Message"])
136
-
137
- # ------------------ GRADIO APP ------------------
138
  with gr.Blocks() as app:
139
- with gr.Row():
140
- with gr.Column(visible=True) as login_ui:
141
- gr.Markdown("## ๐Ÿ” Login Required")
142
- email = gr.Textbox(label="Email")
143
- password = gr.Textbox(label="Password", type="password")
144
- login_btn = gr.Button("Login")
145
- login_msg = gr.Markdown("")
146
-
147
- with gr.Column(visible=False) as main_ui:
148
- gr.Markdown("## ๐Ÿ—‚๏ธ CarMat Dashboard")
149
-
150
- df_initial = load_field_sales()
151
- unique_dates = sorted(df_initial["DateStr"].unique(), reverse=True) if not df_initial.empty else []
152
-
153
- # --- Summary Tab ---
154
- with gr.Tab("๐Ÿ“Š Summary"):
155
- date_summary = gr.Dropdown(label="Select Date", choices=unique_dates)
156
- visits = gr.Dataframe(label="โœ… Total Visits")
157
- breakdown = gr.Dataframe(label="๐Ÿท๏ธ Current vs. Prospect")
158
- inactive = gr.Dataframe(label="โš ๏ธ Inactive Reps")
159
- date_summary.change(fn=generate_summary, inputs=date_summary, outputs=[visits, breakdown, inactive])
160
-
161
- # --- Orders Tab ---
162
- with gr.Tab("๐Ÿ“ฆ Orders"):
163
- order_date = gr.Dropdown(label="Select Date", choices=unique_dates)
164
- order_table = gr.Dataframe(label="๐Ÿ’ฐ Orders Summary")
165
- order_date.change(fn=get_order_summary, inputs=order_date, outputs=order_table)
166
-
167
- # --- Escalations ---
168
- with gr.Tab("๐Ÿšจ Escalations"):
169
- esc_table = gr.Dataframe(value=get_escalations, label="Second-hand Dealerships")
170
- esc_btn = gr.Button("๐Ÿ”„ Refresh")
171
- esc_btn.click(fn=get_escalations, outputs=esc_table)
172
-
173
- # --- TeleSales ---
174
- with gr.Tab("๐Ÿ“ž TeleSales"):
175
- ts_table = gr.Dataframe(value=get_telesales_summary, label="TeleSales Summary")
176
- ts_refresh = gr.Button("๐Ÿ”„ Refresh TeleSales")
177
- ts_refresh.click(fn=get_telesales_summary, outputs=ts_table)
178
-
179
- # --- OEM Visits ---
180
- with gr.Tab("๐Ÿญ OEM Visits"):
181
- oem_table = gr.Dataframe(value=get_oem_summary, label="OEM Visit Summary")
182
- oem_refresh = gr.Button("๐Ÿ”„ Refresh OEM")
183
- oem_refresh.click(fn=get_oem_summary, outputs=oem_table)
184
-
185
- # --- Requests ---
186
- with gr.Tab("๐Ÿ“ฌ Customer Requests"):
187
- req_table = gr.Dataframe(value=get_requests, label="Customer Requests", interactive=False)
188
- req_refresh = gr.Button("๐Ÿ”„ Refresh Requests")
189
- req_refresh.click(fn=get_requests, outputs=req_table)
190
-
191
- # --- Dealerships ---
192
- with gr.Tab("๐Ÿ“‹ Dealership Directory"):
193
- listings_table = gr.Dataframe(value=get_listings, label="Customer Listings")
194
- listings_refresh = gr.Button("๐Ÿ”„ Refresh Listings")
195
- listings_refresh.click(fn=get_listings, outputs=listings_table)
196
-
197
- # --- Users ---
198
- with gr.Tab("๐Ÿ‘ค Users"):
199
- users_table = gr.Dataframe(value=get_users, label="Users")
200
- users_refresh = gr.Button("๐Ÿ”„ Refresh Users")
201
- users_refresh.click(fn=get_users, outputs=users_table)
202
-
203
- def do_login(user, pw):
204
- if VALID_USERS.get(user) == pw:
205
- return gr.update(visible=False), gr.update(visible=True), ""
206
- else:
207
- return gr.update(visible=True), gr.update(visible=False), "โŒ Invalid login."
208
-
209
- login_btn.click(fn=do_login, inputs=[email, password], outputs=[login_ui, main_ui, login_msg])
210
-
211
- app.launch()
 
3
  import gradio as gr
4
  from oauth2client.service_account import ServiceAccountCredentials
5
  from datetime import datetime
6
+ from geopy.distance import geodesic
7
+ import folium
8
+ from io import BytesIO
9
 
10
+ # Google Sheets Auth
 
 
 
 
 
 
 
11
  scope = ["https://spreadsheets.google.com/feeds", "https://www.googleapis.com/auth/drive"]
12
  creds = ServiceAccountCredentials.from_json_keyfile_name("credentials.json", scope)
13
  client = gspread.authorize(creds)
14
  sheet_file = client.open("userAccess")
15
 
16
+ # Load Data
17
  def load_tab(sheet_name):
18
  try:
19
  df = pd.DataFrame(sheet_file.worksheet(sheet_name).get_all_records())
20
  return df
21
  except:
22
+ return pd.DataFrame(["โš ๏ธ Could not load sheet."], columns=["Error"])
23
+
24
+ # GPS calculations
25
+ def calculate_gps_data(df):
26
+ df = df.sort_values(['Date', 'Time'])
27
+ df[['Latitude', 'Longitude']] = df['Location'].str.split(', ', expand=True).astype(float)
28
+ df['Kms Travelled'] = df[['Latitude', 'Longitude']].shift().apply(
29
+ lambda row: geodesic((row['Latitude'], row['Longitude']), (row.name[0], row.name[1])).km if pd.notnull(row['Latitude']) else 0,
30
+ axis=1)
31
+ df['Duration Between Calls (min)'] = df[['Date', 'Time']].apply(
32
+ lambda row: pd.to_datetime(row['Date'] + ' ' + row['Time']), axis=1
33
+ ).diff().dt.total_seconds().div(60)
34
+ df.fillna({'Kms Travelled': 0, 'Duration Between Calls (min)': 0}, inplace=True)
 
 
 
35
  return df
36
 
37
+ # Load and process Field Sales data
38
+ field_sales_df = calculate_gps_data(load_tab("Field Sales"))
39
+
40
+ # Map generation
41
+ def generate_map(df):
42
+ if df.empty or df[['Latitude', 'Longitude']].isna().all().all():
43
+ return None
44
+
45
+ coords = df[['Latitude', 'Longitude']].dropna().values
46
+ map_center = coords[0]
47
+ m = folium.Map(location=map_center, zoom_start=12)
48
+
49
+ for idx, coord in enumerate(coords):
50
+ folium.Marker(location=coord, popup=f"Visit {idx+1}").add_to(m)
51
+
52
+ folium.PolyLine(coords, color='blue').add_to(m)
53
+
54
+ buf = BytesIO()
55
+ m.save(buf, close_file=False)
56
+ return buf.getvalue().decode()
57
+
58
+ # Gradio Interface
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
59
  with gr.Blocks() as app:
60
+ gr.Markdown("## ๐Ÿš— CarMat Dashboard")
61
+
62
+ unique_dates = sorted(field_sales_df['Date'].unique(), reverse=True)
63
+
64
+ # Field Sales Tab
65
+ with gr.Tab("๐Ÿ—บ๏ธ Field Sales"):
66
+ date_selector = gr.Dropdown(label="Select Date", choices=unique_dates)
67
+ data_output = gr.DataFrame()
68
+ map_html = gr.HTML()
69
+
70
+ def update_field_sales(date):
71
+ day_df = field_sales_df[field_sales_df['Date'] == date]
72
+ map_render = generate_map(day_df)
73
+ return day_df, map_render
74
+
75
+ date_selector.change(fn=update_field_sales, inputs=date_selector, outputs=[data_output, map_html])
76
+
77
+ # Summary Tab
78
+ with gr.Tab("๐Ÿ“Š Summary"):
79
+ date_summary = gr.Dropdown(label="Select Date", choices=unique_dates)
80
+ summary_visits = gr.DataFrame()
81
+
82
+ def update_summary(date):
83
+ day_df = field_sales_df[field_sales_df['Date'] == date]
84
+ visits = day_df.groupby("Rep").size().reset_index(name="Total Visits")
85
+ return visits
86
+
87
+ date_summary.change(fn=update_summary, inputs=date_summary, outputs=summary_visits)
88
+
89
+ # Orders Tab
90
+ with gr.Tab("๐Ÿ“ฆ Orders"):
91
+ order_date = gr.Dropdown(label="Select Date", choices=unique_dates)
92
+ orders_output = gr.DataFrame()
93
+
94
+ def orders_summary(date):
95
+ day_df = field_sales_df[field_sales_df['Date'] == date]
96
+ orders_df = day_df[day_df["Order Received"] == "Yes"]
97
+ summary = orders_df.groupby("Rep").agg({
98
+ "Order Value": "sum",
99
+ "Order Received": "count"
100
+ }).rename(columns={"Order Received": "Orders Count"}).reset_index()
101
+ return summary
102
+
103
+ order_date.change(fn=orders_summary, inputs=order_date, outputs=orders_output)
104
+
105
+ app.launch()