fixed reminder
This commit is contained in:
@@ -27,7 +27,8 @@ TOOLS_INSTRUCTIONS = """
|
||||
- Покупки: list_shopping_lists, create_shopping_list, add_shopping_items, check_shopping_item, remove_shopping_item, delete_shopping_list.
|
||||
- «Добавь в список покупок» → add_shopping_items (list_name + товары). «Что купить» → list_shopping_lists. Не выдумывай списки.
|
||||
- Напоминания: list_reminders, create_reminder, update_reminder, delete_reminder, complete_reminder.
|
||||
- «Напомни через 15 минут», «завтра утром», «12 мая 2027 в 12:16» → create_reminder с due_at в ISO (часовой пояс из [Текущее время]).
|
||||
- «Напомни через 15 минут», «завтра утром», «12 мая в 9:00» → create_reminder с due_at в ISO (часовой пояс из [Текущее время]).
|
||||
- День рождения, Новый год и другие праздники → recurrence yearly.
|
||||
- Относительное время считай от «Сейчас» в контексте. «Утром» ≈ 09:00, «вечером» ≈ 19:00, если не уточнено иначе.
|
||||
""".strip()
|
||||
|
||||
|
||||
@@ -14,7 +14,14 @@ RECURRENCE_NONE = "none"
|
||||
RECURRENCE_DAILY = "daily"
|
||||
RECURRENCE_WEEKLY = "weekly"
|
||||
RECURRENCE_MONTHLY = "monthly"
|
||||
VALID_RECURRENCE = frozenset({RECURRENCE_NONE, RECURRENCE_DAILY, RECURRENCE_WEEKLY, RECURRENCE_MONTHLY})
|
||||
RECURRENCE_YEARLY = "yearly"
|
||||
VALID_RECURRENCE = frozenset({
|
||||
RECURRENCE_NONE,
|
||||
RECURRENCE_DAILY,
|
||||
RECURRENCE_WEEKLY,
|
||||
RECURRENCE_MONTHLY,
|
||||
RECURRENCE_YEARLY,
|
||||
})
|
||||
|
||||
|
||||
def _utcnow() -> datetime:
|
||||
@@ -50,6 +57,10 @@ def _advance_due(due_at: datetime, recurrence: str) -> datetime:
|
||||
year += 1
|
||||
day = min(due_at.day, calendar.monthrange(year, month)[1])
|
||||
return due_at.replace(year=year, month=month, day=day)
|
||||
if recurrence == RECURRENCE_YEARLY:
|
||||
year = due_at.year + 1
|
||||
day = min(due_at.day, calendar.monthrange(year, due_at.month)[1])
|
||||
return due_at.replace(year=year, day=day)
|
||||
return due_at
|
||||
|
||||
|
||||
|
||||
@@ -649,8 +649,8 @@ TOOL_DEFINITIONS: list[dict[str, Any]] = [
|
||||
"all_day": {"type": "boolean"},
|
||||
"recurrence": {
|
||||
"type": "string",
|
||||
"enum": ["none", "daily", "weekly", "monthly"],
|
||||
"description": "Повтор",
|
||||
"enum": ["none", "daily", "weekly", "monthly", "yearly"],
|
||||
"description": "Повтор (yearly — день рождения, Новый год)",
|
||||
},
|
||||
},
|
||||
"required": ["title", "due_at"],
|
||||
@@ -670,7 +670,10 @@ TOOL_DEFINITIONS: list[dict[str, Any]] = [
|
||||
"due_at": {"type": "string"},
|
||||
"notes": {"type": "string"},
|
||||
"all_day": {"type": "boolean"},
|
||||
"recurrence": {"type": "string", "enum": ["none", "daily", "weekly", "monthly"]},
|
||||
"recurrence": {
|
||||
"type": "string",
|
||||
"enum": ["none", "daily", "weekly", "monthly", "yearly"],
|
||||
},
|
||||
"enabled": {"type": "boolean"},
|
||||
},
|
||||
"required": ["reminder_id"],
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
"""Идемпотентно добавляет ежегодные напоминания: ДР 12 мая и Новый год."""
|
||||
|
||||
from sqlalchemy import select
|
||||
|
||||
from app.db.base import SessionLocal
|
||||
from app.db.models import Reminder
|
||||
from app.reminders.service import RECURRENCE_YEARLY, RemindersService
|
||||
|
||||
DEFAULTS = (
|
||||
{
|
||||
"title": "День рождения",
|
||||
"due_at": "2027-05-12T09:00:00+03:00",
|
||||
"notes": "С днём рождения!",
|
||||
"all_day": True,
|
||||
"recurrence": RECURRENCE_YEARLY,
|
||||
},
|
||||
{
|
||||
"title": "Новый год",
|
||||
"due_at": "2026-12-31T23:55:00+03:00",
|
||||
"notes": "С наступающим Новым годом!",
|
||||
"all_day": False,
|
||||
"recurrence": RECURRENCE_YEARLY,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
db = SessionLocal()
|
||||
try:
|
||||
service = RemindersService(db)
|
||||
for item in DEFAULTS:
|
||||
exists = db.scalar(
|
||||
select(Reminder.id)
|
||||
.where(
|
||||
Reminder.title == item["title"],
|
||||
Reminder.recurrence == RECURRENCE_YEARLY,
|
||||
Reminder.enabled.is_(True),
|
||||
)
|
||||
.limit(1)
|
||||
)
|
||||
if exists:
|
||||
print(f"skip: {item['title']} (id={exists})")
|
||||
continue
|
||||
result = service.create(**item)
|
||||
reminder = result["reminder"]
|
||||
print(f"created: {reminder['title']} · {reminder['due_at_local']} · yearly")
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -9,9 +9,13 @@ server {
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header Connection "";
|
||||
proxy_buffering off;
|
||||
proxy_cache off;
|
||||
chunked_transfer_encoding off;
|
||||
proxy_connect_timeout 60s;
|
||||
proxy_send_timeout 300s;
|
||||
proxy_read_timeout 300s;
|
||||
}
|
||||
|
||||
location / {
|
||||
|
||||
@@ -271,8 +271,18 @@ export const api = {
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
while (true) {
|
||||
const { done, value } = await reader.read();
|
||||
let done = false;
|
||||
let value: Uint8Array | undefined;
|
||||
try {
|
||||
({ done, value } = await reader.read());
|
||||
} catch {
|
||||
throw new Error(
|
||||
"Соединение прервалось (таймаут прокси). Обновите чат — ответ мог уже сохраниться.",
|
||||
);
|
||||
}
|
||||
|
||||
if (value) {
|
||||
buffer += decoder.decode(value, { stream: !done });
|
||||
}
|
||||
@@ -288,6 +298,9 @@ export const api = {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
reader.releaseLock();
|
||||
}
|
||||
},
|
||||
|
||||
pomodoroStatus: () => request<PomodoroStatus>("/api/v1/pomodoro/status"),
|
||||
|
||||
@@ -252,6 +252,7 @@ export default function Reminders() {
|
||||
<option value="daily">Каждый день</option>
|
||||
<option value="weekly">Каждую неделю</option>
|
||||
<option value="monthly">Каждый месяц</option>
|
||||
<option value="yearly">Каждый год</option>
|
||||
</select>
|
||||
</label>
|
||||
<label>
|
||||
|
||||
Reference in New Issue
Block a user