feat: enhance duty information handling with contact details and current duty view

- Added `bot_username` to settings for dynamic retrieval of the bot's username.
- Implemented `_resolve_bot_username` function to fetch the bot's username if not set, improving user experience in group chats.
- Updated `DutyWithUser` schema to include optional `phone` and `username` fields for enhanced duty information.
- Enhanced API responses to include contact details for users, ensuring better communication.
- Introduced a new current duty view in the web app, displaying active duty information along with contact options.
- Updated CSS styles for better presentation of contact information in duty cards.
- Added unit tests to verify the inclusion of contact details in API responses and the functionality of the current duty view.
This commit is contained in:
2026-03-02 16:09:08 +03:00
parent f8aceabab5
commit e3240d0981
25 changed files with 1126 additions and 44 deletions

View File

@@ -5,6 +5,8 @@ import re
from datetime import date, timedelta
import duty_teller.config as config
from starlette.middleware.base import BaseHTTPMiddleware
from fastapi import Depends, FastAPI, Request
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import Response
@@ -56,6 +58,22 @@ app.add_middleware(
)
class NoCacheStaticMiddleware(BaseHTTPMiddleware):
"""Set Cache-Control for /app/*.js and /app/*.html so WebView gets fresh JS (i18n, etc.)."""
async def dispatch(self, request, call_next):
response = await call_next(request)
path = request.url.path
if path.startswith("/app/") and (
path.endswith(".js") or path.endswith(".html")
):
response.headers["Cache-Control"] = "no-store"
return response
app.add_middleware(NoCacheStaticMiddleware)
@app.get(
"/api/duties",
response_model=list[DutyWithUser],
@@ -126,7 +144,9 @@ def get_team_calendar_ical(
to_date = (today + timedelta(days=365 * 2)).strftime("%Y-%m-%d")
all_duties = get_duties(session, from_date=from_date, to_date=to_date)
duties_duty_only = [
(d, name) for d, name in all_duties if (d.event_type or "duty") == "duty"
(d, name)
for d, name, *_ in all_duties
if (d.event_type or "duty") == "duty"
]
ics_bytes = build_team_ics(duties_duty_only)
ics_calendar_cache.set(cache_key, ics_bytes)

View File

@@ -190,7 +190,7 @@ def fetch_duties_response(
to_date: End date YYYY-MM-DD.
Returns:
List of DutyWithUser (id, user_id, start_at, end_at, full_name, event_type).
List of DutyWithUser (id, user_id, start_at, end_at, full_name, event_type, phone, username).
"""
rows = get_duties(session, from_date=from_date, to_date=to_date)
return [
@@ -203,6 +203,8 @@ def fetch_duties_response(
event_type=(
duty.event_type if duty.event_type in DUTY_EVENT_TYPES else "duty"
),
phone=phone,
username=username,
)
for duty, full_name in rows
for duty, full_name, phone, username in rows
]