IAMTFRMZA commited on
Commit
9424917
Β·
verified Β·
1 Parent(s): b1c35dc

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +136 -164
app.py CHANGED
@@ -1,76 +1,61 @@
 
1
  import gradio as gr
2
  import pandas as pd
3
  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 = ["https://spreadsheets.google.com/feeds",
14
- "https://www.googleapis.com/auth/drive"]
15
- creds = ServiceAccountCredentials.from_json_keyfile_name(CREDS_JSON, scope)
 
 
 
 
16
  client = gspread.authorize(creds)
17
 
18
- # -------------------- WORKSHEET HELPERS --------------------
19
- def open_ws(name_substr):
20
- """
21
- Try to open a worksheet:
22
- 1. exact match on title
23
- 2. first sheet whose title contains name_substr (case-insensitive)
24
- """
25
- sh = client.open_by_url(SHEET_URL)
26
- # 1) exact
27
- try:
28
- return sh.worksheet(name_substr)
29
- except gspread.WorksheetNotFound:
30
- pass
31
-
32
- # 2) contains substring
33
- for ws in sh.worksheets():
34
- if name_substr.lower() in ws.title.lower():
35
- return ws
36
- raise gspread.WorksheetNotFound(f"No tab matching '{name_substr}'")
37
-
38
- def load_sheet_df(tab_name):
39
- """Load & normalize the sheet named by tab_name (substring)."""
40
  try:
41
- ws = open_ws(tab_name)
42
  df = pd.DataFrame(ws.get_all_records())
43
- df.columns = df.columns.str.strip().str.title()
44
- return df
45
  except Exception as e:
46
  return pd.DataFrame([{"Error": str(e)}])
47
 
48
- def find_rep_column(df):
49
- """Find the first column whose header contains 'rep' (case-insensitive)."""
50
- for c in df.columns:
51
- if "rep" in c.lower():
52
- return c
53
- return None
54
-
55
- def rep_options(tab_name):
56
- """Return a list of all unique reps in the given tab (or empty list)."""
57
- df = load_sheet_df(tab_name)
58
- rep_col = find_rep_column(df)
59
- if rep_col:
60
- return sorted(df[rep_col].dropna().unique().tolist())
61
- return []
62
 
63
- # -------------------- DATE FILTERS --------------------
64
  def get_current_week_range():
65
- today = datetime.now()
66
  start = today - timedelta(days=today.weekday())
67
- return start.date(), (start + timedelta(days=6)).date()
 
68
 
69
- def filter_week(df, date_col, rep_col, rep):
70
  df[date_col] = pd.to_datetime(df[date_col], errors="coerce").dt.date
71
  start, end = get_current_week_range()
72
  out = df[(df[date_col] >= start) & (df[date_col] <= end)]
73
- if rep and rep_col in out.columns:
74
  out = out[out[rep_col] == rep]
75
  return out
76
 
@@ -78,88 +63,75 @@ def filter_date(df, date_col, rep_col, y, m, d, rep):
78
  try:
79
  target = datetime(int(y), int(m), int(d)).date()
80
  except:
81
- return pd.DataFrame([{"Error":"Invalid date"}])
82
  df[date_col] = pd.to_datetime(df[date_col], errors="coerce").dt.date
83
  out = df[df[date_col] == target]
84
- if rep and rep_col in out.columns:
85
  out = out[out[rep_col] == rep]
86
  return out
87
 
88
- # -------------------- CALLS --------------------
89
  def get_calls(rep=None):
90
- df = load_sheet_df("Calls")
91
  if "Call Date" not in df.columns:
92
- return pd.DataFrame([{"Error":"Missing 'Call Date' column"}])
93
- return filter_week(df, "Call Date", find_rep_column(df), rep)
94
-
95
- def get_calls_summary(rep=None):
96
- df = get_calls(rep)
97
- if "Error" in df.columns or df.empty:
98
  return df
99
- col = find_rep_column(df)
100
- return df.groupby(col).size().reset_index(name="Count")
101
-
102
- def search_calls_by_date(y,m,d,rep):
103
- df = load_sheet_df("Calls")
104
- if "Call Date" not in df.columns:
105
- return pd.DataFrame([{"Error":"Missing 'Call Date' column"}])
106
- return filter_date(df, "Call Date", find_rep_column(df), y,m,d, rep)
107
 
108
- # -------------------- APPOINTMENTS --------------------
109
  def get_appointments(rep=None):
110
- df = load_sheet_df("Appointments")
111
  if "Appointment Date" not in df.columns:
112
- return pd.DataFrame([{"Error":"Missing 'Appointment Date' column"}])
113
- return filter_week(df, "Appointment Date", find_rep_column(df), rep)
114
 
