mirror of
https://github.com/Sea-Haven-Industries/afterhours-shift-manager.git
synced 2026-09-30 06:43:12 +00:00
Fix weekend day shift commands and admin 3CX routing
- Add _find_employee_shift() to check both day/night on weekends - Drop/swap now correctly find and operate on weekend day shifts - Pick finds first available shift type on weekends - Admin override/open/clear update 3CX for same-day active shifts
This commit is contained in:
parent
eb8f397b9e
commit
69220afb9a
1 changed files with 72 additions and 13 deletions
|
|
@ -100,6 +100,24 @@ def _is_active_shift_type(shift_type: str) -> bool:
|
||||||
return shift_type == "night"
|
return shift_type == "night"
|
||||||
|
|
||||||
|
|
||||||
|
def _find_employee_shift(schedule, date_str, day_name, employee_ext):
|
||||||
|
"""Find which shift type an employee is assigned to on a given date.
|
||||||
|
|
||||||
|
On weekends, checks both day and night shifts. Returns (ext, name, source, shift_type)
|
||||||
|
or None if not found on any shift.
|
||||||
|
"""
|
||||||
|
if day_name in WEEKEND_DAYS:
|
||||||
|
for st in ("day", "night"):
|
||||||
|
ext, name, source = schedule.resolve_shift(date_str, day_name, st)
|
||||||
|
if ext == employee_ext:
|
||||||
|
return ext, name, source, st
|
||||||
|
return None
|
||||||
|
ext, name, source = schedule.resolve_shift(date_str, day_name, "night")
|
||||||
|
if ext == employee_ext:
|
||||||
|
return ext, name, source, "night"
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
def create_app(
|
def create_app(
|
||||||
bot_token: str, signing_secret: str, schedule_channel: str | None = None
|
bot_token: str, signing_secret: str, schedule_channel: str | None = None
|
||||||
) -> App:
|
) -> App:
|
||||||
|
|
@ -380,7 +398,19 @@ def create_app(
|
||||||
return
|
return
|
||||||
|
|
||||||
day_name = date.strftime("%A")
|
day_name = date.strftime("%A")
|
||||||
ext, name, source = schedule.resolve_shift(date_str, day_name)
|
|
||||||
|
# On weekends, find the first available shift (day then night)
|
||||||
|
shift_type = "night"
|
||||||
|
if day_name in WEEKEND_DAYS:
|
||||||
|
for st in ("day", "night"):
|
||||||
|
ext, name, source = schedule.resolve_shift(date_str, day_name, st)
|
||||||
|
if source == "available":
|
||||||
|
shift_type = st
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
ext, name, source = schedule.resolve_shift(date_str, day_name)
|
||||||
|
else:
|
||||||
|
ext, name, source = schedule.resolve_shift(date_str, day_name)
|
||||||
|
|
||||||
# Already assigned to someone else (not open)
|
# Already assigned to someone else (not open)
|
||||||
if source in ("weekly", "override") and ext != FALLBACK_EXTENSION:
|
if source in ("weekly", "override") and ext != FALLBACK_EXTENSION:
|
||||||
|
|
@ -389,15 +419,22 @@ def create_app(
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
schedule.set_override(date_str, employee["extension"], employee["name"])
|
schedule.set_override(
|
||||||
|
date_str, employee["extension"], employee["name"], shift_type
|
||||||
|
)
|
||||||
|
|
||||||
if is_today(date_str) and _is_active_shift_type("night"):
|
if is_today(date_str) and _is_active_shift_type(shift_type):
|
||||||
_update_3cx_routing(employee["extension"])
|
_update_3cx_routing(employee["extension"])
|
||||||
|
|
||||||
respond(text=f"You picked up the shift for *{date.strftime('%A, %b %-d')}*.")
|
respond(text=f"You picked up the shift for *{date.strftime('%A, %b %-d')}*.")
|
||||||
|
|
||||||
blocks = build_shift_change_message(
|
blocks = build_shift_change_message(
|
||||||
user_id, date_str, "picked_up", employee["extension"], employee["name"]
|
user_id,
|
||||||
|
date_str,
|
||||||
|
"picked_up",
|
||||||
|
employee["extension"],
|
||||||
|
employee["name"],
|
||||||
|
shift_type=shift_type,
|
||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
client.chat_postMessage(
|
client.chat_postMessage(
|
||||||
|
|
@ -435,22 +472,28 @@ def create_app(
|
||||||
return
|
return
|
||||||
|
|
||||||
day_name = date.strftime("%A")
|
day_name = date.strftime("%A")
|
||||||
ext, name, source = schedule.resolve_shift(date_str, day_name)
|
found = _find_employee_shift(
|
||||||
|
schedule, date_str, day_name, employee["extension"]
|
||||||
|
)
|
||||||
|
|
||||||
if ext != employee["extension"]:
|
if not found:
|
||||||
|
ext, name, _source = schedule.resolve_shift(date_str, day_name)
|
||||||
respond(text=f"That's not your shift — it belongs to {name} (Ext {ext}).")
|
respond(text=f"That's not your shift — it belongs to {name} (Ext {ext}).")
|
||||||
return
|
return
|
||||||
|
|
||||||
schedule.mark_open(date_str)
|
ext, name, source, shift_type = found
|
||||||
|
schedule.mark_open(date_str, shift_type)
|
||||||
|
|
||||||
if is_today(date_str) and _is_active_shift_type("night"):
|
if is_today(date_str) and _is_active_shift_type(shift_type):
|
||||||
_update_3cx_routing(FALLBACK_EXTENSION)
|
_update_3cx_routing(FALLBACK_EXTENSION)
|
||||||
|
|
||||||
respond(
|
respond(
|
||||||
text=f"You dropped the shift for *{date.strftime('%A, %b %-d')}*. It's now open for pickup."
|
text=f"You dropped the shift for *{date.strftime('%A, %b %-d')}*. It's now open for pickup."
|
||||||
)
|
)
|
||||||
|
|
||||||
blocks = build_shift_change_message(user_id, date_str, "dropped", ext, name)
|
blocks = build_shift_change_message(
|
||||||
|
user_id, date_str, "dropped", ext, name, shift_type=shift_type
|
||||||
|
)
|
||||||
try:
|
try:
|
||||||
client.chat_postMessage(
|
client.chat_postMessage(
|
||||||
channel=channel_id, blocks=blocks, text=f"Shift dropped for {date_str}"
|
channel=channel_id, blocks=blocks, text=f"Shift dropped for {date_str}"
|
||||||
|
|
@ -486,14 +529,19 @@ def create_app(
|
||||||
return
|
return
|
||||||
|
|
||||||
day_name = date.strftime("%A")
|
day_name = date.strftime("%A")
|
||||||
ext, name, source = schedule.resolve_shift(date_str, day_name)
|
found = _find_employee_shift(
|
||||||
|
schedule, date_str, day_name, employee["extension"]
|
||||||
|
)
|
||||||
|
|
||||||
if ext != employee["extension"]:
|
if not found:
|
||||||
|
ext, name, _source = schedule.resolve_shift(date_str, day_name)
|
||||||
respond(
|
respond(
|
||||||
text=f"That's not your shift — it belongs to {name} (Ext {ext}). You can only swap your own shifts."
|
text=f"That's not your shift — it belongs to {name} (Ext {ext}). You can only swap your own shifts."
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
_ext, _name, _source, shift_type = found
|
||||||
|
|
||||||
# Resolve target user — could be <@U12345> or an extension number
|
# Resolve target user — could be <@U12345> or an extension number
|
||||||
target_text = parts[2].strip()
|
target_text = parts[2].strip()
|
||||||
slack_id_match = re.match(r"<@(\w+)(?:\|[^>]*)?>", target_text)
|
slack_id_match = re.match(r"<@(\w+)(?:\|[^>]*)?>", target_text)
|
||||||
|
|
@ -511,9 +559,9 @@ def create_app(
|
||||||
respond(text=f"Extension `{target_text}` not found in the roster.")
|
respond(text=f"Extension `{target_text}` not found in the roster.")
|
||||||
return
|
return
|
||||||
|
|
||||||
schedule.set_override(date_str, target["extension"], target["name"])
|
schedule.set_override(date_str, target["extension"], target["name"], shift_type)
|
||||||
|
|
||||||
if is_today(date_str) and _is_active_shift_type("night"):
|
if is_today(date_str) and _is_active_shift_type(shift_type):
|
||||||
_update_3cx_routing(target["extension"])
|
_update_3cx_routing(target["extension"])
|
||||||
|
|
||||||
blocks = build_shift_change_message(
|
blocks = build_shift_change_message(
|
||||||
|
|
@ -522,6 +570,7 @@ def create_app(
|
||||||
"swapped",
|
"swapped",
|
||||||
target["extension"],
|
target["extension"],
|
||||||
target["name"],
|
target["name"],
|
||||||
|
shift_type=shift_type,
|
||||||
)
|
)
|
||||||
respond(
|
respond(
|
||||||
text=f"Swapped *{date.strftime('%A, %b %-d')}* to {target['name']} (Ext {target['extension']})."
|
text=f"Swapped *{date.strftime('%A, %b %-d')}* to {target['name']} (Ext {target['extension']})."
|
||||||
|
|
@ -572,6 +621,8 @@ def create_app(
|
||||||
return
|
return
|
||||||
date_str = date.strftime("%Y-%m-%d")
|
date_str = date.strftime("%Y-%m-%d")
|
||||||
schedule.set_override(date_str, employee["extension"], employee["name"])
|
schedule.set_override(date_str, employee["extension"], employee["name"])
|
||||||
|
if is_today(date_str) and _is_active_shift_type("night"):
|
||||||
|
_update_3cx_routing(employee["extension"])
|
||||||
respond(
|
respond(
|
||||||
text=f"Override set: *{date.strftime('%A, %b %-d')}* → {employee['name']} (Ext {ext})"
|
text=f"Override set: *{date.strftime('%A, %b %-d')}* → {employee['name']} (Ext {ext})"
|
||||||
)
|
)
|
||||||
|
|
@ -587,6 +638,8 @@ def create_app(
|
||||||
return
|
return
|
||||||
date_str = date.strftime("%Y-%m-%d")
|
date_str = date.strftime("%Y-%m-%d")
|
||||||
schedule.mark_open(date_str)
|
schedule.mark_open(date_str)
|
||||||
|
if is_today(date_str) and _is_active_shift_type("night"):
|
||||||
|
_update_3cx_routing(FALLBACK_EXTENSION)
|
||||||
respond(text=f"*{date.strftime('%A, %b %-d')}* marked as open.")
|
respond(text=f"*{date.strftime('%A, %b %-d')}* marked as open.")
|
||||||
_refresh_schedule_post(client)
|
_refresh_schedule_post(client)
|
||||||
|
|
||||||
|
|
@ -600,6 +653,12 @@ def create_app(
|
||||||
return
|
return
|
||||||
date_str = date.strftime("%Y-%m-%d")
|
date_str = date.strftime("%Y-%m-%d")
|
||||||
schedule.remove_override(date_str)
|
schedule.remove_override(date_str)
|
||||||
|
if is_today(date_str) and _is_active_shift_type("night"):
|
||||||
|
day_name = date.strftime("%A")
|
||||||
|
ext, _name, _source = schedule.resolve_shift(
|
||||||
|
date_str, day_name, "night"
|
||||||
|
)
|
||||||
|
_update_3cx_routing(ext)
|
||||||
respond(
|
respond(
|
||||||
text=f"Override cleared for *{date.strftime('%A, %b %-d')}* — reverted to weekly schedule."
|
text=f"Override cleared for *{date.strftime('%A, %b %-d')}* — reverted to weekly schedule."
|
||||||
)
|
)
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue