IAMTFRMZA commited on
Commit
c8f0059
Β·
verified Β·
1 Parent(s): 1688c60

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +207 -149
app.py CHANGED
@@ -4,109 +4,119 @@ import gspread
4
  from gspread_dataframe import set_with_dataframe
5
  from oauth2client.service_account import ServiceAccountCredentials
6
  from datetime import datetime, timedelta
7
- from collections import Counter
 
 
 
8
 
9
  # -------------------- AUTH --------------------
10
  scope = [
11
  "https://spreadsheets.google.com/feeds",
12
  "https://www.googleapis.com/auth/drive"
13
  ]
14
- creds = ServiceAccountCredentials.from_json_keyfile_name(
15
- "deep-mile-461309-t8-0e90103411e0.json", scope
16
- )
17
  client = gspread.authorize(creds)
18
 
19
- # YOUR SPREADSHEET URL
20
- SHEET_URL = "https://docs.google.com/spreadsheets/d/1if4KoVQvw5ZbhknfdZbzMkcTiPfsD6bz9V3a1th-bwQ"
 
 
21
 
22
- # -------------------- UTILS --------------------
23
- def normalize_columns(cols):
24
- return [c.strip().title() for c in cols]
25
-
26
- def load_sheet_df(name):
27
- """
28
- Load a sheet into a DataFrame without confusing duplicates in
29
- the header row. We fetch all values, dedupe the first row,
30
- then build a DataFrame.
31
- """
32
- ws = client.open_by_url(SHEET_URL).worksheet(name)
33
- data = ws.get_all_values()
34
- if not data:
35
- return pd.DataFrame()
36
- raw_header, *rows = data
37
- # make header unique
38
- counts = Counter()
39
- header = []
40
- for col in raw_header:
41
- counts[col] += 1
42
- if counts[col] > 1:
43
- header.append(f"{col}_{counts[col]}")
44
- else:
45
- header.append(col)
46
- header = normalize_columns(header)
47
- return pd.DataFrame(rows, columns=header)
48
 
 
49
  def get_current_week_range():
50
- today = datetime.now().date()
51
  start = today - timedelta(days=today.weekday())
52
- end = start + timedelta(days=6)
53
- return start, end
54
-
55
- def filter_by_week(df, date_col, rep=None):
56
- if date_col not in df.columns:
57
- return pd.DataFrame([{"Error": f"Missing '{date_col}' column"}])
58
- df = df.copy()
59
- df[date_col] = pd.to_datetime(df[date_col], errors="coerce").dt.date
60
- start, end = get_current_week_range()
61
- m = df[date_col].between(start, end)
62
- if rep:
63
- m &= df.get("Rep", pd.Series()).astype(str) == rep
64
- return df[m]
65
-
66
- def filter_by_date(df, date_col, y, m, d, rep=None):
67
  try:
68
  target = datetime(int(y), int(m), int(d)).date()
69
  except:
70
- return pd.DataFrame([{"Error": "Invalid date"}])
71
- if date_col not in df.columns:
72
- return pd.DataFrame([{"Error": f"Missing '{date_col}' column"}])
73
- df = df.copy()
74
- df[date_col] = pd.to_datetime(df[date_col], errors="coerce").dt.date
75
- m = df[date_col] == target
76
- if rep:
77
- m &= df.get("Rep", pd.Series()).astype(str) == rep
78
- return df[m]
79
-
80
- def rep_choices(sheet, col="Rep"):
81
- df = load_sheet_df(sheet)
82
- return sorted(df[col].dropna().unique().tolist()) if col in df else []
83
-
84
- # -------------------- REPORT FUNCTIONS --------------------
85
  def get_calls(rep=None):
86
  df = load_sheet_df("Calls")
87
- return filter_by_week(df, "Call Date", rep)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88
 
89
  def get_appointments(rep=None):
90
  df = load_sheet_df("Appointments")
91
- return filter_by_week(df, "Appointment Date", rep)
 
 
92
 
93
- def search_calls(y, m, d, rep=None):
94
- df = load_sheet_df("Calls")
95
- return filter_by_date(df, "Call Date", y, m, d, rep)
 
 
 
 
 
 
 
96
 
97
- def search_appointments(y, m, d, rep=None):
98
  df = load_sheet_df("Appointments")
99
- return filter_by_date(df, "Appointment Date", y, m, d, rep)
 
 
100
 
101
- # -------------------- LEADS --------------------
102
  def get_leads_detail():
103
  df = load_sheet_df("AllocatedLeads")
 
 
 
 
104
  return df
105
 
106
  def get_leads_summary():
107
  df = get_leads_detail()
108
- if "Assigned Rep" not in df:
109
- return pd.DataFrame([{"Error": "Missing 'Assigned Rep'"}])
110
  return df.groupby("Assigned Rep").size().reset_index(name="Leads Count")
111
 
112
  # -------------------- INSIGHTS --------------------
@@ -115,101 +125,149 @@ def compute_insights():
115
  appts = get_appointments()
116
  leads = get_leads_detail()
117
 
118
- def top(df, col="Rep"):
119
- if col in df and not df.empty:
120
- vc = df[col].value_counts()
121
- return vc.idxmax() if not vc.empty else "N/A"
122
- return "N/A"
 
 
123
 
124
- data = [
125
- {"Metric": "Most Calls This Week", "Rep": top(calls, "Rep")},
126
- {"Metric": "Most Appointments This Week", "Rep": top(appts, "Rep")},
127
- {"Metric": "Most Leads Allocated", "Rep": top(leads, "Assigned Rep")},
128
- ]
129
- return pd.DataFrame(data)
 
 
 
 
 
130
 
131
  # -------------------- USER MANAGEMENT --------------------
132
  def load_users():
133
- df = load_sheet_df("Users")
134
- # select & rename your columns as needed
135
- want = [
136
- "Id", "Email", "Name", "Business", "Role",
137
- "Daily Phone Call Target", "Daily Phone Appointment Target",
138
- "Daily Quote Number Target", "Daily Quote Revenue Target",
139
- "Weekly Phone Call Target", "Weekly Phone Appointment Target",
140
- "Weekly Quote Number Target", "Weekly Quote Revenue Target",
141
- "Monthly Phone Call Target", "Monthly Phone Appointment Target",
142
- "Monthly Quote Number Target", "Monthly Quote Revenue Target",
143
  "Monthly Sales Revenue Target"
144
  ]
145
- exist = [c for c in want if c in df.columns]
146
  return df[exist]
147
 
148
  def save_users(df):
149
- ws = client.open_by_url(SHEET_URL).worksheet("Users")
150
  ws.clear()
151
- set_with_dataframe(ws, df) # writes headers + data
152
  return "βœ… Users saved!"
153
 
154
- # -------------------- UI LAYOUT --------------------
155
- with gr.Blocks(title="Graffiti Admin Dashboard") as app:
156
- gr.Markdown("# πŸ“† Graffiti Admin Dashboard")
157
 
158
- # -- Calls Tab --
159
  with gr.Tab("Calls Report"):
160
- rep_c = gr.Dropdown(choices=rep_choices("Calls"), label="Filter by Rep", allow_custom_value=True)
161
- btn_c = gr.Button("Load This Week’s Calls")
162
- tbl_c = gr.Dataframe()
163
- btn_c.click(get_calls, rep_c, tbl_c)
164
-
165
- gr.Markdown("### Search Calls by Date")
166
- y1, m1, d1 = gr.Textbox(label="Year"), gr.Textbox(label="Month"), gr.Textbox(label="Day")
167
- rep_c2 = gr.Dropdown(choices=rep_choices("Calls"), label="Filter by Rep", allow_custom_value=True)
168
- btn_c2 = gr.Button("Search")
169
- tbl_c2 = gr.Dataframe()
170
- btn_c2.click(search_calls, [y1, m1, d1, rep_c2], tbl_c2)
171
-
172
- # -- Appointments Tab --
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
173
  with gr.Tab("Appointments Report"):