115
- def get_appointments_summary(rep=None):
116
- df = get_appointments(rep)
117
- if "Error" in df.columns or df.empty:
118
  return df
119
- col = find_rep_column(df)
120
- return df.groupby(col).size().reset_index(name="Count")
121
 
122
- def search_appointments_by_date(y,m,d,rep):
123
- df = load_sheet_df("Appointments")
124
  if "Appointment Date" not in df.columns:
125
- return pd.DataFrame([{"Error":"Missing 'Appointment Date' column"}])
126
- return filter_date(df, "Appointment Date", find_rep_column(df), y,m,d, rep)
127
 
128
- # -------------------- APPOINTED LEADS --------------------
129
  def get_leads_detail():
130
- return load_sheet_df("Leads")
 
131
 
132
  def get_leads_summary():
133
  df = get_leads_detail()
134
- col = find_rep_column(df) or "Assigned Rep"
135
- if col not in df.columns:
136
- return pd.DataFrame([{"Error":"Missing rep column in leads"}])
137
- return df.groupby(col).size().reset_index(name="Leads Count")
138
 
139
- # -------------------- INSIGHTS --------------------
140
  def compute_insights():
141
  calls = get_calls()
142
  appts = get_appointments()
143
  leads = get_leads_detail()
144
 
145
- def top(df):
146
- col = find_rep_column(df)
147
- if "Error" in df.columns or df.empty or not col:
148
  return "N/A"
149
- s = df.groupby(col).size()
150
- return s.idxmax() if not s.empty else "N/A"
 
 
 
 
 
 
151
 
152
- return pd.DataFrame([
153
- {"Metric":"Most Calls This Week", "Rep": top(calls)},
154
- {"Metric":"Most Appointments This Week", "Rep": top(appts)},
155
- {"Metric":"Most Leads Allocated", "Rep": top(leads)},
156
- ])
157
 
158
  # -------------------- USER MANAGEMENT --------------------
159
- def load_users():
160
- df = load_sheet_df("Users")
161
- want = [
162
- "Id","Email","Name","Business","Role",
163
  "Daily Phone Call Target","Daily Phone Appointment Target",
164
  "Daily Quote Number Target","Daily Quote Revenue Target",
165
  "Weekly Phone Call Target","Weekly Phone Appointment Target",
@@ -168,83 +140,83 @@ def load_users():
168
  "Monthly Quote Number Target","Monthly Quote Revenue Target",
169
  "Monthly Sales Revenue Target"
170
  ]
171
- cols = [c for c in want if c in df.columns]
172
- return df[cols]
 
 
 
173
 
174
- def save_users(df):
175
- ws = open_ws("Users")
176
  ws.clear()
177
  set_with_dataframe(ws, df)
178
  return "βœ… Users saved!"
179
 
180
- # -------------------- GRADIO UI --------------------
181
  with gr.Blocks(title="Graffiti Admin Dashboard") as app:
182
  gr.Markdown("# πŸ“† Graffiti Admin Dashboard")
183
 
184
- # --- Calls ---
185
  with gr.Tab("Calls Report"):
186
- rc = rep_options("Calls")
187
- rep_calls = gr.Dropdown(rc or ["(no reps found)"],
188
- label="Optional Rep Filter",
189
- allow_custom_value=True)
190
- calls_btn = gr.Button("Load Current Week Calls")
191
- calls_sum = gr.Dataframe(label="πŸ“Š Calls by Rep")
192
- calls_det = gr.Dataframe(label="πŸ”Ž Detailed Calls")
193
- calls_btn.click(lambda r: (get_calls_summary(r), get_calls(r)),
194
- inputs=rep_calls, outputs=[calls_sum, calls_det])
195
-
196
- gr.Markdown("### πŸ” Search Calls by Date")
197
  y1,m1,d1 = gr.Textbox(label="Year"), gr.Textbox(label="Month"), gr.Textbox(label="Day")
198
- rep1 = gr.Dropdown(rc or ["(no reps found)"],
199
- label="Optional Rep Filter", allow_custom_value=True)
200
- calls_dt_btn = gr.Button("Search Calls by Date")
201
- calls_dt_tbl = gr.Dataframe()
202
- calls_dt_btn.click(search_calls_by_date,
203
- inputs=[y1,m1,d1,rep1], outputs=calls_dt_tbl)
204
-
205
- # --- Appointments ---
206
  with gr.Tab("Appointments Report"):
