Spaces:
Paused
Paused
Update app.py
Browse files
app.py
CHANGED
|
@@ -82,35 +82,28 @@ def fetch_todays_messages(slack: WebClient, log: list) -> list:
|
|
| 82 |
|
| 83 |
|
| 84 |
# βββ STEP 2B: CHECK LEAVE STATUS VIA SLACK PROFILE ββββββββββββββββββββββββββ
|
| 85 |
-
def is_on_leave_today(name: str,
|
| 86 |
-
"""Use Claude to
|
| 87 |
-
if not
|
| 88 |
return False
|
| 89 |
|
| 90 |
today = datetime.datetime.now(IST).strftime("%d-%m-%Y")
|
| 91 |
|
| 92 |
-
prompt = f"""Today
|
| 93 |
|
| 94 |
-
Intern
|
| 95 |
-
Slack status text: "{status_text}"
|
| 96 |
-
Slack status emoji: "{status_emoji}"
|
| 97 |
|
| 98 |
-
Is this intern on leave TODAY?
|
| 99 |
-
|
| 100 |
-
|
| 101 |
-
- Red circle emoji or similar leave indicators
|
| 102 |
-
- Any combination of emoji + text suggesting absence
|
| 103 |
-
|
| 104 |
-
Reply with ONLY "yes" or "no"."""
|
| 105 |
|
| 106 |
try:
|
| 107 |
resp = claude_client.messages.create(
|
| 108 |
model="claude-sonnet-4-20250514",
|
| 109 |
-
max_tokens=
|
| 110 |
messages=[{"role": "user", "content": prompt}]
|
| 111 |
)
|
| 112 |
-
|
| 113 |
-
return answer.startswith("yes")
|
| 114 |
except Exception:
|
| 115 |
return False
|
| 116 |
|
|
@@ -123,22 +116,22 @@ def get_interns_on_leave(slack: WebClient, claude_client, interns: list, log: li
|
|
| 123 |
|
| 124 |
for intern in interns:
|
| 125 |
try:
|
| 126 |
-
|
| 127 |
-
|
|
|
|
| 128 |
status_text = profile.get("status_text", "")
|
| 129 |
status_emoji = profile.get("status_emoji", "")
|
|
|
|
| 130 |
|
| 131 |
-
if not
|
| 132 |
continue
|
| 133 |
|
| 134 |
-
on_leave_today = is_on_leave_today(
|
| 135 |
-
intern["name"], status_text, status_emoji, claude_client
|
| 136 |
-
)
|
| 137 |
if on_leave_today:
|
| 138 |
on_leave.add(intern["userId"])
|
| 139 |
-
log.append(f" {intern['name']}
|
| 140 |
except Exception as e:
|
| 141 |
-
log.append(f" Could not check
|
| 142 |
|
| 143 |
return on_leave
|
| 144 |
|
|
@@ -277,49 +270,59 @@ def get_alert_channel_id(slack: WebClient, log: list) -> str:
|
|
| 277 |
|
| 278 |
# βββ STEP 5: BUILD SLACK MESSAGE ββββββββββββββββββββββββββββββββββββββββββββββ
|
| 279 |
def build_slack_message(results: dict, evals: dict, run_time: str) -> str:
|
| 280 |
-
|
| 281 |
-
|
| 282 |
-
|
| 283 |
-
|
| 284 |
-
|
|
|
|
|
|
|
| 285 |
|
| 286 |
total = len(results)
|
|
|
|
| 287 |
date_str = datetime.datetime.now(IST).strftime("%d %b %Y")
|
|
|
|
| 288 |
lines = []
|
| 289 |
-
lines.append(f"<!channel>
|
| 290 |
-
lines.append(f"
|
|
|
|
|
|
|
| 291 |
lines.append("")
|
| 292 |
|
| 293 |
-
|
| 294 |
-
if on_leave_list:
|
| 295 |
-
lines.append(f"*On Leave ({len(on_leave_list)}/{total})*")
|
| 296 |
-
for r in on_leave_list:
|
| 297 |
-
lines.append(f" :red_circle: *{r['name']}* β on approved leave")
|
| 298 |
-
lines.append("")
|
| 299 |
-
|
| 300 |
if submitted:
|
| 301 |
-
lines.append(f"*
|
| 302 |
-
for uid, r in
|
| 303 |
-
if not r["submitted"]
|
| 304 |
continue
|
| 305 |
-
ev
|
| 306 |
-
q
|
| 307 |
-
sc
|
| 308 |
-
|
| 309 |
flags = ev.get("flags", [])
|
| 310 |
-
line = f" {
|
| 311 |
if flags:
|
| 312 |
-
line +=
|
| 313 |
lines.append(line)
|
| 314 |
lines.append("")
|
| 315 |
|
|
|
|
| 316 |
if missed:
|
| 317 |
-
lines.append(f"*
|
| 318 |
for r in missed:
|
| 319 |
-
lines.append(f" :
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 320 |
lines.append("")
|
| 321 |
|
| 322 |
-
|
|
|
|
|
|
|
| 323 |
|
| 324 |
return "\n".join(lines)
|
| 325 |
|
|
|
|
| 82 |
|
| 83 |
|
| 84 |
# βββ STEP 2B: CHECK LEAVE STATUS VIA SLACK PROFILE ββββββββββββββββββββββββββ
|
| 85 |
+
def is_on_leave_today(name: str, full_status: str, claude_client) -> bool:
|
| 86 |
+
"""Use Claude to determine if intern is on leave today from their full Slack status string."""
|
| 87 |
+
if not full_status or full_status.strip() == "":
|
| 88 |
return False
|
| 89 |
|
| 90 |
today = datetime.datetime.now(IST).strftime("%d-%m-%Y")
|
| 91 |
|
| 92 |
+
prompt = f"""Today is {today}.
|
| 93 |
|
| 94 |
+
Intern Slack status: "{full_status}"
|
|
|
|
|
|
|
| 95 |
|
| 96 |
+
Is this intern on leave TODAY?
|
| 97 |
+
Consider: date ranges, "on leave", "off", "absent", :red_circle: emoji, away messages.
|
| 98 |
+
Reply ONLY with "yes" or "no"."""
|
|
|
|
|
|
|
|
|
|
|
|
|
| 99 |
|
| 100 |
try:
|
| 101 |
resp = claude_client.messages.create(
|
| 102 |
model="claude-sonnet-4-20250514",
|
| 103 |
+
max_tokens=5,
|
| 104 |
messages=[{"role": "user", "content": prompt}]
|
| 105 |
)
|
| 106 |
+
return resp.content[0].text.strip().lower().startswith("yes")
|
|
|
|
| 107 |
except Exception:
|
| 108 |
return False
|
| 109 |
|
|
|
|
| 116 |
|
| 117 |
for intern in interns:
|
| 118 |
try:
|
| 119 |
+
# Use users.info for reliable status reading
|
| 120 |
+
resp = slack.users_info(user=intern["userId"])
|
| 121 |
+
profile = resp.get("user", {}).get("profile", {})
|
| 122 |
status_text = profile.get("status_text", "")
|
| 123 |
status_emoji = profile.get("status_emoji", "")
|
| 124 |
+
full_status = f"{status_emoji} {status_text}".strip()
|
| 125 |
|
| 126 |
+
if not full_status:
|
| 127 |
continue
|
| 128 |
|
| 129 |
+
on_leave_today = is_on_leave_today(intern["name"], full_status, claude_client)
|
|
|
|
|
|
|
| 130 |
if on_leave_today:
|
| 131 |
on_leave.add(intern["userId"])
|
| 132 |
+
log.append(f" {intern['name']} β on leave ({full_status})")
|
| 133 |
except Exception as e:
|
| 134 |
+
log.append(f" Could not check {intern['name']}: {e}")
|
| 135 |
|
| 136 |
return on_leave
|
| 137 |
|
|
|
|
| 270 |
|
| 271 |
# βββ STEP 5: BUILD SLACK MESSAGE ββββββββββββββββββββββββββββββββββββββββββββββ
|
| 272 |
def build_slack_message(results: dict, evals: dict, run_time: str) -> str:
|
| 273 |
+
active = {uid: r for uid, r in results.items() if not r.get("on_leave")}
|
| 274 |
+
submitted = [r for r in active.values() if r["submitted"]]
|
| 275 |
+
missed = [r for r in active.values() if not r["submitted"]]
|
| 276 |
+
on_leave_list = [r for r in results.values() if r.get("on_leave")]
|
| 277 |
+
good = [r for uid, r in active.items() if r["submitted"] and evals.get(uid, {}).get("quality") == "good"]
|
| 278 |
+
weak = [r for uid, r in active.items() if r["submitted"] and evals.get(uid, {}).get("quality") == "weak"]
|
| 279 |
+
invalid = [r for uid, r in active.items() if r["submitted"] and evals.get(uid, {}).get("quality") == "invalid"]
|
| 280 |
|
| 281 |
total = len(results)
|
| 282 |
+
active_count = len(active)
|
| 283 |
date_str = datetime.datetime.now(IST).strftime("%d %b %Y")
|
| 284 |
+
|
| 285 |
lines = []
|
| 286 |
+
lines.append(f"<!channel>")
|
| 287 |
+
lines.append(f"*Daily Report Check β {date_str}*")
|
| 288 |
+
lines.append(f"{'β' * 32}")
|
| 289 |
+
lines.append(f":busts_in_silhouette: *{active_count} active* Β· :spiral_calendar_pad: Deadline 11:00 PM IST")
|
| 290 |
lines.append("")
|
| 291 |
|
| 292 |
+
# Submitted
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 293 |
if submitted:
|
| 294 |
+
lines.append(f":notepad_spiral: *Reports Received β {len(submitted)}/{active_count}*")
|
| 295 |
+
for uid, r in active.items():
|
| 296 |
+
if not r["submitted"]:
|
| 297 |
continue
|
| 298 |
+
ev = evals.get(uid, {})
|
| 299 |
+
q = ev.get("quality", "?")
|
| 300 |
+
sc = ev.get("score", "?")
|
| 301 |
+
icon = {"good": ":large_green_circle:", "weak": ":large_yellow_circle:", "invalid": ":red_circle:"}.get(q, ":white_circle:")
|
| 302 |
flags = ev.get("flags", [])
|
| 303 |
+
line = f" {icon} *{r['name']}* Β· {sc}/10 Β· {ev.get('reason', '')}"
|
| 304 |
if flags:
|
| 305 |
+
line += "\n _flags: " + ", ".join(flags) + "_"
|
| 306 |
lines.append(line)
|
| 307 |
lines.append("")
|
| 308 |
|
| 309 |
+
# Missed
|
| 310 |
if missed:
|
| 311 |
+
lines.append(f":x: *No Report β {len(missed)}/{active_count}*")
|
| 312 |
for r in missed:
|
| 313 |
+
lines.append(f" :red_circle: *{r['name']}*")
|
| 314 |
+
lines.append("")
|
| 315 |
+
|
| 316 |
+
# On Leave
|
| 317 |
+
if on_leave_list:
|
| 318 |
+
lines.append(f":palm_tree: *On Leave β {len(on_leave_list)}/{total}*")
|
| 319 |
+
for r in on_leave_list:
|
| 320 |
+
lines.append(f" :white_circle: *{r['name']}*")
|
| 321 |
lines.append("")
|
| 322 |
|
| 323 |
+
# Footer
|
| 324 |
+
lines.append(f"{'β' * 32}")
|
| 325 |
+
lines.append(f":large_green_circle: {len(good)} :large_yellow_circle: {len(weak)} :red_circle: {len(invalid)} :x: {len(missed)} :palm_tree: {len(on_leave_list)}")
|
| 326 |
|
| 327 |
return "\n".join(lines)
|
| 328 |
|