174
- rep_a = gr.Dropdown(choices=rep_choices("Appointments"), label="Filter by Rep", allow_custom_value=True)
175
- btn_a = gr.Button("Load This Week’s Appts")
176
- sum_a = gr.Dataframe(label="πŸ“Š Appts by Rep")
177
- tbl_a = gr.Dataframe()
178
- def _load_appts(r):
179
- df = get_appointments(r)
180
- return df.groupby("Rep").size().reset_index(name="Count"), df
181
- btn_a.click(_load_appts, rep_a, [sum_a, tbl_a])
182
-
183
- gr.Markdown("### Search Appts by Date")
184
- y2, m2, d2 = gr.Textbox(label="Year"), gr.Textbox(label="Month"), gr.Textbox(label="Day")
185
- rep_a2 = gr.Dropdown(choices=rep_choices("Appointments"), label="Filter by Rep", allow_custom_value=True)
186
- btn_a2 = gr.Button("Search")
187
- sum_a2 = gr.Dataframe(label="πŸ“Š Appts by Rep")
188
- tbl_a2 = gr.Dataframe()
189
- def _search_appts(y,m,d,r):
190
- df = search_appointments(y,m,d,r)
191
- return df.groupby("Rep").size().reset_index(name="Count"), df
192
- btn_a2.click(_search_appts, [y2,m2,d2,rep_a2], [sum_a2, tbl_a2])
193
-
194
- # -- Appointed Leads --
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
195
  with gr.Tab("Appointed Leads"):
196
- btn_l = gr.Button("View Leads")
197
- sum_l = gr.Dataframe(label="πŸ“Š Leads by Rep")
198
- det_l = gr.Dataframe(label="πŸ”Ž Details")
199
- btn_l.click(lambda: (get_leads_summary(), get_leads_detail()), None, [sum_l, det_l])
200
 
201
- # -- Insights --
 
 
 
 
 
202
  with gr.Tab("Insights"):
203
- btn_i = gr.Button("Generate Insights")
204
- out_i = gr.Dataframe()
205
- btn_i.click(compute_insights, None, out_i)
 
206
 
207
- # -- User Management --
208
  with gr.Tab("User Management"):
209
- gr.Markdown("## πŸ‘€ Manage Users\nEdit the grid below then click **Save Users** to push back to the sheet.")
210
  users_tbl = gr.Dataframe(value=load_users(), interactive=True)
211
- save_btn = gr.Button("Save Users")
212
- save_out = gr.Textbox()
213
- save_btn.click(save_users, users_tbl, save_out)
 
214
 
215
- app.launch()
 
 
4
  from gspread_dataframe import set_with_dataframe
5
  from oauth2client.service_account import ServiceAccountCredentials
6
  from datetime import datetime, timedelta
7
+
8
+ # -------------------- CONFIG --------------------
9
+ SHEET_URL = "https://docs.google.com/spreadsheets/d/1if4KoVQvw5ZbhknfdZbzMkcTiPfsD6bz9V3a1th-bwQ"
10
+ CREDS_JSON = "deep-mile-461309-t8-0e90103411e0.json"
11
 
12
  # -------------------- AUTH --------------------
13
  scope = [
14
  "https://spreadsheets.google.com/feeds",
15
  "https://www.googleapis.com/auth/drive"
16
  ]
17
+ creds = ServiceAccountCredentials.from_json_keyfile_name(CREDS_JSON, scope)
 
 
18
  client = gspread.authorize(creds)
19
 
20
+ # -------------------- SHEET LOAD/UTILS --------------------
21
+ def normalize_columns(df):
22
+ df.columns = df.columns.str.strip().str.title()
23
+ return df
24
 
