Напоминалки: таблица reminders и CRUD в database.py
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
+76
@@ -192,6 +192,21 @@ class DatabaseManager:
|
|||||||
)
|
)
|
||||||
""")
|
""")
|
||||||
|
|
||||||
|
# Table for admin reminders (настраиваемые напоминания: дата, текст, периодичность)
|
||||||
|
cursor.execute("""
|
||||||
|
CREATE TABLE IF NOT EXISTS reminders (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
text TEXT NOT NULL,
|
||||||
|
remind_at TEXT NOT NULL, -- якорная дата первого срабатывания, не меняется
|
||||||
|
next_at TEXT NOT NULL, -- следующее срабатывание, двигается вперёд
|
||||||
|
recurrence TEXT NOT NULL, -- 'once' | 'weekly' | 'monthly' | 'yearly'
|
||||||
|
recipients TEXT NOT NULL, -- JSON: список vk_id, либо строка "all"
|
||||||
|
created_by INTEGER NOT NULL,
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
active INTEGER NOT NULL DEFAULT 1
|
||||||
|
)
|
||||||
|
""")
|
||||||
|
|
||||||
conn.commit()
|
conn.commit()
|
||||||
|
|
||||||
# ---------- Редактируемые ссылки (кнопки записи/оплаты) ----------
|
# ---------- Редактируемые ссылки (кнопки записи/оплаты) ----------
|
||||||
@@ -606,6 +621,67 @@ class DatabaseManager:
|
|||||||
cursor.execute("SELECT user_id FROM admins ORDER BY user_id")
|
cursor.execute("SELECT user_id FROM admins ORDER BY user_id")
|
||||||
return [r[0] for r in cursor.fetchall()]
|
return [r[0] for r in cursor.fetchall()]
|
||||||
|
|
||||||
|
# --- Напоминалки для админов (дата/время, периодичность) ---
|
||||||
|
def add_reminder(self, text: str, remind_at: str, recurrence: str,
|
||||||
|
recipients, created_by: int) -> int:
|
||||||
|
"""recipients — список vk_id (list[int]) либо строка 'all'."""
|
||||||
|
with self._get_connection() as conn:
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute(
|
||||||
|
"INSERT INTO reminders (text, remind_at, next_at, recurrence, recipients, created_by) "
|
||||||
|
"VALUES (?, ?, ?, ?, ?, ?)",
|
||||||
|
(text, remind_at, remind_at, recurrence, json.dumps(recipients), created_by)
|
||||||
|
)
|
||||||
|
conn.commit()
|
||||||
|
return cursor.lastrowid
|
||||||
|
|
||||||
|
def _rows_with_parsed_recipients(self, cursor) -> list:
|
||||||
|
rows = [dict(r) for r in cursor.fetchall()]
|
||||||
|
for r in rows:
|
||||||
|
r["recipients"] = json.loads(r["recipients"])
|
||||||
|
return rows
|
||||||
|
|
||||||
|
def get_active_reminders(self) -> list:
|
||||||
|
"""Активные напоминания для отображения в админке, по next_at."""
|
||||||
|
with self._get_connection() as conn:
|
||||||
|
conn.row_factory = sqlite3.Row
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute(
|
||||||
|
"SELECT id, text, remind_at, next_at, recurrence, recipients, created_by "
|
||||||
|
"FROM reminders WHERE active = 1 ORDER BY next_at"
|
||||||
|
)
|
||||||
|
return self._rows_with_parsed_recipients(cursor)
|
||||||
|
|
||||||
|
def get_due_reminders(self, now_str: str) -> list:
|
||||||
|
"""Активные напоминания, чьё время (next_at) уже наступило."""
|
||||||
|
with self._get_connection() as conn:
|
||||||
|
conn.row_factory = sqlite3.Row
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute(
|
||||||
|
"SELECT id, text, remind_at, next_at, recurrence, recipients, created_by "
|
||||||
|
"FROM reminders WHERE active = 1 AND next_at <= ?", (now_str,)
|
||||||
|
)
|
||||||
|
return self._rows_with_parsed_recipients(cursor)
|
||||||
|
|
||||||
|
def update_reminder_next_at(self, reminder_id: int, next_at: str):
|
||||||
|
with self._get_connection() as conn:
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute("UPDATE reminders SET next_at = ? WHERE id = ?", (next_at, reminder_id))
|
||||||
|
conn.commit()
|
||||||
|
|
||||||
|
def deactivate_reminder(self, reminder_id: int):
|
||||||
|
with self._get_connection() as conn:
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute("UPDATE reminders SET active = 0 WHERE id = ?", (reminder_id,))
|
||||||
|
conn.commit()
|
||||||
|
|
||||||
|
def delete_reminder(self, reminder_id: int) -> bool:
|
||||||
|
with self._get_connection() as conn:
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute("DELETE FROM reminders WHERE id = ?", (reminder_id,))
|
||||||
|
conn.commit()
|
||||||
|
return cursor.rowcount > 0
|
||||||
|
|
||||||
# --- Быстрые ответы (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:
|
||||||
|
|||||||
@@ -56,3 +56,30 @@ def test_marketing_subscribers(tmp_db):
|
|||||||
tmp_db.update_user_field(11, "consent_2", "Не согласен")
|
tmp_db.update_user_field(11, "consent_2", "Не согласен")
|
||||||
subs = tmp_db.get_marketing_subscribers()
|
subs = tmp_db.get_marketing_subscribers()
|
||||||
assert 10 in subs and 11 not in subs
|
assert 10 in subs and 11 not in subs
|
||||||
|
|
||||||
|
|
||||||
|
def test_reminders_crud(tmp_db):
|
||||||
|
rid = tmp_db.add_reminder("Продлить домен", "2027-03-15 09:00:00", "yearly", [1, 2], created_by=1)
|
||||||
|
active = tmp_db.get_active_reminders()
|
||||||
|
assert any(r["id"] == rid for r in active)
|
||||||
|
r = next(r for r in active if r["id"] == rid)
|
||||||
|
assert r["recipients"] == [1, 2] and r["recurrence"] == "yearly"
|
||||||
|
|
||||||
|
due = tmp_db.get_due_reminders("2027-03-15 09:00:00")
|
||||||
|
assert any(r["id"] == rid for r in due)
|
||||||
|
assert tmp_db.get_due_reminders("2027-03-14 09:00:00") == []
|
||||||
|
|
||||||
|
tmp_db.update_reminder_next_at(rid, "2028-03-15 09:00:00")
|
||||||
|
r2 = next(r for r in tmp_db.get_active_reminders() if r["id"] == rid)
|
||||||
|
assert r2["next_at"] == "2028-03-15 09:00:00"
|
||||||
|
|
||||||
|
tmp_db.deactivate_reminder(rid)
|
||||||
|
assert not any(r["id"] == rid for r in tmp_db.get_active_reminders())
|
||||||
|
|
||||||
|
|
||||||
|
def test_reminders_all_recipients_and_delete(tmp_db):
|
||||||
|
rid = tmp_db.add_reminder("Оплатить сервер", "2027-01-01 09:00:00", "once", "all", created_by=1)
|
||||||
|
r = next(r for r in tmp_db.get_active_reminders() if r["id"] == rid)
|
||||||
|
assert r["recipients"] == "all"
|
||||||
|
assert tmp_db.delete_reminder(rid)
|
||||||
|
assert not tmp_db.delete_reminder(rid) # уже удалено, повторное удаление — False
|
||||||
|
|||||||
Reference in New Issue
Block a user