This commit is contained in:
timka123 committed 2026-10-18 18:02:05 +03:00
commit 17a0a3c4c9
121 files changed
+17641

No files matched your search

+155
View File
@@ -0,0 +1,155 @@
"""The timer endpoints. Thin: every decision lives in `services.pomodoro`."""
from __future__ import annotations
from datetime import date
from fastapi import APIRouter, HTTPException, Query
from app.api.deps import DbSession, Settings
from app.api.schemas import (
AbandonRequest,
AttachTaskRequest,
CompleteTimerRequest,
ExtendRequest,
StartTimerRequest,
)
from app.services import pomodoro, report, stats
from app.services.notify import count_sent, notify, record_sent
from app.timeutil import local_date
router = APIRouter(prefix="/api/timer", tags=["timer"])
@router.get("/state")
def get_state(session: DbSession, settings: Settings) -> dict:
return pomodoro.state(session, settings)
@router.post("/start")
def start(body: StartTimerRequest, session: DbSession, settings: Settings) -> dict:
try:
pomodoro.start(
session,
settings,
kind=body.kind,
minutes=body.minutes,
task_id=body.task_id,
note=body.note,
)
except pomodoro.TimerError as exc:
raise HTTPException(status_code=400, detail=str(exc)) from exc
return pomodoro.state(session, settings)
@router.post("/pause")
def pause(session: DbSession, settings: Settings) -> dict:
try:
pomodoro.pause(session, settings)
except pomodoro.TimerError as exc:
raise HTTPException(status_code=400, detail=str(exc)) from exc
return pomodoro.state(session, settings)
@router.post("/resume")
def resume(session: DbSession, settings: Settings) -> dict:
try:
pomodoro.resume(session)
except pomodoro.TimerError as exc:
raise HTTPException(status_code=400, detail=str(exc)) from exc
return pomodoro.state(session, settings)
@router.post("/complete")
def complete(body: CompleteTimerRequest, session: DbSession, settings: Settings) -> dict:
try:
finished, follow_on = pomodoro.complete(session, settings, auto_next=body.auto_next)
except pomodoro.TimerError as exc:
raise HTTPException(status_code=400, detail=str(exc)) from exc
celebration = _maybe_celebrate(session, settings) if finished.is_work else None
state = pomodoro.state(session, settings)
state["finished"] = pomodoro.session_dict(finished)
state["auto_started"] = pomodoro.session_dict(follow_on)
state["celebration"] = celebration
return state
@router.post("/abandon")
def abandon(body: AbandonRequest, session: DbSession, settings: Settings) -> dict:
try:
pomodoro.abandon(session, settings, reason=body.reason)
except pomodoro.TimerError as exc:
raise HTTPException(status_code=400, detail=str(exc)) from exc
return pomodoro.state(session, settings)
@router.post("/interrupt")
def interrupt(session: DbSession, settings: Settings) -> dict:
try:
pomodoro.add_interruption(session)
except pomodoro.TimerError as exc:
raise HTTPException(status_code=400, detail=str(exc)) from exc
return pomodoro.state(session, settings)
@router.post("/extend")
def extend(body: ExtendRequest, session: DbSession, settings: Settings) -> dict:
try:
pomodoro.extend(session, body.minutes)
except pomodoro.TimerError as exc:
raise HTTPException(status_code=400, detail=str(exc)) from exc
return pomodoro.state(session, settings)
@router.post("/task")
def attach_task(body: AttachTaskRequest, session: DbSession, settings: Settings) -> dict:
try:
pomodoro.attach_task(session, body.task_id)
except pomodoro.TimerError as exc:
raise HTTPException(status_code=400, detail=str(exc)) from exc
return pomodoro.state(session, settings)
@router.get("/sessions")
def sessions(
session: DbSession,
settings: Settings,
day: date | None = Query(default=None, description="Local date; defaults to today."),
) -> dict:
target = day or local_date(settings.timezone)
rows = pomodoro.sessions_for_day(session, settings, target)
return {
"date": target.isoformat(),
"sessions": [pomodoro.session_dict(row) for row in rows],
"metrics": stats.day_metrics(session, settings, target),
}
def _maybe_celebrate(session, settings) -> str | None:
"""Cheer in Telegram every N pomodoros — the cheapest dopamine there is."""
every = settings.notifications.telegram.celebrate_every_n_pomodoros
if not (settings.notifications.telegram.enabled and every):
return None
done = pomodoro.completed_work_today(session, settings)
if done == 0 or done % every != 0:
return None
today = local_date(settings.timezone)
kind = f"celebrate:{done}"
if count_sent(session, kind, today):
return None
ru = settings.notifications.report.language == "ru"
goal = settings.pomodoro.daily_goal_pomodoros
bar = report.progress_bar(done, goal)
text = (
f"🎉 <b>{done}</b> помидорок готово!\n{bar} {done}/{goal}\nДержи темп."
if ru
else f"🎉 <b>{done}</b> pomodoros done!\n{bar} {done}/{goal}\nKeep the rhythm."
)
deliveries = notify(settings, text)
if any(delivery.ok for delivery in deliveries):
record_sent(session, kind, today, {"pomodoros": done})
return text