Напоминалки: часовой пояс админов и пауза в database.py
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
+47
-5
@@ -203,7 +203,22 @@ class DatabaseManager:
|
|||||||
recipients TEXT NOT NULL, -- JSON: список vk_id, либо строка "all"
|
recipients TEXT NOT NULL, -- JSON: список vk_id, либо строка "all"
|
||||||
created_by INTEGER NOT NULL,
|
created_by INTEGER NOT NULL,
|
||||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
active INTEGER NOT NULL DEFAULT 1
|
active INTEGER NOT NULL DEFAULT 1,
|
||||||
|
paused INTEGER NOT NULL DEFAULT 0
|
||||||
|
)
|
||||||
|
""")
|
||||||
|
# Миграция: колонка paused для уже существующих БД (созданных до этой фичи)
|
||||||
|
try:
|
||||||
|
cursor.execute("ALTER TABLE reminders ADD COLUMN paused INTEGER NOT NULL DEFAULT 0")
|
||||||
|
except sqlite3.OperationalError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Table for per-admin settings (пока только часовой пояс для напоминалок)
|
||||||
|
cursor.execute("""
|
||||||
|
CREATE TABLE IF NOT EXISTS admin_settings (
|
||||||
|
user_id INTEGER PRIMARY KEY,
|
||||||
|
tz_offset INTEGER NOT NULL, -- смещение от UTC в часах, напр. 3 для Москвы
|
||||||
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||||
)
|
)
|
||||||
""")
|
""")
|
||||||
|
|
||||||
@@ -647,19 +662,19 @@ class DatabaseManager:
|
|||||||
conn.row_factory = sqlite3.Row
|
conn.row_factory = sqlite3.Row
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
cursor.execute(
|
cursor.execute(
|
||||||
"SELECT id, text, remind_at, next_at, recurrence, recipients, created_by "
|
"SELECT id, text, remind_at, next_at, recurrence, recipients, created_by, paused "
|
||||||
"FROM reminders WHERE active = 1 ORDER BY next_at"
|
"FROM reminders WHERE active = 1 ORDER BY next_at"
|
||||||
)
|
)
|
||||||
return self._rows_with_parsed_recipients(cursor)
|
return self._rows_with_parsed_recipients(cursor)
|
||||||
|
|
||||||
def get_due_reminders(self, now_str: str) -> list:
|
def get_due_reminders(self, now_str: str) -> list:
|
||||||
"""Активные напоминания, чьё время (next_at) уже наступило."""
|
"""Активные напоминания (не на паузе), чьё время (next_at) уже наступило."""
|
||||||
with self._get_connection() as conn:
|
with self._get_connection() as conn:
|
||||||
conn.row_factory = sqlite3.Row
|
conn.row_factory = sqlite3.Row
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
cursor.execute(
|
cursor.execute(
|
||||||
"SELECT id, text, remind_at, next_at, recurrence, recipients, created_by "
|
"SELECT id, text, remind_at, next_at, recurrence, recipients, created_by, paused "
|
||||||
"FROM reminders WHERE active = 1 AND next_at <= ?", (now_str,)
|
"FROM reminders WHERE active = 1 AND paused = 0 AND next_at <= ?", (now_str,)
|
||||||
)
|
)
|
||||||
return self._rows_with_parsed_recipients(cursor)
|
return self._rows_with_parsed_recipients(cursor)
|
||||||
|
|
||||||
@@ -682,6 +697,33 @@ class DatabaseManager:
|
|||||||
conn.commit()
|
conn.commit()
|
||||||
return cursor.rowcount > 0
|
return cursor.rowcount > 0
|
||||||
|
|
||||||
|
def set_reminder_paused(self, reminder_id: int, paused: bool):
|
||||||
|
with self._get_connection() as conn:
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute("UPDATE reminders SET paused = ? WHERE id = ?",
|
||||||
|
(1 if paused else 0, reminder_id))
|
||||||
|
conn.commit()
|
||||||
|
|
||||||
|
# --- Часовой пояс админов (для корректного показа/ввода времени напоминалок) ---
|
||||||
|
def get_admin_tz_offset(self, user_id: int) -> Optional[int]:
|
||||||
|
"""Смещение от UTC в часах, которое задал себе админ, или None, если ещё не задавал."""
|
||||||
|
with self._get_connection() as conn:
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute("SELECT tz_offset FROM admin_settings WHERE user_id = ?", (user_id,))
|
||||||
|
row = cursor.fetchone()
|
||||||
|
return row[0] if row else None
|
||||||
|
|
||||||
|
def set_admin_tz_offset(self, user_id: int, tz_offset: int):
|
||||||
|
with self._get_connection() as conn:
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute(
|
||||||
|
"INSERT INTO admin_settings (user_id, tz_offset) VALUES (?, ?) "
|
||||||
|
"ON CONFLICT(user_id) DO UPDATE SET tz_offset = excluded.tz_offset, "
|
||||||
|
"updated_at = CURRENT_TIMESTAMP",
|
||||||
|
(user_id, tz_offset)
|
||||||
|
)
|
||||||
|
conn.commit()
|
||||||
|
|
||||||
# --- Быстрые ответы (custom FAQ) ---
|
# --- Быстрые ответы (custom FAQ) ---
|
||||||
def add_custom_faq(self, keyword: str, answer: str) -> int:
|
def add_custom_faq(self, keyword: str, answer: str) -> int:
|
||||||
with self._get_connection() as conn:
|
with self._get_connection() as conn:
|
||||||
|
|||||||
@@ -83,3 +83,26 @@ def test_reminders_all_recipients_and_delete(tmp_db):
|
|||||||
assert r["recipients"] == "all"
|
assert r["recipients"] == "all"
|
||||||
assert tmp_db.delete_reminder(rid)
|
assert tmp_db.delete_reminder(rid)
|
||||||
assert not tmp_db.delete_reminder(rid) # уже удалено, повторное удаление — False
|
assert not tmp_db.delete_reminder(rid) # уже удалено, повторное удаление — False
|
||||||
|
|
||||||
|
|
||||||
|
def test_admin_tz_offset(tmp_db):
|
||||||
|
assert tmp_db.get_admin_tz_offset(999) is None
|
||||||
|
tmp_db.set_admin_tz_offset(999, 3)
|
||||||
|
assert tmp_db.get_admin_tz_offset(999) == 3
|
||||||
|
tmp_db.set_admin_tz_offset(999, -5) # перезапись существующего значения
|
||||||
|
assert tmp_db.get_admin_tz_offset(999) == -5
|
||||||
|
|
||||||
|
|
||||||
|
def test_reminder_pause_excludes_from_due_but_stays_visible(tmp_db):
|
||||||
|
rid = tmp_db.add_reminder("Продлить домен", "2020-01-01 09:00:00", "once", "all", created_by=1)
|
||||||
|
assert tmp_db.get_due_reminders("2020-01-01 09:00:00") # просрочено — должно найтись
|
||||||
|
|
||||||
|
tmp_db.set_reminder_paused(rid, True)
|
||||||
|
assert tmp_db.get_due_reminders("2020-01-01 09:00:00") == [] # на паузе — не должно
|
||||||
|
row = next(r for r in tmp_db.get_active_reminders() if r["id"] == rid)
|
||||||
|
assert row["paused"] == 1 # но в общем списке всё ещё видно
|
||||||
|
|
||||||
|
tmp_db.set_reminder_paused(rid, False)
|
||||||
|
assert tmp_db.get_due_reminders("2020-01-01 09:00:00") # сняли паузу — снова попадает в due
|
||||||
|
row = next(r for r in tmp_db.get_active_reminders() if r["id"] == rid)
|
||||||
|
assert row["paused"] == 0
|
||||||
|
|||||||
Reference in New Issue
Block a user