25
+ def load_sheet_df(sheet_name):
26
+ try:
27
+ ws = client.open_by_url(SHEET_URL).worksheet(sheet_name)
28
+ records= ws.get_all_records()
29
+ df = pd.DataFrame(records)
30
+ return normalize_columns(df)
31
+ except Exception as e:
32
+ # return a one-row DF with an Error column
33
+ return pd.DataFrame([{"Error": str(e)}])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34
 
35
+ # -------------------- DATE FILTERS --------------------
36
  def get_current_week_range():
37
+ today = datetime.now()
38
  start = today - timedelta(days=today.weekday())
39
+ end = start + timedelta(days=6)
40
+ return start.date(), end.date()
41
+
42
+ def filter_week(df, date_column, rep_column=None, rep=None):
43
+ df[date_column] = pd.to_datetime(df[date_column], errors="coerce").dt.date
44
+ start,end = get_current_week_range()
45
+ out = df[(df[date_column] >= start) & (df[date_column] <= end)]
46
+ if rep and rep in out.columns:
47
+ out = out[out[rep_column] == rep]
48
+ return out
49
+
50
+ def filter_date(df, date_column, rep_column, y,m,d, rep):
 
 
 
51
  try:
52
  target = datetime(int(y), int(m), int(d)).date()
53
  except:
54
+ return pd.DataFrame([{"Error":"Invalid date"}])
55
+ df[date_column] = pd.to_datetime(df[date_column], errors="coerce").dt.date
56
+ out = df[df[date_column] == target]
57
+ if rep and rep in out.columns:
58
+ out = out[out[rep_column] == rep]
59
+ return out
60
+
61
+ # -------------------- REPORT DATA --------------------
 
 
 
 
 
 
 
62
  def get_calls(rep=None):
63
  df = load_sheet_df("Calls")
64
+ if "Call Date" not in df.columns:
65
+ return pd.DataFrame([{"Error":"Missing 'Call Date' column"}])
66
+ return filter_week(df, "Call Date", "Rep", rep)
67
+
68
+ def get_calls_summary(rep=None):
69
+ df = get_calls(rep)
70
+ if "Error" in df.columns or df.empty:
71
+ return df
72
+ return (
73
+ df.groupby("Rep")
74
+ .size()
75
+ .reset_index(name="Count")
76
+ .sort_values("Count", ascending=False)
77
+ )
78
+
79
+ def search_calls_by_date(y,m,d, rep):
80
+ df = load_sheet_df("Calls")
81
+ if "Call Date" not in df.columns:
82
+ return pd.DataFrame([{"Error":"Missing 'Call Date' column"}])
83
+ return filter_date(df, "Call Date", "Rep", y,m,d, rep)
84
 
85
  def get_appointments(rep=None):
86
  df = load_sheet_df("Appointments")
87
+ if "Appointment Date" not in df.columns:
88
+ return pd.DataFrame([{"Error":"Missing 'Appointment Date' column"}])
89
+ return filter_week(df, "Appointment Date", "Rep", rep)
90
 
91
+ def get_appointments_summary(rep=None):
92
+ df = get_appointments(rep)
93
+ if "Error" in df.columns or df.empty:
94
+ return df
95
+ return (
96
+ df.groupby("Rep")
97
+ .size()
98
+ .reset_index(name="Count")
99
+ .sort_values("Count", ascending=False)
100
+ )
101
 
102
+ def search_appointments_by_date(y,m,d, rep):
103
  df = load_sheet_df("Appointments")
104
+ if "Appointment Date" not in df.columns:
105
+ return pd.DataFrame([{"Error":"Missing 'Appointment Date' column"}])
106
+ return filter_date(df, "Appointment Date", "Rep", y,m,d, rep)
107
 
 
108
  def get_leads_detail():
109
  df = load_sheet_df("AllocatedLeads")
110
+ # rename if needed
111
+ df = df.rename(columns={"Assigned Rep":"Assigned Rep"})
112
+ if "Assigned Rep" not in df.columns:
113
+ return pd.DataFrame([{"Error":"Missing 'Assigned Rep' col"}])
114
  return df