207
- ra = rep_options("Appointments")
208
- rep_appt = gr.Dropdown(ra or ["(no reps found)"],
209
- label="Optional Rep Filter",
210
- allow_custom_value=True)
211
- appt_btn = gr.Button("Load Current Week Appointments")
212
- appt_sum = gr.Dataframe(label="πŸ“Š Appts by Rep")
213
- appt_det = gr.Dataframe(label="πŸ”Ž Detailed Appts")
214
- appt_btn.click(lambda r: (get_appointments_summary(r), get_appointments(r)),
215
- inputs=rep_appt, outputs=[appt_sum, appt_det])
216
-
217
- gr.Markdown("### πŸ” Search Appts by Date")
 
218
  y2,m2,d2 = gr.Textbox(label="Year"), gr.Textbox(label="Month"), gr.Textbox(label="Day")
219
- rep2 = gr.Dropdown(ra or ["(no reps found)"],
220
- label="Optional Rep Filter", allow_custom_value=True)
221
- appt_dt_btn = gr.Button("Search Appts by Date")
222
- appt_dt_sum = gr.Dataframe(label="πŸ“Š Appts by Rep")
223
- appt_dt_det = gr.Dataframe(label="πŸ”Ž Detailed Appts")
224
- appt_dt_btn.click(search_appointments_by_date,
225
- inputs=[y2,m2,d2,rep2],
226
- outputs=appt_dt_det)
227
-
228
- # --- Appointed Leads ---
 
 
 
 
229
  with gr.Tab("Appointed Leads"):
230
- leads_btn = gr.Button("View Appointed Leads")
231
- leads_sum = gr.Dataframe(label="πŸ“Š Leads Count by Rep")
232
- leads_det = gr.Dataframe(label="πŸ”Ž Detailed Leads")
233
- leads_btn.click(lambda: (get_leads_summary(), get_leads_detail()),
234
- outputs=[leads_sum, leads_det])
235
 
236
- # --- Insights ---
237
  with gr.Tab("Insights"):
238
- ins_btn = gr.Button("Generate Insights")
239
- ins_tbl = gr.Dataframe()
240
- ins_btn.click(compute_insights, outputs=ins_tbl)
241
 
242
- # --- User Management ---
243
  with gr.Tab("User Management"):
244
- gr.Markdown("## πŸ‘€ Manage Users β€” edit/add/remove, then Save")
245
- users_df = gr.Dataframe(load_users(), interactive=True)
246
- save_btn = gr.Button("Save Users")
247
- save_out = gr.Textbox()
248
- save_btn.click(save_users, inputs=users_df, outputs=save_out)
249
 
250
- app.launch()
 
1
+ # app.py
2
  import gradio as gr
3
  import pandas as pd
4
  import gspread
5
+ from gspread.exceptions import WorksheetNotFound
6
  from oauth2client.service_account import ServiceAccountCredentials
7
  from datetime import datetime, timedelta
8
+ from gspread_dataframe import set_with_dataframe
9
 
10
  # -------------------- CONFIG --------------------
11
+ SHEET_URL = "https://docs.google.com/spreadsheets/d/1if4KoVQvw5ZbhknfdZbzMkcTiPfsD6bz9V3a1th-bwQ"
12
+ USER_SHEET_NAME = "Users" # <--- make sure this tab exists
13
 
14
  # -------------------- AUTH --------------------
15
+ scope = [
16
+ "https://spreadsheets.google.com/feeds",
17
+ "https://www.googleapis.com/auth/drive",
18
+ ]
19
+ creds = ServiceAccountCredentials.from_json_keyfile_name(
20
+ "deep-mile-461309-t8-0e90103411e0.json", scope
21
+ )
22
  client = gspread.authorize(creds)
23
 
24
+ # -------------------- SHEET UTILS --------------------
25
+ def normalize_columns(df: pd.DataFrame) -> pd.DataFrame:
26
+ df.columns = df.columns.str.strip().str.title()
27
+ return df
28
+
29
+ def load_sheet(sheet_name: str) -> pd.DataFrame:
30
+ """Loads an entire sheet into a DataFrame, or returns a one-row error."""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
31
  try:
32
+ ws = client.open_by_url(SHEET_URL).worksheet(sheet_name)
33
  df = pd.DataFrame(ws.get_all_records())
34
+ return normalize_columns(df)
 
35
  except Exception as e:
36
  return pd.DataFrame([{"Error": str(e)}])
37
 
38
+ def load_sheet_df(sheet_name: str) -> pd.DataFrame:
39
+ """Same as load_sheet but re-raises WorksheetNotFound for callers to catch."""
40
+ try:
41
+ ws = client.open_by_url(SHEET_URL).worksheet(sheet_name)
42
+ except WorksheetNotFound:
43
+ raise
44
+ df = pd.DataFrame(ws.get_all_records())
45
+ return normalize_columns(df)
 
 
 
 
 
 
46
 
