IAMTFRMZA commited on
Commit
b52ede6
·
verified ·
1 Parent(s): fa1e3ad

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +148 -206
app.py CHANGED
@@ -4,119 +4,109 @@ import gspread
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,149 +115,101 @@ def compute_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 Admin Dashboard") as app:
173
  gr.Markdown("# 📆 Graffiti 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()
 
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
  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("User")
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("User")
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()