115
 
116
  def get_leads_summary():
117
  df = get_leads_detail()
118
+ if "Error" in df.columns:
119
+ return df
120
  return df.groupby("Assigned Rep").size().reset_index(name="Leads Count")
121
 
122
  # -------------------- INSIGHTS --------------------
 
125
  appts = get_appointments()
126
  leads = get_leads_detail()
127
 
128
+ def top_rep(df, col):
129
+ if "Error" in df.columns or df.empty:
130
+ return "N/A"
131
+ counts = df.groupby(col).size()
132
+ if counts.empty:
133
+ return "N/A"
134
+ return counts.idxmax()
135
 
136
+ top_calls = top_rep(calls, "Rep")
137
+ top_appts = top_rep(appts, "Rep")
138
+ # unify column name for leads
139
+ leads = leads.rename(columns={"Assigned Rep":"Rep"})
140
+ top_leads = top_rep(leads, "Rep")
141
+
142
+ return pd.DataFrame([
143
+ {"Metric":"Most Calls This Week", "Rep":top_calls},
144
+ {"Metric":"Most Appointments This Week", "Rep":top_appts},
145
+ {"Metric":"Most Leads Allocated", "Rep":top_leads},
146
+ ])
147
 
148
  # -------------------- USER MANAGEMENT --------------------
149
  def load_users():
150
+ df = load_sheet_df("userAccess") # your actual tab name
151
+ # pick & title-case only the cols you want
152
+ wanted = [
153
+ "Id","Email","Name","Business","Role",
154
+ "Daily Phone Call Target","Daily Phone Appointment Target",
155
+ "Daily Quote Number Target","Daily Quote Revenue Target",
156
+ "Weekly Phone Call Target","Weekly Phone Appointment Target",
157
+ "Weekly Quote Number Target","Weekly Quote Revenue Target",
158
+ "Monthly Phone Call Target","Monthly Phone Appointment Target",
159
+ "Monthly Quote Number Target","Monthly Quote Revenue Target",
160
  "Monthly Sales Revenue Target"
161
  ]
162
+ exist = [c for c in wanted if c in df.columns]
163
  return df[exist]
164
 
165
  def save_users(df):
166
+ ws = client.open_by_url(SHEET_URL).worksheet("userAccess")
167
  ws.clear()
168
+ set_with_dataframe(ws, df)
169
  return "βœ… Users saved!"
170
 
171
+ # -------------------- GRADIO UI --------------------
172
+ with gr.Blocks(title="πŸ“† Graffiti Field App Admin Dashboard") as app:
173
+ gr.Markdown("# πŸ“† Graffiti Field App Admin Dashboard")
174
 
175
+ # ─── Calls Report ─────────────────────────────
176
  with gr.Tab("Calls Report"):
177
+ rep_calls = gr.Dropdown(
178
+ label="Optional Rep Filter",
179
+ choices=load_sheet_df("Calls")["Rep"].dropna().unique().tolist(),
180
+ allow_custom_value=True
181
+ )
182
+ calls_btn = gr.Button("Load Current Week Calls")
183
+ calls_summary = gr.Dataframe(label="πŸ“Š Calls by Rep")
184
+ calls_table = gr.Dataframe(label="πŸ”Ž Detailed Calls")
185
+
186
+ calls_btn.click(
187
+ fn=lambda r: (get_calls_summary(r), get_calls(r)),
188
+ inputs=rep_calls,
189
+ outputs=[calls_summary, calls_table]
190
+ )
191
+
192
+ gr.Markdown("### πŸ” Search Calls by Specific Date")
193
+ y1,m1,d1 = gr.Textbox(label="Year"), gr.Textbox(label="Month"), gr.Textbox(label="Day")
194
+ rep1 = gr.Dropdown(
195
+ label="Optional Rep Filter",
196
+ choices=load_sheet_df("Calls")["Rep"].dropna().unique().tolist(),
197
+ allow_custom_value=True
198
+ )
199
+ calls_date_btn = gr.Button("Search Calls by Date")
200
+ calls_date_table = gr.Dataframe()
201
+
202
+ calls_date_btn.click(
203
+ fn=search_calls_by_date,
204
+ inputs=[y1,m1,d1,rep1],
205
+ outputs=calls_date_table
206
+ )
207
+
208
+ # ─── Appointments Report ─────────────────────
209
  with gr.Tab("Appointments Report"):
