IAMTFRMZA commited on
Commit
de7d48b
Β·
verified Β·
1 Parent(s): 581bd58

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +189 -89
app.py CHANGED
@@ -3,109 +3,209 @@ import gspread
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']).reset_index(drop=True)
27
- df[['Latitude', 'Longitude']] = df['Location'].str.split(', ', expand=True).astype(float)
28
-
29
- df['Kms Travelled'] = 0.0
30
- df['Duration Between Calls (min)'] = 0.0
31
-
32
- for i in range(1, len(df)):
33
- prev_coords = (df.at[i-1, 'Latitude'], df.at[i-1, 'Longitude'])
34
- current_coords = (df.at[i, 'Latitude'], df.at[i, 'Longitude'])
35
- df.at[i, 'Kms Travelled'] = geodesic(prev_coords, current_coords).km
36
-
37
- prev_time = pd.to_datetime(df.at[i-1, 'Date'] + ' ' + df.at[i-1, 'Time'])
38
- current_time = pd.to_datetime(df.at[i, 'Date'] + ' ' + df.at[i, 'Time'])
39
- df.at[i, 'Duration Between Calls (min)'] = (current_time - prev_time).total_seconds() / 60.0
40
 
41
  return df
42
 
43
- # Load and process Field Sales data
44
- field_sales_df = calculate_gps_data(load_tab("Field Sales"))
45
-
46
- # Map generation
47
- def generate_map(df):
48
- if df.empty or df[['Latitude', 'Longitude']].isna().all().all():
49
- return None
50
-
51
- coords = df[['Latitude', 'Longitude']].dropna().values
52
- map_center = coords[0]
53
- m = folium.Map(location=map_center, zoom_start=12)
54
-
55
- for idx, coord in enumerate(coords):
56
- folium.Marker(location=coord, popup=f"Visit {idx+1}").add_to(m)
57
-
58
- folium.PolyLine(coords, color='blue').add_to(m)
59
-
60
- buf = BytesIO()
61
- m.save(buf, close_file=False)
62
- return buf.getvalue().decode()
63
-
64
- # Gradio Interface
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
65
  with gr.Blocks() as app:
66
- gr.Markdown("## πŸš— CarMat Dashboard")
67
-
68
- unique_dates = sorted(field_sales_df['Date'].unique(), reverse=True)
69
-
70
- # Field Sales Tab
71
- with gr.Tab("πŸ—ΊοΈ Field Sales"):
72
- date_selector = gr.Dropdown(label="Select Date", choices=unique_dates)
73
- data_output = gr.DataFrame()
74
- map_html = gr.HTML()
75
-
76
- def update_field_sales(date):
77
- day_df = field_sales_df[field_sales_df['Date'] == date]
78
- map_render = generate_map(day_df)
79
- return day_df, map_render
80
-
81
- date_selector.change(fn=update_field_sales, inputs=date_selector, outputs=[data_output, map_html])
82
-
83
- # Summary Tab
84
- with gr.Tab("πŸ“Š Summary"):
85
- date_summary = gr.Dropdown(label="Select Date", choices=unique_dates)
86
- summary_visits = gr.DataFrame()
87
-
88
- def update_summary(date):
89
- day_df = field_sales_df[field_sales_df['Date'] == date]
90
- visits = day_df.groupby("Rep").size().reset_index(name="Total Visits")
91
- return visits
92
-
93
- date_summary.change(fn=update_summary, inputs=date_summary, outputs=summary_visits)
94
-
95
- # Orders Tab
96
- with gr.Tab("πŸ“¦ Orders"):
97
- order_date = gr.Dropdown(label="Select Date", choices=unique_dates)
98
- orders_output = gr.DataFrame()
99
-
100
- def orders_summary(date):
101
- day_df = field_sales_df[field_sales_df['Date'] == date]
102
- orders_df = day_df[day_df["Order Received"] == "Yes"]
103
- summary = orders_df.groupby("Rep").agg({
104
- "Order Value": "sum",
105
- "Order Received": "count"
106
- }).rename(columns={"Order Received": "Orders Count"}).reset_index()
107
- return summary
108
-
109
- order_date.change(fn=orders_summary, inputs=order_date, outputs=orders_output)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
110
 
111
  app.launch()
 
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()