47
+ # -------------------- WEEK FILTERS --------------------
48
  def get_current_week_range():
49
+ today = datetime.now().date()
50
  start = today - timedelta(days=today.weekday())
51
+ end = start + timedelta(days=6)
52
+ return start, end
53
 
54
+ def filter_week(df, date_col, rep_col=None, rep=None):
55
  df[date_col] = pd.to_datetime(df[date_col], errors="coerce").dt.date
56
  start, end = get_current_week_range()
57
  out = df[(df[date_col] >= start) & (df[date_col] <= end)]
58
+ if rep:
59
  out = out[out[rep_col] == rep]
60
  return out
61
 
 
63
  try:
64
  target = datetime(int(y), int(m), int(d)).date()
65
  except:
66
+ return pd.DataFrame([{"Error": "Invalid date"}])
67
  df[date_col] = pd.to_datetime(df[date_col], errors="coerce").dt.date
68
  out = df[df[date_col] == target]
69
+ if rep:
70
  out = out[out[rep_col] == rep]
71
  return out
72
 
73
+ # -------------------- REPORT FUNCTIONS --------------------
74
  def get_calls(rep=None):
75
+ df = load_sheet("Calls")
76
  if "Call Date" not in df.columns:
 
 
 
 
 
 
77
  return df
78
+ return filter_week(df, "Call Date", "Rep", rep)
 
 
 
 
 
 
 
79
 
 
80
  def get_appointments(rep=None):
81
+ df = load_sheet("Appointments")
82
  if "Appointment Date" not in df.columns:
83
+ return df
84
+ return filter_week(df, "Appointment Date", "Rep", rep)
85
 
86
+ def search_calls_by_date(y, m, d, rep):
87
+ df = load_sheet("Calls")
88
+ if "Call Date" not in df.columns:
89
  return df
90
+ return filter_date(df, "Call Date", "Rep", y, m, d, rep)
 
91
 
92
+ def search_appointments_by_date(y, m, d, rep):
93
+ df = load_sheet("Appointments")
94
  if "Appointment Date" not in df.columns:
95
+ return df
96
+ return filter_date(df, "Appointment Date", "Rep", y, m, d, rep)
97
 
 
98
  def get_leads_detail():
99
+ df = load_sheet("AllocatedLeads")
100
+ return df
101
 
102
  def get_leads_summary():
103
  df = get_leads_detail()
104
+ if "Error" in df.columns:
105
+ return df
106
+ return df.groupby("Assigned Rep").size().reset_index(name="Leads Count")
 
107
 
 
108
  def compute_insights():
109
  calls = get_calls()
110
  appts = get_appointments()
111
  leads = get_leads_detail()
112
 
113
+ def top_rep(df, col):
114
+ if "Error" in df.columns or df.empty or col not in df.columns:
 
115
  return "N/A"
116
+ return df.groupby(col).size().idxmax()
117
+
118
+ data = [
119
+ {"Metric": "Most Calls This Week", "Rep": top_rep(calls, "Rep")},
120
+ {"Metric": "Most Appointments This Week", "Rep": top_rep(appts, "Rep")},
121
+ {"Metric": "Most Leads Allocated", "Rep": top_rep(leads, "Assigned Rep")},
122
+ ]
123
+ return pd.DataFrame(data)
124
 
125
+ def rep_options(sheet, col):
126
+ df = load_sheet(sheet)
127
+ if col in df.columns:
128
+ return sorted(df[col].dropna().unique().tolist())
129
+ return []
130
 
131
  # -------------------- USER MANAGEMENT --------------------
132
+ def load_users() -> pd.DataFrame:
133
+ cols = [
134
+ "ID","Email","Name","Business","Role",
 
135
  "Daily Phone Call Target","Daily Phone Appointment Target",
136
  "Daily Quote Number Target","Daily Quote Revenue Target",
137
  "Weekly Phone Call Target","Weekly Phone Appointment Target",
 
140
  "Monthly Quote Number Target","Monthly Quote Revenue Target",
141
  "Monthly Sales Revenue Target"
142
  ]
143
+ try:
144
+ return load_sheet_df(USER_SHEET_NAME)
145
+ except WorksheetNotFound:
146
+ # fall back to empty skeleton
147
+ return pd.DataFrame(columns=cols)
148
 
149
+ def save_users(df: pd.DataFrame):
150
+ ws = client.open_by_url(SHEET_URL).worksheet(USER_SHEET_NAME)
151
  ws.clear()