210
+ rep_appt = gr.Dropdown(
211
+ label="Optional Rep Filter",
212
+ choices=load_sheet_df("Appointments")["Rep"].dropna().unique().tolist(),
213
+ allow_custom_value=True
214
+ )
215
+ appt_btn = gr.Button("Load Current Week Appointments")
216
+ appt_summary = gr.Dataframe(label="πŸ“Š Appts by Rep")
217
+ appt_table = gr.Dataframe(label="πŸ”Ž Detailed Appointments")
218
+
219
+ appt_btn.click(
220
+ fn=lambda r: (get_appointments_summary(r), get_appointments(r)),
221
+ inputs=rep_appt,
222
+ outputs=[appt_summary, appt_table]
223
+ )
224
+
225
+ gr.Markdown("### πŸ” Search Appointments by Specific Date")
226
+ y2,m2,d2 = gr.Textbox(label="Year"), gr.Textbox(label="Month"), gr.Textbox(label="Day")
227
+ rep2 = gr.Dropdown(
228
+ label="Optional Rep Filter",
229
+ choices=load_sheet_df("Appointments")["Rep"].dropna().unique().tolist(),
230
+ allow_custom_value=True
231
+ )
232
+ appt_date_btn = gr.Button("Search Appts by Date")
233
+ appt_date_summary = gr.Dataframe(label="πŸ“Š Appts Summary by Rep")
234
+ appt_date_table = gr.Dataframe()
235
+
236
+ appt_date_btn.click(
237
+ fn=lambda y,m,d,r: (
238
+ (lambda df: df.groupby("Rep").size().reset_index(name="Count"))(search_appointments_by_date(y,m,d,r)),
239
+ search_appointments_by_date(y,m,d,r)
240
+ ),
241
+ inputs=[y2,m2,d2,rep2],
242
+ outputs=[appt_date_summary, appt_date_table]
243
+ )
244
+
245
+ # ─── Appointed Leads ─���────────────────────────
246
  with gr.Tab("Appointed Leads"):
247
+ leads_btn = gr.Button("View Appointed Leads")
248
+ leads_summary = gr.Dataframe(label="πŸ“Š Leads Count by Rep")
249
+ leads_detail = gr.Dataframe(label="πŸ”Ž Detailed Leads")
 
250
 
251
+ leads_btn.click(
252
+ fn=lambda: (get_leads_summary(), get_leads_detail()),
253
+ outputs=[leads_summary, leads_detail]
254
+ )
255
+
256
+ # ─── Insights ─────────────────────────────────
257
  with gr.Tab("Insights"):
258
+ insights_btn = gr.Button("Generate Insights")
259
+ insights_tbl = gr.Dataframe()
260
+
261
+ insights_btn.click(fn=compute_insights, outputs=insights_tbl)
262
 
263
+ # ─── User Management ──────────────────────────
264
  with gr.Tab("User Management"):
265
+ gr.Markdown("## πŸ‘€ Manage Users\nEdit/add/remove rows below, then click **Save Users**.")
266
  users_tbl = gr.Dataframe(value=load_users(), interactive=True)
267
+ save_btn = gr.Button("Save Users")
268
+ status = gr.Textbox()
269
+
270
+ save_btn.click(fn=save_users, inputs=users_tbl, outputs=status)
271
 
272
+ # end Blocks
273
+ app.launch()