152
  set_with_dataframe(ws, df)
153
  return "βœ… Users saved!"
154
 
155
+ # -------------------- BUILD UI --------------------
156
  with gr.Blocks(title="Graffiti Admin Dashboard") as app:
157
  gr.Markdown("# πŸ“† Graffiti Admin Dashboard")
158
 
159
+ # -- Calls Tab --
160
  with gr.Tab("Calls Report"):
161
+ rep_calls = gr.Dropdown(choices=rep_options("Calls","Rep"), label="Optional Rep Filter", allow_custom_value=True)
162
+ calls_btn = gr.Button("Load Current Week Calls")
163
+ calls_tbl = gr.Dataframe()
164
+ calls_btn.click(fn=get_calls, inputs=rep_calls, outputs=calls_tbl)
165
+
166
+ gr.Markdown("### πŸ” Search Calls by Specific Date")
 
 
 
 
 
167
  y1,m1,d1 = gr.Textbox(label="Year"), gr.Textbox(label="Month"), gr.Textbox(label="Day")
168
+ rep1 = gr.Dropdown(choices=rep_options("Calls","Rep"), label="Optional Rep Filter", allow_custom_value=True)
169
+ calls_date_btn = gr.Button("Search Calls by Date")
170
+ calls_date_tbl = gr.Dataframe()
171
+ calls_date_btn.click(fn=search_calls_by_date, inputs=[y1,m1,d1,rep1], outputs=calls_date_tbl)
172
+
173
+ # -- Appointments Tab --
 
 
174
  with gr.Tab("Appointments Report"):
175
+ rep_appt = gr.Dropdown(choices=rep_options("Appointments","Rep"), label="Optional Rep Filter", allow_custom_value=True)
176
+ appt_btn = gr.Button("Load Current Week Appointments")
177
+ appt_sum = gr.Dataframe(label="πŸ“Š Weekly Appointments Summary by Rep")
178
+ appt_tbl = gr.Dataframe()
179
+ appt_btn.click(
180
+ fn=lambda r: ( get_appointments(r).groupby("Rep").size().reset_index(name="Count"),
181
+ get_appointments(r) ),
182
+ inputs=rep_appt,
183
+ outputs=[appt_sum, appt_tbl]
184
+ )
185
+
186
+ gr.Markdown("### πŸ” Search Appointments by Specific Date")
187
  y2,m2,d2 = gr.Textbox(label="Year"), gr.Textbox(label="Month"), gr.Textbox(label="Day")
188
+ rep2 = gr.Dropdown(choices=rep_options("Appointments","Rep"), label="Optional Rep Filter", allow_custom_value=True)
189
+ appt_date_btn = gr.Button("Search Appointments by Date")
190
+ appt_date_sum = gr.Dataframe(label="πŸ“Š Appointments Summary for Date by Rep")
191
+ appt_date_tbl = gr.Dataframe()
192
+ appt_date_btn.click(
193
+ fn=lambda y,m,d,r: (
194
+ search_appointments_by_date(y,m,d,r).groupby("Rep").size().reset_index(name="Count"),
195
+ search_appointments_by_date(y,m,d,r)
196
+ ),
197
+ inputs=[y2,m2,d2,rep2],
198
+ outputs=[appt_date_sum, appt_date_tbl]
199
+ )
200
+
201
+ # -- Appointed Leads Tab --
202
  with gr.Tab("Appointed Leads"):
203
+ leads_btn = gr.Button("View Appointed Leads")
204
+ leads_sum = gr.Dataframe(label="πŸ“Š Leads Count by Rep")
205
+ leads_detail = gr.Dataframe(label="πŸ”Ž Detailed Leads")
206
+ leads_btn.click(fn=lambda: (get_leads_summary(), get_leads_detail()), outputs=[leads_sum, leads_detail])
 
207
 
208
+ # -- Insights Tab --
209
  with gr.Tab("Insights"):
210
+ insights_btn = gr.Button("Generate Insights")
211
+ insights_tbl = gr.Dataframe()
212
+ insights_btn.click(fn=compute_insights, outputs=insights_tbl)
213
 
214
+ # -- User Management Tab --
215
  with gr.Tab("User Management"):
216
+ gr.Markdown("### πŸ™ Manage Users\nEdit the grid and click **Save Users** to push changes.")
217
+ users_tbl = gr.Dataframe(value=load_users(), interactive=True)
218
+ save_btn = gr.Button("Save Users")
219
+ save_msg = gr.Textbox(interactive=False)
220
+ save_btn.click(fn=save_users, inputs=users_tbl, outputs=save_msg)
221
 
222
+ app.launch()